From d6e640f9766e2fb9aa3853b4ff19e4d7d5d7e373 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 8 May 2012 22:20:33 +0200 Subject: can: update documentation wording error frames -> error messages As Heinz-Juergen Oertel pointed out 'CAN error frames' are a already defined term for the CAN protocol violation indication on the wire. To avoid confusion with the error messages created by CAN drivers available via CAN RAW sockets update the documentation and change the naming from 'error frames' to 'error messages' or 'error message frames'. Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/linux/can.h | 8 ++++---- include/linux/can/error.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/can.h b/include/linux/can.h index 9a19bcb3eeaf..17334c09bd93 100644 --- a/include/linux/can.h +++ b/include/linux/can.h @@ -21,7 +21,7 @@ /* special address description flags for the CAN_ID */ #define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */ #define CAN_RTR_FLAG 0x40000000U /* remote transmission request */ -#define CAN_ERR_FLAG 0x20000000U /* error frame */ +#define CAN_ERR_FLAG 0x20000000U /* error message frame */ /* valid bits in CAN ID for frame formats */ #define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */ @@ -32,14 +32,14 @@ * Controller Area Network Identifier structure * * bit 0-28 : CAN identifier (11/29 bit) - * bit 29 : error frame flag (0 = data frame, 1 = error frame) + * bit 29 : error message frame flag (0 = data frame, 1 = error message) * bit 30 : remote transmission request flag (1 = rtr frame) * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit) */ typedef __u32 canid_t; /* - * Controller Area Network Error Frame Mask structure + * Controller Area Network Error Message Frame Mask structure * * bit 0-28 : error class mask (see include/linux/can/error.h) * bit 29-31 : set to zero @@ -97,7 +97,7 @@ struct sockaddr_can { * & mask == can_id & mask * * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can - * filter for error frames (CAN_ERR_FLAG bit set in mask). + * filter for error message frames (CAN_ERR_FLAG bit set in mask). */ struct can_filter { canid_t can_id; diff --git a/include/linux/can/error.h b/include/linux/can/error.h index 63e855ea6b84..7b7148bded71 100644 --- a/include/linux/can/error.h +++ b/include/linux/can/error.h @@ -1,7 +1,7 @@ /* * linux/can/error.h * - * Definitions of the CAN error frame to be filtered and passed to the user. + * Definitions of the CAN error messages to be filtered and passed to the user. * * Author: Oliver Hartkopp * Copyright (c) 2002-2007 Volkswagen Group Electronic Research @@ -12,7 +12,7 @@ #ifndef CAN_ERROR_H #define CAN_ERROR_H -#define CAN_ERR_DLC 8 /* dlc for error frames */ +#define CAN_ERR_DLC 8 /* dlc for error message frames */ /* error class (mask) in can_id */ #define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */ -- cgit v1.2.3-59-g8ed1b From a60977a51333a8108f0574aa26094d66b7fedf34 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 14:59:26 +0900 Subject: sh: clkfwk: Move to common clk_div_table accessors for div4/div6. This plugs in a generic clk_div_table, based on the div4 version. div6 is then adopted to use it for encapsulating its div table, which permits us to start div6/4 unification, as well as preparation for other div types. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 46 +++++++++++++++++++++++++++++++++++----------- include/linux/sh_clk.h | 5 +++-- 2 files changed, 38 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index f0d015dd0fef..9dea32907795 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -71,6 +71,22 @@ static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) return clk_rate_table_round(clk, clk->freq_table, rate); } +/* + * Div/mult table lookup helpers + */ +static inline struct clk_div_table *clk_to_div_table(struct clk *clk) +{ + return clk->priv; +} + +static inline struct clk_div_mult_table *clk_to_div_mult_table(struct clk *clk) +{ + return clk_to_div_table(clk)->div_mult_table; +} + +/* + * div6 support + */ static int sh_clk_div6_divisors[64] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, @@ -78,14 +94,18 @@ static int sh_clk_div6_divisors[64] = { 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 }; -static struct clk_div_mult_table sh_clk_div6_table = { +static struct clk_div_mult_table div6_div_mult_table = { .divisors = sh_clk_div6_divisors, .nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors), }; +static struct clk_div_table sh_clk_div6_table = { + .div_mult_table = &div6_div_mult_table, +}; + static unsigned long sh_clk_div6_recalc(struct clk *clk) { - struct clk_div_mult_table *table = &sh_clk_div6_table; + struct clk_div_mult_table *table = clk_to_div_mult_table(clk); unsigned int idx; clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, @@ -98,7 +118,7 @@ static unsigned long sh_clk_div6_recalc(struct clk *clk) static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) { - struct clk_div_mult_table *table = &sh_clk_div6_table; + struct clk_div_mult_table *table = clk_to_div_mult_table(clk); u32 value; int ret, i; @@ -223,7 +243,8 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, { struct clk *clkp; void *freq_table; - int nr_divs = sh_clk_div6_table.nr_divisors; + struct clk_div_table *table = &sh_clk_div6_table; + int nr_divs = table->div_mult_table->nr_divisors; int freq_table_size = sizeof(struct cpufreq_frequency_table); int ret = 0; int k; @@ -239,6 +260,7 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, clkp = clks + k; clkp->ops = ops; + clkp->priv = table; clkp->freq_table = freq_table + (k * freq_table_size); clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; ret = clk_register(clkp); @@ -262,10 +284,12 @@ int __init sh_clk_div6_reparent_register(struct clk *clks, int nr) &sh_clk_div6_reparent_clk_ops); } +/* + * div4 support + */ static unsigned long sh_clk_div4_recalc(struct clk *clk) { - struct clk_div4_table *d4t = clk->priv; - struct clk_div_mult_table *table = d4t->div_mult_table; + struct clk_div_mult_table *table = clk_to_div_mult_table(clk); unsigned int idx; clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, @@ -278,8 +302,7 @@ static unsigned long sh_clk_div4_recalc(struct clk *clk) static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) { - struct clk_div4_table *d4t = clk->priv; - struct clk_div_mult_table *table = d4t->div_mult_table; + struct clk_div_mult_table *table = clk_to_div_mult_table(clk); u32 value; int ret; @@ -308,7 +331,7 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate) { - struct clk_div4_table *d4t = clk->priv; + struct clk_div_table *dt = clk_to_div_table(clk); unsigned long value; int idx = clk_rate_table_find(clk, clk->freq_table, rate); if (idx < 0) @@ -319,8 +342,9 @@ static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate) value |= (idx << clk->enable_bit); sh_clk_write(value, clk); - if (d4t->kick) - d4t->kick(clk); + /* XXX: Should use a post-change notifier */ + if (dt->kick) + dt->kick(clk); return 0; } diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h index c513b73cd7cb..706b803df7b7 100644 --- a/include/linux/sh_clk.h +++ b/include/linux/sh_clk.h @@ -18,7 +18,6 @@ struct clk_mapping { struct kref ref; }; - struct sh_clk_ops { #ifdef CONFIG_SH_CLK_CPG_LEGACY void (*init)(struct clk *clk); @@ -149,11 +148,13 @@ static inline int __deprecated sh_clk_mstp32_register(struct clk *clks, int nr) .flags = _flags, \ } -struct clk_div4_table { +struct clk_div_table { struct clk_div_mult_table *div_mult_table; void (*kick)(struct clk *clk); }; +#define clk_div4_table clk_div_table + int sh_clk_div4_register(struct clk *clks, int nr, struct clk_div4_table *table); int sh_clk_div4_enable_register(struct clk *clks, int nr, -- cgit v1.2.3-59-g8ed1b From 1111cc1e8080b5ff46f5b945acb2f99d6176b2d1 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 15:21:43 +0900 Subject: sh: clkfwk: Introduce a div_mask for variable div types. This plugs in a div_mask for the clock and sets it up for the existing div6/4 cases. This will make it possible to support other div types, as well as share more div6/4 infrastructure. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 10 +++++----- include/linux/sh_clk.h | 8 ++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 9dea32907795..9386bd21c003 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -111,7 +111,7 @@ static unsigned long sh_clk_div6_recalc(struct clk *clk) clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, table, NULL); - idx = sh_clk_read(clk) & 0x003f; + idx = sh_clk_read(clk) & clk->div_mask; return clk->freq_table[idx].frequency; } @@ -159,7 +159,7 @@ static int sh_clk_div6_set_rate(struct clk *clk, unsigned long rate) return idx; value = sh_clk_read(clk); - value &= ~0x3f; + value &= ~clk->div_mask; value |= idx; sh_clk_write(value, clk); return 0; @@ -185,7 +185,7 @@ static void sh_clk_div6_disable(struct clk *clk) value = sh_clk_read(clk); value |= 0x100; /* stop clock */ - value |= 0x3f; /* VDIV bits must be non-zero, overwrite divider */ + value |= clk->div_mask; /* VDIV bits must be non-zero, overwrite divider */ sh_clk_write(value, clk); } @@ -295,7 +295,7 @@ static unsigned long sh_clk_div4_recalc(struct clk *clk) clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, table, &clk->arch_flags); - idx = (sh_clk_read(clk) >> clk->enable_bit) & 0x000f; + idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask; return clk->freq_table[idx].frequency; } @@ -338,7 +338,7 @@ static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate) return idx; value = sh_clk_read(clk); - value &= ~(0xf << clk->enable_bit); + value &= ~(clk->div_mask << clk->enable_bit); value |= (idx << clk->enable_bit); sh_clk_write(value, clk); diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h index 706b803df7b7..d540b8153178 100644 --- a/include/linux/sh_clk.h +++ b/include/linux/sh_clk.h @@ -30,6 +30,10 @@ struct sh_clk_ops { long (*round_rate)(struct clk *clk, unsigned long rate); }; +#define SH_CLK_DIV_MSK(div) ((1 << (div)) - 1) +#define SH_CLK_DIV4_MSK SH_CLK_DIV_MSK(4) +#define SH_CLK_DIV6_MSK SH_CLK_DIV_MSK(6) + struct clk { struct list_head node; struct clk *parent; @@ -51,6 +55,7 @@ struct clk { unsigned int enable_bit; void __iomem *mapped_reg; + unsigned int div_mask; unsigned long arch_flags; void *priv; struct clk_mapping *mapping; @@ -145,6 +150,7 @@ static inline int __deprecated sh_clk_mstp32_register(struct clk *clks, int nr) .enable_reg = (void __iomem *)_reg, \ .enable_bit = _shift, \ .arch_flags = _div_bitmap, \ + .div_mask = SH_CLK_DIV4_MSK, \ .flags = _flags, \ } @@ -167,6 +173,7 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, { \ .enable_reg = (void __iomem *)_reg, \ .flags = _flags, \ + .div_mask = SH_CLK_DIV6_MSK, \ .parent_table = _parents, \ .parent_num = _num_parents, \ .src_shift = _src_shift, \ @@ -177,6 +184,7 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, { \ .parent = _parent, \ .enable_reg = (void __iomem *)_reg, \ + .div_mask = SH_CLK_DIV6_MSK, \ .flags = _flags, \ } -- cgit v1.2.3-59-g8ed1b From 75f5f8a56e0fdf6d32b3ae9c44c10bc0acd3857c Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 15:26:01 +0900 Subject: sh: clkfwk: Use shared sh_clk_div_recalc(). This generalizes the div4 recalc routine for use by div6 and others, then makes it the default. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 62 +++++++++++++++++++++----------------------------- include/linux/sh_clk.h | 2 ++ 2 files changed, 28 insertions(+), 36 deletions(-) (limited to 'include/linux') diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 9386bd21c003..84aeeb8fe013 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -66,11 +66,6 @@ int __init sh_clk_mstp_register(struct clk *clks, int nr) return ret; } -static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) -{ - return clk_rate_table_round(clk, clk->freq_table, rate); -} - /* * Div/mult table lookup helpers */ @@ -84,6 +79,27 @@ static inline struct clk_div_mult_table *clk_to_div_mult_table(struct clk *clk) return clk_to_div_table(clk)->div_mult_table; } +/* + * Common div ops + */ +static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) +{ + return clk_rate_table_round(clk, clk->freq_table, rate); +} + +static unsigned long sh_clk_div_recalc(struct clk *clk) +{ + struct clk_div_mult_table *table = clk_to_div_mult_table(clk); + unsigned int idx; + + clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, + table, clk->arch_flags ? &clk->arch_flags : NULL); + + idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask; + + return clk->freq_table[idx].frequency; +} + /* * div6 support */ @@ -103,19 +119,6 @@ static struct clk_div_table sh_clk_div6_table = { .div_mult_table = &div6_div_mult_table, }; -static unsigned long sh_clk_div6_recalc(struct clk *clk) -{ - struct clk_div_mult_table *table = clk_to_div_mult_table(clk); - unsigned int idx; - - clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, - table, NULL); - - idx = sh_clk_read(clk) & clk->div_mask; - - return clk->freq_table[idx].frequency; -} - static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) { struct clk_div_mult_table *table = clk_to_div_mult_table(clk); @@ -190,7 +193,7 @@ static void sh_clk_div6_disable(struct clk *clk) } static struct sh_clk_ops sh_clk_div6_clk_ops = { - .recalc = sh_clk_div6_recalc, + .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, .set_rate = sh_clk_div6_set_rate, .enable = sh_clk_div6_enable, @@ -198,7 +201,7 @@ static struct sh_clk_ops sh_clk_div6_clk_ops = { }; static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { - .recalc = sh_clk_div6_recalc, + .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, .set_rate = sh_clk_div6_set_rate, .enable = sh_clk_div6_enable, @@ -287,19 +290,6 @@ int __init sh_clk_div6_reparent_register(struct clk *clks, int nr) /* * div4 support */ -static unsigned long sh_clk_div4_recalc(struct clk *clk) -{ - struct clk_div_mult_table *table = clk_to_div_mult_table(clk); - unsigned int idx; - - clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, - table, &clk->arch_flags); - - idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask; - - return clk->freq_table[idx].frequency; -} - static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) { struct clk_div_mult_table *table = clk_to_div_mult_table(clk); @@ -361,13 +351,13 @@ static void sh_clk_div4_disable(struct clk *clk) } static struct sh_clk_ops sh_clk_div4_clk_ops = { - .recalc = sh_clk_div4_recalc, + .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div4_set_rate, .round_rate = sh_clk_div_round_rate, }; static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { - .recalc = sh_clk_div4_recalc, + .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div4_set_rate, .round_rate = sh_clk_div_round_rate, .enable = sh_clk_div4_enable, @@ -375,7 +365,7 @@ static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { }; static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { - .recalc = sh_clk_div4_recalc, + .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div4_set_rate, .round_rate = sh_clk_div_round_rate, .enable = sh_clk_div4_enable, diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h index d540b8153178..35a04f19fb53 100644 --- a/include/linux/sh_clk.h +++ b/include/linux/sh_clk.h @@ -172,6 +172,7 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, _num_parents, _src_shift, _src_width) \ { \ .enable_reg = (void __iomem *)_reg, \ + .enable_bit = 0, /* unused */ \ .flags = _flags, \ .div_mask = SH_CLK_DIV6_MSK, \ .parent_table = _parents, \ @@ -184,6 +185,7 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, { \ .parent = _parent, \ .enable_reg = (void __iomem *)_reg, \ + .enable_bit = 0, /* unused */ \ .div_mask = SH_CLK_DIV6_MSK, \ .flags = _flags, \ } -- cgit v1.2.3-59-g8ed1b From 764f4e4e33d18cde4dcaf8a0d860b749c6d6d08b Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 16:34:48 +0900 Subject: sh: clkfwk: Use shared sh_clk_div_enable/disable(). This introduces a new flag for clocks that need to have their divisor ratio set back to their initial mask at disable time to prevent interactivity problems with the clock stop bit (presently div6 only). With this in place it's possible to handle the corner case on top of the div4 op without any particular need for leaving things split out. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 77 ++++++++++++++++++++++---------------------------- include/linux/sh_clk.h | 6 ++-- 2 files changed, 38 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 29ee5f7072a4..06537f2b2fb8 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -14,6 +14,8 @@ #include #include +#define CPG_CKSTP_BIT BIT(8) + static unsigned int sh_clk_read(struct clk *clk) { if (clk->flags & CLK_ENABLE_REG_8BIT) @@ -122,6 +124,30 @@ static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate) return 0; } +static int sh_clk_div_enable(struct clk *clk) +{ + sh_clk_write(sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk); + return 0; +} + +static void sh_clk_div_disable(struct clk *clk) +{ + unsigned int val; + + val = sh_clk_read(clk); + val |= CPG_CKSTP_BIT; + + /* + * div6 clocks require the divisor field to be non-zero or the + * above CKSTP toggle silently fails. Ensure that the divisor + * array is reset to its initial state on disable. + */ + if (clk->flags & CLK_MASK_DIV_ON_DISABLE) + val |= clk->div_mask; + + sh_clk_write(val, clk); +} + /* * div6 support */ @@ -174,44 +200,20 @@ static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) return 0; } -static int sh_clk_div6_enable(struct clk *clk) -{ - unsigned long value; - int ret; - - ret = sh_clk_div_set_rate(clk, clk->rate); - if (ret == 0) { - value = sh_clk_read(clk); - value &= ~0x100; /* clear stop bit to enable clock */ - sh_clk_write(value, clk); - } - return ret; -} - -static void sh_clk_div6_disable(struct clk *clk) -{ - unsigned long value; - - value = sh_clk_read(clk); - value |= 0x100; /* stop clock */ - value |= clk->div_mask; /* VDIV bits must be non-zero, overwrite divider */ - sh_clk_write(value, clk); -} - static struct sh_clk_ops sh_clk_div6_clk_ops = { .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, .set_rate = sh_clk_div_set_rate, - .enable = sh_clk_div6_enable, - .disable = sh_clk_div6_disable, + .enable = sh_clk_div_enable, + .disable = sh_clk_div_disable, }; static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, .set_rate = sh_clk_div_set_rate, - .enable = sh_clk_div6_enable, - .disable = sh_clk_div6_disable, + .enable = sh_clk_div_enable, + .disable = sh_clk_div_disable, .set_parent = sh_clk_div6_set_parent, }; @@ -325,17 +327,6 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) return 0; } -static int sh_clk_div4_enable(struct clk *clk) -{ - sh_clk_write(sh_clk_read(clk) & ~(1 << 8), clk); - return 0; -} - -static void sh_clk_div4_disable(struct clk *clk) -{ - sh_clk_write(sh_clk_read(clk) | (1 << 8), clk); -} - static struct sh_clk_ops sh_clk_div4_clk_ops = { .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div_set_rate, @@ -346,16 +337,16 @@ static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div_set_rate, .round_rate = sh_clk_div_round_rate, - .enable = sh_clk_div4_enable, - .disable = sh_clk_div4_disable, + .enable = sh_clk_div_enable, + .disable = sh_clk_div_disable, }; static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div_set_rate, .round_rate = sh_clk_div_round_rate, - .enable = sh_clk_div4_enable, - .disable = sh_clk_div4_disable, + .enable = sh_clk_div_enable, + .disable = sh_clk_div_disable, .set_parent = sh_clk_div4_set_parent, }; diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h index 35a04f19fb53..50910913b268 100644 --- a/include/linux/sh_clk.h +++ b/include/linux/sh_clk.h @@ -69,6 +69,8 @@ struct clk { #define CLK_ENABLE_REG_16BIT BIT(2) #define CLK_ENABLE_REG_8BIT BIT(3) +#define CLK_MASK_DIV_ON_DISABLE BIT(4) + #define CLK_ENABLE_REG_MASK (CLK_ENABLE_REG_32BIT | \ CLK_ENABLE_REG_16BIT | \ CLK_ENABLE_REG_8BIT) @@ -173,7 +175,7 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, { \ .enable_reg = (void __iomem *)_reg, \ .enable_bit = 0, /* unused */ \ - .flags = _flags, \ + .flags = _flags | CLK_MASK_DIV_ON_DISABLE, \ .div_mask = SH_CLK_DIV6_MSK, \ .parent_table = _parents, \ .parent_num = _num_parents, \ @@ -187,7 +189,7 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, .enable_reg = (void __iomem *)_reg, \ .enable_bit = 0, /* unused */ \ .div_mask = SH_CLK_DIV6_MSK, \ - .flags = _flags, \ + .flags = _flags | CLK_MASK_DIV_ON_DISABLE, \ } int sh_clk_div6_register(struct clk *clks, int nr); -- cgit v1.2.3-59-g8ed1b From 32fad281c0680ed0ccade7dda85a2121cf9b1d06 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 4 May 2012 02:32:53 +0000 Subject: KVM: PPC: Book3S HV: Make the guest hash table size configurable This adds a new ioctl to enable userspace to control the size of the guest hashed page table (HPT) and to clear it out when resetting the guest. The KVM_PPC_ALLOCATE_HTAB ioctl is a VM ioctl and takes as its parameter a pointer to a u32 containing the desired order of the HPT (log base 2 of the size in bytes), which is updated on successful return to the actual order of the HPT which was allocated. There must be no vcpus running at the time of this ioctl. To enforce this, we now keep a count of the number of vcpus running in kvm->arch.vcpus_running. If the ioctl is called when a HPT has already been allocated, we don't reallocate the HPT but just clear it out. We first clear the kvm->arch.rma_setup_done flag, which has two effects: (a) since we hold the kvm->lock mutex, it will prevent any vcpus from starting to run until we're done, and (b) it means that the first vcpu to run after we're done will re-establish the VRMA if necessary. If userspace doesn't call this ioctl before running the first vcpu, the kernel will allocate a default-sized HPT at that point. We do it then rather than when creating the VM, as the code did previously, so that userspace has a chance to do the ioctl if it wants. When allocating the HPT, we can allocate either from the kernel page allocator, or from the preallocated pool. If userspace is asking for a different size from the preallocated HPTs, we first try to allocate using the kernel page allocator. Then we try to allocate from the preallocated pool, and then if that fails, we try allocating decreasing sizes from the kernel page allocator, down to the minimum size allowed (256kB). Note that the kernel page allocator limits allocations to 1 << CONFIG_FORCE_MAX_ZONEORDER pages, which by default corresponds to 16MB (on 64-bit powerpc, at least). Signed-off-by: Paul Mackerras [agraf: fix module compilation] Signed-off-by: Alexander Graf --- Documentation/virtual/kvm/api.txt | 36 +++++++++ arch/powerpc/include/asm/kvm_book3s_64.h | 7 +- arch/powerpc/include/asm/kvm_host.h | 4 + arch/powerpc/include/asm/kvm_ppc.h | 3 +- arch/powerpc/kvm/book3s_64_mmu_hv.c | 123 ++++++++++++++++++++++++------- arch/powerpc/kvm/book3s_hv.c | 40 +++++++--- arch/powerpc/kvm/book3s_hv_builtin.c | 5 +- arch/powerpc/kvm/book3s_hv_rm_mmu.c | 15 ++-- arch/powerpc/kvm/powerpc.c | 18 +++++ include/linux/kvm.h | 3 + 10 files changed, 200 insertions(+), 54 deletions(-) (limited to 'include/linux') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 930126698a0f..310fe508d9cd 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1930,6 +1930,42 @@ The "pte_enc" field provides a value that can OR'ed into the hash PTE's RPN field (ie, it needs to be shifted left by 12 to OR it into the hash PTE second double word). + +4.75 KVM_PPC_ALLOCATE_HTAB + +Capability: KVM_CAP_PPC_ALLOC_HTAB +Architectures: powerpc +Type: vm ioctl +Parameters: Pointer to u32 containing hash table order (in/out) +Returns: 0 on success, -1 on error + +This requests the host kernel to allocate an MMU hash table for a +guest using the PAPR paravirtualization interface. This only does +anything if the kernel is configured to use the Book 3S HV style of +virtualization. Otherwise the capability doesn't exist and the ioctl +returns an ENOTTY error. The rest of this description assumes Book 3S +HV. + +There must be no vcpus running when this ioctl is called; if there +are, it will do nothing and return an EBUSY error. + +The parameter is a pointer to a 32-bit unsigned integer variable +containing the order (log base 2) of the desired size of the hash +table, which must be between 18 and 46. On successful return from the +ioctl, it will have been updated with the order of the hash table that +was allocated. + +If no hash table has been allocated when any vcpu is asked to run +(with the KVM_RUN ioctl), the host kernel will allocate a +default-sized hash table (16 MB). + +If this ioctl is called when a hash table has already been allocated, +the kernel will clear out the existing hash table (zero all HPTEs) and +return the hash table order in the parameter. (If the guest is using +the virtualized real-mode area (VRMA) facility, the kernel will +re-create the VMRA HPTEs on the next KVM_RUN of any vcpu.) + + 5. The kvm_run structure ------------------------ diff --git a/arch/powerpc/include/asm/kvm_book3s_64.h b/arch/powerpc/include/asm/kvm_book3s_64.h index b0c08b142770..0dd1d86d3e31 100644 --- a/arch/powerpc/include/asm/kvm_book3s_64.h +++ b/arch/powerpc/include/asm/kvm_book3s_64.h @@ -36,11 +36,8 @@ static inline void svcpu_put(struct kvmppc_book3s_shadow_vcpu *svcpu) #define SPAPR_TCE_SHIFT 12 #ifdef CONFIG_KVM_BOOK3S_64_HV -/* For now use fixed-size 16MB page table */ -#define HPT_ORDER 24 -#define HPT_NPTEG (1ul << (HPT_ORDER - 7)) /* 128B per pteg */ -#define HPT_NPTE (HPT_NPTEG << 3) /* 8 PTEs per PTEG */ -#define HPT_HASH_MASK (HPT_NPTEG - 1) +#define KVM_DEFAULT_HPT_ORDER 24 /* 16MB HPT by default */ +extern int kvm_hpt_order; /* order of preallocated HPTs */ #endif #define VRMA_VSID 0x1ffffffUL /* 1TB VSID reserved for VRMA */ diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index d848cdc49715..dd783beb88b3 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -237,6 +237,10 @@ struct kvm_arch { unsigned long vrma_slb_v; int rma_setup_done; int using_mmu_notifiers; + u32 hpt_order; + atomic_t vcpus_running; + unsigned long hpt_npte; + unsigned long hpt_mask; spinlock_t slot_phys_lock; unsigned long *slot_phys[KVM_MEM_SLOTS_NUM]; int slot_npages[KVM_MEM_SLOTS_NUM]; diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index f68c22fa2fce..0124937a23b9 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -119,7 +119,8 @@ extern void kvmppc_core_destroy_mmu(struct kvm_vcpu *vcpu); extern int kvmppc_kvm_pv(struct kvm_vcpu *vcpu); extern void kvmppc_map_magic(struct kvm_vcpu *vcpu); -extern long kvmppc_alloc_hpt(struct kvm *kvm); +extern long kvmppc_alloc_hpt(struct kvm *kvm, u32 *htab_orderp); +extern long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp); extern void kvmppc_free_hpt(struct kvm *kvm); extern long kvmppc_prepare_vrma(struct kvm *kvm, struct kvm_userspace_memory_region *mem); diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c index 80a577517584..d03eb6f7b058 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c @@ -37,56 +37,121 @@ /* POWER7 has 10-bit LPIDs, PPC970 has 6-bit LPIDs */ #define MAX_LPID_970 63 -long kvmppc_alloc_hpt(struct kvm *kvm) +/* Power architecture requires HPT is at least 256kB */ +#define PPC_MIN_HPT_ORDER 18 + +long kvmppc_alloc_hpt(struct kvm *kvm, u32 *htab_orderp) { unsigned long hpt; - long lpid; struct revmap_entry *rev; struct kvmppc_linear_info *li; + long order = kvm_hpt_order; - /* Allocate guest's hashed page table */ - li = kvm_alloc_hpt(); - if (li) { - /* using preallocated memory */ - hpt = (ulong)li->base_virt; - kvm->arch.hpt_li = li; - } else { - /* using dynamic memory */ + if (htab_orderp) { + order = *htab_orderp; + if (order < PPC_MIN_HPT_ORDER) + order = PPC_MIN_HPT_ORDER; + } + + /* + * If the user wants a different size from default, + * try first to allocate it from the kernel page allocator. + */ + hpt = 0; + if (order != kvm_hpt_order) { hpt = __get_free_pages(GFP_KERNEL|__GFP_ZERO|__GFP_REPEAT| - __GFP_NOWARN, HPT_ORDER - PAGE_SHIFT); + __GFP_NOWARN, order - PAGE_SHIFT); + if (!hpt) + --order; } + /* Next try to allocate from the preallocated pool */ if (!hpt) { - pr_err("kvm_alloc_hpt: Couldn't alloc HPT\n"); - return -ENOMEM; + li = kvm_alloc_hpt(); + if (li) { + hpt = (ulong)li->base_virt; + kvm->arch.hpt_li = li; + order = kvm_hpt_order; + } } + + /* Lastly try successively smaller sizes from the page allocator */ + while (!hpt && order > PPC_MIN_HPT_ORDER) { + hpt = __get_free_pages(GFP_KERNEL|__GFP_ZERO|__GFP_REPEAT| + __GFP_NOWARN, order - PAGE_SHIFT); + if (!hpt) + --order; + } + + if (!hpt) + return -ENOMEM; + kvm->arch.hpt_virt = hpt; + kvm->arch.hpt_order = order; + /* HPTEs are 2**4 bytes long */ + kvm->arch.hpt_npte = 1ul << (order - 4); + /* 128 (2**7) bytes in each HPTEG */ + kvm->arch.hpt_mask = (1ul << (order - 7)) - 1; /* Allocate reverse map array */ - rev = vmalloc(sizeof(struct revmap_entry) * HPT_NPTE); + rev = vmalloc(sizeof(struct revmap_entry) * kvm->arch.hpt_npte); if (!rev) { pr_err("kvmppc_alloc_hpt: Couldn't alloc reverse map array\n"); goto out_freehpt; } kvm->arch.revmap = rev; + kvm->arch.sdr1 = __pa(hpt) | (order - 18); - lpid = kvmppc_alloc_lpid(); - if (lpid < 0) - goto out_freeboth; + pr_info("KVM guest htab at %lx (order %ld), LPID %x\n", + hpt, order, kvm->arch.lpid); - kvm->arch.sdr1 = __pa(hpt) | (HPT_ORDER - 18); - kvm->arch.lpid = lpid; - - pr_info("KVM guest htab at %lx, LPID %lx\n", hpt, lpid); + if (htab_orderp) + *htab_orderp = order; return 0; - out_freeboth: - vfree(rev); out_freehpt: - free_pages(hpt, HPT_ORDER - PAGE_SHIFT); + if (kvm->arch.hpt_li) + kvm_release_hpt(kvm->arch.hpt_li); + else + free_pages(hpt, order - PAGE_SHIFT); return -ENOMEM; } +long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp) +{ + long err = -EBUSY; + long order; + + mutex_lock(&kvm->lock); + if (kvm->arch.rma_setup_done) { + kvm->arch.rma_setup_done = 0; + /* order rma_setup_done vs. vcpus_running */ + smp_mb(); + if (atomic_read(&kvm->arch.vcpus_running)) { + kvm->arch.rma_setup_done = 1; + goto out; + } + } + if (kvm->arch.hpt_virt) { + order = kvm->arch.hpt_order; + /* Set the entire HPT to 0, i.e. invalid HPTEs */ + memset((void *)kvm->arch.hpt_virt, 0, 1ul << order); + /* + * Set the whole last_vcpu array to an invalid vcpu number. + * This ensures that each vcpu will flush its TLB on next entry. + */ + memset(kvm->arch.last_vcpu, 0xff, sizeof(kvm->arch.last_vcpu)); + *htab_orderp = order; + err = 0; + } else { + err = kvmppc_alloc_hpt(kvm, htab_orderp); + order = *htab_orderp; + } + out: + mutex_unlock(&kvm->lock); + return err; +} + void kvmppc_free_hpt(struct kvm *kvm) { kvmppc_free_lpid(kvm->arch.lpid); @@ -94,7 +159,8 @@ void kvmppc_free_hpt(struct kvm *kvm) if (kvm->arch.hpt_li) kvm_release_hpt(kvm->arch.hpt_li); else - free_pages(kvm->arch.hpt_virt, HPT_ORDER - PAGE_SHIFT); + free_pages(kvm->arch.hpt_virt, + kvm->arch.hpt_order - PAGE_SHIFT); } /* Bits in first HPTE dword for pagesize 4k, 64k or 16M */ @@ -119,6 +185,7 @@ void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, unsigned long psize; unsigned long hp0, hp1; long ret; + struct kvm *kvm = vcpu->kvm; psize = 1ul << porder; npages = memslot->npages >> (porder - PAGE_SHIFT); @@ -127,8 +194,8 @@ void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, if (npages > 1ul << (40 - porder)) npages = 1ul << (40 - porder); /* Can't use more than 1 HPTE per HPTEG */ - if (npages > HPT_NPTEG) - npages = HPT_NPTEG; + if (npages > kvm->arch.hpt_mask + 1) + npages = kvm->arch.hpt_mask + 1; hp0 = HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16)) | HPTE_V_BOLTED | hpte0_pgsize_encoding(psize); @@ -138,7 +205,7 @@ void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, for (i = 0; i < npages; ++i) { addr = i << porder; /* can't use hpt_hash since va > 64 bits */ - hash = (i ^ (VRMA_VSID ^ (VRMA_VSID << 25))) & HPT_HASH_MASK; + hash = (i ^ (VRMA_VSID ^ (VRMA_VSID << 25))) & kvm->arch.hpt_mask; /* * We assume that the hash table is empty and no * vcpus are using it at this stage. Since we create diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index c6af1d623839..d084e412b3c5 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -56,7 +56,7 @@ /* #define EXIT_DEBUG_INT */ static void kvmppc_end_cede(struct kvm_vcpu *vcpu); -static int kvmppc_hv_setup_rma(struct kvm_vcpu *vcpu); +static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu); void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { @@ -1068,11 +1068,15 @@ int kvmppc_vcpu_run(struct kvm_run *run, struct kvm_vcpu *vcpu) return -EINTR; } - /* On the first time here, set up VRMA or RMA */ + atomic_inc(&vcpu->kvm->arch.vcpus_running); + /* Order vcpus_running vs. rma_setup_done, see kvmppc_alloc_reset_hpt */ + smp_mb(); + + /* On the first time here, set up HTAB and VRMA or RMA */ if (!vcpu->kvm->arch.rma_setup_done) { - r = kvmppc_hv_setup_rma(vcpu); + r = kvmppc_hv_setup_htab_rma(vcpu); if (r) - return r; + goto out; } flush_fp_to_thread(current); @@ -1090,6 +1094,9 @@ int kvmppc_vcpu_run(struct kvm_run *run, struct kvm_vcpu *vcpu) kvmppc_core_prepare_to_enter(vcpu); } } while (r == RESUME_GUEST); + + out: + atomic_dec(&vcpu->kvm->arch.vcpus_running); return r; } @@ -1305,7 +1312,7 @@ void kvmppc_core_commit_memory_region(struct kvm *kvm, { } -static int kvmppc_hv_setup_rma(struct kvm_vcpu *vcpu) +static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu) { int err = 0; struct kvm *kvm = vcpu->kvm; @@ -1324,6 +1331,15 @@ static int kvmppc_hv_setup_rma(struct kvm_vcpu *vcpu) if (kvm->arch.rma_setup_done) goto out; /* another vcpu beat us to it */ + /* Allocate hashed page table (if not done already) and reset it */ + if (!kvm->arch.hpt_virt) { + err = kvmppc_alloc_hpt(kvm, NULL); + if (err) { + pr_err("KVM: Couldn't alloc HPT\n"); + goto out; + } + } + /* Look up the memslot for guest physical address 0 */ memslot = gfn_to_memslot(kvm, 0); @@ -1435,13 +1451,14 @@ static int kvmppc_hv_setup_rma(struct kvm_vcpu *vcpu) int kvmppc_core_init_vm(struct kvm *kvm) { - long r; - unsigned long lpcr; + unsigned long lpcr, lpid; - /* Allocate hashed page table */ - r = kvmppc_alloc_hpt(kvm); - if (r) - return r; + /* Allocate the guest's logical partition ID */ + + lpid = kvmppc_alloc_lpid(); + if (lpid < 0) + return -ENOMEM; + kvm->arch.lpid = lpid; INIT_LIST_HEAD(&kvm->arch.spapr_tce_tables); @@ -1451,7 +1468,6 @@ int kvmppc_core_init_vm(struct kvm *kvm) if (cpu_has_feature(CPU_FTR_ARCH_201)) { /* PPC970; HID4 is effectively the LPCR */ - unsigned long lpid = kvm->arch.lpid; kvm->arch.host_lpid = 0; kvm->arch.host_lpcr = lpcr = mfspr(SPRN_HID4); lpcr &= ~((3 << HID4_LPID1_SH) | (0xful << HID4_LPID5_SH)); diff --git a/arch/powerpc/kvm/book3s_hv_builtin.c b/arch/powerpc/kvm/book3s_hv_builtin.c index e1b60f56f2a1..fb4eac290fef 100644 --- a/arch/powerpc/kvm/book3s_hv_builtin.c +++ b/arch/powerpc/kvm/book3s_hv_builtin.c @@ -25,6 +25,9 @@ static void __init kvm_linear_init_one(ulong size, int count, int type); static struct kvmppc_linear_info *kvm_alloc_linear(int type); static void kvm_release_linear(struct kvmppc_linear_info *ri); +int kvm_hpt_order = KVM_DEFAULT_HPT_ORDER; +EXPORT_SYMBOL_GPL(kvm_hpt_order); + /*************** RMA *************/ /* @@ -209,7 +212,7 @@ static void kvm_release_linear(struct kvmppc_linear_info *ri) void __init kvm_linear_init(void) { /* HPT */ - kvm_linear_init_one(1 << HPT_ORDER, kvm_hpt_count, KVM_LINEAR_HPT); + kvm_linear_init_one(1 << kvm_hpt_order, kvm_hpt_count, KVM_LINEAR_HPT); /* RMA */ /* Only do this on PPC970 in HV mode */ diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c index cec4daddbf31..5c70d19494f9 100644 --- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c +++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c @@ -237,7 +237,7 @@ long kvmppc_h_enter(struct kvm_vcpu *vcpu, unsigned long flags, /* Find and lock the HPTEG slot to use */ do_insert: - if (pte_index >= HPT_NPTE) + if (pte_index >= kvm->arch.hpt_npte) return H_PARAMETER; if (likely((flags & H_EXACT) == 0)) { pte_index &= ~7UL; @@ -352,7 +352,7 @@ long kvmppc_h_remove(struct kvm_vcpu *vcpu, unsigned long flags, unsigned long v, r, rb; struct revmap_entry *rev; - if (pte_index >= HPT_NPTE) + if (pte_index >= kvm->arch.hpt_npte) return H_PARAMETER; hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4)); while (!try_lock_hpte(hpte, HPTE_V_HVLOCK)) @@ -419,7 +419,8 @@ long kvmppc_h_bulk_remove(struct kvm_vcpu *vcpu) i = 4; break; } - if (req != 1 || flags == 3 || pte_index >= HPT_NPTE) { + if (req != 1 || flags == 3 || + pte_index >= kvm->arch.hpt_npte) { /* parameter error */ args[j] = ((0xa0 | flags) << 56) + pte_index; ret = H_PARAMETER; @@ -521,7 +522,7 @@ long kvmppc_h_protect(struct kvm_vcpu *vcpu, unsigned long flags, struct revmap_entry *rev; unsigned long v, r, rb, mask, bits; - if (pte_index >= HPT_NPTE) + if (pte_index >= kvm->arch.hpt_npte) return H_PARAMETER; hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4)); @@ -583,7 +584,7 @@ long kvmppc_h_read(struct kvm_vcpu *vcpu, unsigned long flags, int i, n = 1; struct revmap_entry *rev = NULL; - if (pte_index >= HPT_NPTE) + if (pte_index >= kvm->arch.hpt_npte) return H_PARAMETER; if (flags & H_READ_4) { pte_index &= ~3; @@ -678,7 +679,7 @@ long kvmppc_hv_find_lock_hpte(struct kvm *kvm, gva_t eaddr, unsigned long slb_v, somask = (1UL << 28) - 1; vsid = (slb_v & ~SLB_VSID_B) >> SLB_VSID_SHIFT; } - hash = (vsid ^ ((eaddr & somask) >> pshift)) & HPT_HASH_MASK; + hash = (vsid ^ ((eaddr & somask) >> pshift)) & kvm->arch.hpt_mask; avpn = slb_v & ~(somask >> 16); /* also includes B */ avpn |= (eaddr & somask) >> 16; @@ -723,7 +724,7 @@ long kvmppc_hv_find_lock_hpte(struct kvm *kvm, gva_t eaddr, unsigned long slb_v, if (val & HPTE_V_SECONDARY) break; val |= HPTE_V_SECONDARY; - hash = hash ^ HPT_HASH_MASK; + hash = hash ^ kvm->arch.hpt_mask; } return -1; } diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 1493c8de947b..87f4dc886076 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -246,6 +246,7 @@ int kvm_dev_ioctl_check_extension(long ext) #endif #ifdef CONFIG_PPC_BOOK3S_64 case KVM_CAP_SPAPR_TCE: + case KVM_CAP_PPC_ALLOC_HTAB: r = 1; break; #endif /* CONFIG_PPC_BOOK3S_64 */ @@ -802,6 +803,23 @@ long kvm_arch_vm_ioctl(struct file *filp, r = -EFAULT; break; } + + case KVM_PPC_ALLOCATE_HTAB: { + struct kvm *kvm = filp->private_data; + u32 htab_order; + + r = -EFAULT; + if (get_user(htab_order, (u32 __user *)argp)) + break; + r = kvmppc_alloc_reset_hpt(kvm, &htab_order); + if (r) + break; + r = -EFAULT; + if (put_user(htab_order, (u32 __user *)argp)) + break; + r = 0; + break; + } #endif /* CONFIG_KVM_BOOK3S_64_HV */ #ifdef CONFIG_PPC_BOOK3S_64 diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 09f2b3aa2da7..2ce09aa7d3b3 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -617,6 +617,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_SIGNAL_MSI 77 #define KVM_CAP_PPC_GET_SMMU_INFO 78 #define KVM_CAP_S390_COW 79 +#define KVM_CAP_PPC_ALLOC_HTAB 80 #ifdef KVM_CAP_IRQ_ROUTING @@ -828,6 +829,8 @@ struct kvm_s390_ucas_mapping { #define KVM_SIGNAL_MSI _IOW(KVMIO, 0xa5, struct kvm_msi) /* Available with KVM_CAP_PPC_GET_SMMU_INFO */ #define KVM_PPC_GET_SMMU_INFO _IOR(KVMIO, 0xa6, struct kvm_ppc_smmu_info) +/* Available with KVM_CAP_PPC_ALLOC_HTAB */ +#define KVM_PPC_ALLOCATE_HTAB _IOWR(KVMIO, 0xa7, __u32) /* * ioctls for vcpu fds -- cgit v1.2.3-59-g8ed1b From b86aeafc766b71f6d55e54ed2c77fdf7f56ec1ba Mon Sep 17 00:00:00 2001 From: Jean Pihet Date: Wed, 25 Apr 2012 16:06:20 +0530 Subject: ARM: OMAP2+: SmartReflex: move the smartreflex header to include/linux/power Move the smartreflex header file (arch/arm/mach-omap2/smartreflex.h) in a new header file include/linux/power/smartreflex.h. This change makes the SmartReflex implementation ready for the move to drivers/. Signed-off-by: Jean Pihet Signed-off-by: J Keerthy Reviewed-by: Kevin Hilman Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 4 +- arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 3 +- arch/arm/mach-omap2/smartreflex-class3.c | 3 +- arch/arm/mach-omap2/smartreflex.c | 3 +- arch/arm/mach-omap2/smartreflex.h | 256 ---------------------------- arch/arm/mach-omap2/sr_device.c | 2 +- include/linux/power/smartreflex.h | 257 +++++++++++++++++++++++++++++ 7 files changed, 264 insertions(+), 264 deletions(-) delete mode 100644 arch/arm/mach-omap2/smartreflex.h create mode 100644 include/linux/power/smartreflex.h (limited to 'include/linux') diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index fd48797fa95a..8c7241b7279c 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -14,6 +14,8 @@ * * XXX these should be marked initdata for multi-OMAP kernels */ +#include + #include #include #include @@ -29,8 +31,6 @@ #include #include "omap_hwmod_common_data.h" - -#include "smartreflex.h" #include "prm-regbits-34xx.h" #include "cm-regbits-34xx.h" #include "wd_timer.h" diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index 950454a3fa31..0b3af8233f21 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -19,6 +19,7 @@ */ #include +#include #include #include @@ -32,8 +33,6 @@ #include #include "omap_hwmod_common_data.h" - -#include "smartreflex.h" #include "cm1_44xx.h" #include "cm2_44xx.h" #include "prm44xx.h" diff --git a/arch/arm/mach-omap2/smartreflex-class3.c b/arch/arm/mach-omap2/smartreflex-class3.c index 955566eefac4..ab8cf83e853e 100644 --- a/arch/arm/mach-omap2/smartreflex-class3.c +++ b/arch/arm/mach-omap2/smartreflex-class3.c @@ -11,7 +11,8 @@ * published by the Free Software Foundation. */ -#include "smartreflex.h" +#include +#include "voltage.h" static int sr_class3_enable(struct voltagedomain *voltdm) { diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c index 008fbd7b9352..98309d32ba98 100644 --- a/arch/arm/mach-omap2/smartreflex.c +++ b/arch/arm/mach-omap2/smartreflex.c @@ -25,11 +25,10 @@ #include #include #include +#include #include "common.h" - #include "pm.h" -#include "smartreflex.h" #define SMARTREFLEX_NAME_LEN 16 #define NVALUE_NAME_LEN 40 diff --git a/arch/arm/mach-omap2/smartreflex.h b/arch/arm/mach-omap2/smartreflex.h deleted file mode 100644 index 5809141171f8..000000000000 --- a/arch/arm/mach-omap2/smartreflex.h +++ /dev/null @@ -1,256 +0,0 @@ -/* - * OMAP Smartreflex Defines and Routines - * - * Author: Thara Gopinath - * - * Copyright (C) 2010 Texas Instruments, Inc. - * Thara Gopinath - * - * Copyright (C) 2008 Nokia Corporation - * Kalle Jokiniemi - * - * Copyright (C) 2007 Texas Instruments, Inc. - * Lesly A M - * - * 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_ARM_OMAP_SMARTREFLEX_H -#define __ASM_ARM_OMAP_SMARTREFLEX_H - -#include - -#include "voltage.h" - -/* - * Different Smartreflex IPs version. The v1 is the 65nm version used in - * OMAP3430. The v2 is the update for the 45nm version of the IP - * used in OMAP3630 and OMAP4430 - */ -#define SR_TYPE_V1 1 -#define SR_TYPE_V2 2 - -/* SMART REFLEX REG ADDRESS OFFSET */ -#define SRCONFIG 0x00 -#define SRSTATUS 0x04 -#define SENVAL 0x08 -#define SENMIN 0x0C -#define SENMAX 0x10 -#define SENAVG 0x14 -#define AVGWEIGHT 0x18 -#define NVALUERECIPROCAL 0x1c -#define SENERROR_V1 0x20 -#define ERRCONFIG_V1 0x24 -#define IRQ_EOI 0x20 -#define IRQSTATUS_RAW 0x24 -#define IRQSTATUS 0x28 -#define IRQENABLE_SET 0x2C -#define IRQENABLE_CLR 0x30 -#define SENERROR_V2 0x34 -#define ERRCONFIG_V2 0x38 - -/* Bit/Shift Positions */ - -/* SRCONFIG */ -#define SRCONFIG_ACCUMDATA_SHIFT 22 -#define SRCONFIG_SRCLKLENGTH_SHIFT 12 -#define SRCONFIG_SENNENABLE_V1_SHIFT 5 -#define SRCONFIG_SENPENABLE_V1_SHIFT 3 -#define SRCONFIG_SENNENABLE_V2_SHIFT 1 -#define SRCONFIG_SENPENABLE_V2_SHIFT 0 -#define SRCONFIG_CLKCTRL_SHIFT 0 - -#define SRCONFIG_ACCUMDATA_MASK (0x3ff << 22) - -#define SRCONFIG_SRENABLE BIT(11) -#define SRCONFIG_SENENABLE BIT(10) -#define SRCONFIG_ERRGEN_EN BIT(9) -#define SRCONFIG_MINMAXAVG_EN BIT(8) -#define SRCONFIG_DELAYCTRL BIT(2) - -/* AVGWEIGHT */ -#define AVGWEIGHT_SENPAVGWEIGHT_SHIFT 2 -#define AVGWEIGHT_SENNAVGWEIGHT_SHIFT 0 - -/* NVALUERECIPROCAL */ -#define NVALUERECIPROCAL_SENPGAIN_SHIFT 20 -#define NVALUERECIPROCAL_SENNGAIN_SHIFT 16 -#define NVALUERECIPROCAL_RNSENP_SHIFT 8 -#define NVALUERECIPROCAL_RNSENN_SHIFT 0 - -/* ERRCONFIG */ -#define ERRCONFIG_ERRWEIGHT_SHIFT 16 -#define ERRCONFIG_ERRMAXLIMIT_SHIFT 8 -#define ERRCONFIG_ERRMINLIMIT_SHIFT 0 - -#define SR_ERRWEIGHT_MASK (0x07 << 16) -#define SR_ERRMAXLIMIT_MASK (0xff << 8) -#define SR_ERRMINLIMIT_MASK (0xff << 0) - -#define ERRCONFIG_VPBOUNDINTEN_V1 BIT(31) -#define ERRCONFIG_VPBOUNDINTST_V1 BIT(30) -#define ERRCONFIG_MCUACCUMINTEN BIT(29) -#define ERRCONFIG_MCUACCUMINTST BIT(28) -#define ERRCONFIG_MCUVALIDINTEN BIT(27) -#define ERRCONFIG_MCUVALIDINTST BIT(26) -#define ERRCONFIG_MCUBOUNDINTEN BIT(25) -#define ERRCONFIG_MCUBOUNDINTST BIT(24) -#define ERRCONFIG_MCUDISACKINTEN BIT(23) -#define ERRCONFIG_VPBOUNDINTST_V2 BIT(23) -#define ERRCONFIG_MCUDISACKINTST BIT(22) -#define ERRCONFIG_VPBOUNDINTEN_V2 BIT(22) - -#define ERRCONFIG_STATUS_V1_MASK (ERRCONFIG_VPBOUNDINTST_V1 | \ - ERRCONFIG_MCUACCUMINTST | \ - ERRCONFIG_MCUVALIDINTST | \ - ERRCONFIG_MCUBOUNDINTST | \ - ERRCONFIG_MCUDISACKINTST) -/* IRQSTATUS */ -#define IRQSTATUS_MCUACCUMINT BIT(3) -#define IRQSTATUS_MCVALIDINT BIT(2) -#define IRQSTATUS_MCBOUNDSINT BIT(1) -#define IRQSTATUS_MCUDISABLEACKINT BIT(0) - -/* IRQENABLE_SET and IRQENABLE_CLEAR */ -#define IRQENABLE_MCUACCUMINT BIT(3) -#define IRQENABLE_MCUVALIDINT BIT(2) -#define IRQENABLE_MCUBOUNDSINT BIT(1) -#define IRQENABLE_MCUDISABLEACKINT BIT(0) - -/* Common Bit values */ - -#define SRCLKLENGTH_12MHZ_SYSCLK 0x3c -#define SRCLKLENGTH_13MHZ_SYSCLK 0x41 -#define SRCLKLENGTH_19MHZ_SYSCLK 0x60 -#define SRCLKLENGTH_26MHZ_SYSCLK 0x82 -#define SRCLKLENGTH_38MHZ_SYSCLK 0xC0 - -/* - * 3430 specific values. Maybe these should be passed from board file or - * pmic structures. - */ -#define OMAP3430_SR_ACCUMDATA 0x1f4 - -#define OMAP3430_SR1_SENPAVGWEIGHT 0x03 -#define OMAP3430_SR1_SENNAVGWEIGHT 0x03 - -#define OMAP3430_SR2_SENPAVGWEIGHT 0x01 -#define OMAP3430_SR2_SENNAVGWEIGHT 0x01 - -#define OMAP3430_SR_ERRWEIGHT 0x04 -#define OMAP3430_SR_ERRMAXLIMIT 0x02 - -/** - * struct omap_sr_pmic_data - Strucutre to be populated by pmic code to pass - * pmic specific info to smartreflex driver - * - * @sr_pmic_init: API to initialize smartreflex on the PMIC side. - */ -struct omap_sr_pmic_data { - void (*sr_pmic_init) (void); -}; - -/** - * struct omap_smartreflex_dev_attr - Smartreflex Device attribute. - * - * @sensor_voltdm_name: Name of voltdomain of SR instance - */ -struct omap_smartreflex_dev_attr { - const char *sensor_voltdm_name; -}; - -#ifdef CONFIG_OMAP_SMARTREFLEX -/* - * The smart reflex driver supports CLASS1 CLASS2 and CLASS3 SR. - * The smartreflex class driver should pass the class type. - * Should be used to populate the class_type field of the - * omap_smartreflex_class_data structure. - */ -#define SR_CLASS1 0x1 -#define SR_CLASS2 0x2 -#define SR_CLASS3 0x3 - -/** - * struct omap_sr_class_data - Smartreflex class driver info - * - * @enable: API to enable a particular class smaartreflex. - * @disable: API to disable a particular class smartreflex. - * @configure: API to configure a particular class smartreflex. - * @notify: API to notify the class driver about an event in SR. - * Not needed for class3. - * @notify_flags: specify the events to be notified to the class driver - * @class_type: specify which smartreflex class. - * Can be used by the SR driver to take any class - * based decisions. - */ -struct omap_sr_class_data { - int (*enable)(struct voltagedomain *voltdm); - int (*disable)(struct voltagedomain *voltdm, int is_volt_reset); - int (*configure)(struct voltagedomain *voltdm); - int (*notify)(struct voltagedomain *voltdm, u32 status); - u8 notify_flags; - u8 class_type; -}; - -/** - * struct omap_sr_nvalue_table - Smartreflex n-target value info - * - * @efuse_offs: The offset of the efuse where n-target values are stored. - * @nvalue: The n-target value. - */ -struct omap_sr_nvalue_table { - u32 efuse_offs; - u32 nvalue; -}; - -/** - * struct omap_sr_data - Smartreflex platform data. - * - * @ip_type: Smartreflex IP type. - * @senp_mod: SENPENABLE value for the sr - * @senn_mod: SENNENABLE value for sr - * @nvalue_count: Number of distinct nvalues in the nvalue table - * @enable_on_init: whether this sr module needs to enabled at - * boot up or not. - * @nvalue_table: table containing the efuse offsets and nvalues - * corresponding to them. - * @voltdm: Pointer to the voltage domain associated with the SR - */ -struct omap_sr_data { - int ip_type; - u32 senp_mod; - u32 senn_mod; - int nvalue_count; - bool enable_on_init; - struct omap_sr_nvalue_table *nvalue_table; - struct voltagedomain *voltdm; -}; - -/* Smartreflex module enable/disable interface */ -void omap_sr_enable(struct voltagedomain *voltdm); -void omap_sr_disable(struct voltagedomain *voltdm); -void omap_sr_disable_reset_volt(struct voltagedomain *voltdm); - -/* API to register the pmic specific data with the smartreflex driver. */ -void omap_sr_register_pmic(struct omap_sr_pmic_data *pmic_data); - -/* Smartreflex driver hooks to be called from Smartreflex class driver */ -int sr_enable(struct voltagedomain *voltdm, unsigned long volt); -void sr_disable(struct voltagedomain *voltdm); -int sr_configure_errgen(struct voltagedomain *voltdm); -int sr_disable_errgen(struct voltagedomain *voltdm); -int sr_configure_minmax(struct voltagedomain *voltdm); - -/* API to register the smartreflex class driver with the smartreflex driver */ -int sr_register_class(struct omap_sr_class_data *class_data); -#else -static inline void omap_sr_enable(struct voltagedomain *voltdm) {} -static inline void omap_sr_disable(struct voltagedomain *voltdm) {} -static inline void omap_sr_disable_reset_volt( - struct voltagedomain *voltdm) {} -static inline void omap_sr_register_pmic( - struct omap_sr_pmic_data *pmic_data) {} -#endif -#endif diff --git a/arch/arm/mach-omap2/sr_device.c b/arch/arm/mach-omap2/sr_device.c index a503e1e8358c..86e438e75105 100644 --- a/arch/arm/mach-omap2/sr_device.c +++ b/arch/arm/mach-omap2/sr_device.c @@ -17,6 +17,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include #include #include @@ -24,7 +25,6 @@ #include -#include "smartreflex.h" #include "voltage.h" #include "control.h" #include "pm.h" diff --git a/include/linux/power/smartreflex.h b/include/linux/power/smartreflex.h new file mode 100644 index 000000000000..69eb270c6297 --- /dev/null +++ b/include/linux/power/smartreflex.h @@ -0,0 +1,257 @@ +/* + * OMAP Smartreflex Defines and Routines + * + * Author: Thara Gopinath + * + * Copyright (C) 2010 Texas Instruments, Inc. + * Thara Gopinath + * + * Copyright (C) 2008 Nokia Corporation + * Kalle Jokiniemi + * + * Copyright (C) 2007 Texas Instruments, Inc. + * Lesly A M + * + * 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 __POWER_SMARTREFLEX_H +#define __POWER_SMARTREFLEX_H + +#include +#include + +#include + +/* + * Different Smartreflex IPs version. The v1 is the 65nm version used in + * OMAP3430. The v2 is the update for the 45nm version of the IP + * used in OMAP3630 and OMAP4430 + */ +#define SR_TYPE_V1 1 +#define SR_TYPE_V2 2 + +/* SMART REFLEX REG ADDRESS OFFSET */ +#define SRCONFIG 0x00 +#define SRSTATUS 0x04 +#define SENVAL 0x08 +#define SENMIN 0x0C +#define SENMAX 0x10 +#define SENAVG 0x14 +#define AVGWEIGHT 0x18 +#define NVALUERECIPROCAL 0x1c +#define SENERROR_V1 0x20 +#define ERRCONFIG_V1 0x24 +#define IRQ_EOI 0x20 +#define IRQSTATUS_RAW 0x24 +#define IRQSTATUS 0x28 +#define IRQENABLE_SET 0x2C +#define IRQENABLE_CLR 0x30 +#define SENERROR_V2 0x34 +#define ERRCONFIG_V2 0x38 + +/* Bit/Shift Positions */ + +/* SRCONFIG */ +#define SRCONFIG_ACCUMDATA_SHIFT 22 +#define SRCONFIG_SRCLKLENGTH_SHIFT 12 +#define SRCONFIG_SENNENABLE_V1_SHIFT 5 +#define SRCONFIG_SENPENABLE_V1_SHIFT 3 +#define SRCONFIG_SENNENABLE_V2_SHIFT 1 +#define SRCONFIG_SENPENABLE_V2_SHIFT 0 +#define SRCONFIG_CLKCTRL_SHIFT 0 + +#define SRCONFIG_ACCUMDATA_MASK (0x3ff << 22) + +#define SRCONFIG_SRENABLE BIT(11) +#define SRCONFIG_SENENABLE BIT(10) +#define SRCONFIG_ERRGEN_EN BIT(9) +#define SRCONFIG_MINMAXAVG_EN BIT(8) +#define SRCONFIG_DELAYCTRL BIT(2) + +/* AVGWEIGHT */ +#define AVGWEIGHT_SENPAVGWEIGHT_SHIFT 2 +#define AVGWEIGHT_SENNAVGWEIGHT_SHIFT 0 + +/* NVALUERECIPROCAL */ +#define NVALUERECIPROCAL_SENPGAIN_SHIFT 20 +#define NVALUERECIPROCAL_SENNGAIN_SHIFT 16 +#define NVALUERECIPROCAL_RNSENP_SHIFT 8 +#define NVALUERECIPROCAL_RNSENN_SHIFT 0 + +/* ERRCONFIG */ +#define ERRCONFIG_ERRWEIGHT_SHIFT 16 +#define ERRCONFIG_ERRMAXLIMIT_SHIFT 8 +#define ERRCONFIG_ERRMINLIMIT_SHIFT 0 + +#define SR_ERRWEIGHT_MASK (0x07 << 16) +#define SR_ERRMAXLIMIT_MASK (0xff << 8) +#define SR_ERRMINLIMIT_MASK (0xff << 0) + +#define ERRCONFIG_VPBOUNDINTEN_V1 BIT(31) +#define ERRCONFIG_VPBOUNDINTST_V1 BIT(30) +#define ERRCONFIG_MCUACCUMINTEN BIT(29) +#define ERRCONFIG_MCUACCUMINTST BIT(28) +#define ERRCONFIG_MCUVALIDINTEN BIT(27) +#define ERRCONFIG_MCUVALIDINTST BIT(26) +#define ERRCONFIG_MCUBOUNDINTEN BIT(25) +#define ERRCONFIG_MCUBOUNDINTST BIT(24) +#define ERRCONFIG_MCUDISACKINTEN BIT(23) +#define ERRCONFIG_VPBOUNDINTST_V2 BIT(23) +#define ERRCONFIG_MCUDISACKINTST BIT(22) +#define ERRCONFIG_VPBOUNDINTEN_V2 BIT(22) + +#define ERRCONFIG_STATUS_V1_MASK (ERRCONFIG_VPBOUNDINTST_V1 | \ + ERRCONFIG_MCUACCUMINTST | \ + ERRCONFIG_MCUVALIDINTST | \ + ERRCONFIG_MCUBOUNDINTST | \ + ERRCONFIG_MCUDISACKINTST) +/* IRQSTATUS */ +#define IRQSTATUS_MCUACCUMINT BIT(3) +#define IRQSTATUS_MCVALIDINT BIT(2) +#define IRQSTATUS_MCBOUNDSINT BIT(1) +#define IRQSTATUS_MCUDISABLEACKINT BIT(0) + +/* IRQENABLE_SET and IRQENABLE_CLEAR */ +#define IRQENABLE_MCUACCUMINT BIT(3) +#define IRQENABLE_MCUVALIDINT BIT(2) +#define IRQENABLE_MCUBOUNDSINT BIT(1) +#define IRQENABLE_MCUDISABLEACKINT BIT(0) + +/* Common Bit values */ + +#define SRCLKLENGTH_12MHZ_SYSCLK 0x3c +#define SRCLKLENGTH_13MHZ_SYSCLK 0x41 +#define SRCLKLENGTH_19MHZ_SYSCLK 0x60 +#define SRCLKLENGTH_26MHZ_SYSCLK 0x82 +#define SRCLKLENGTH_38MHZ_SYSCLK 0xC0 + +/* + * 3430 specific values. Maybe these should be passed from board file or + * pmic structures. + */ +#define OMAP3430_SR_ACCUMDATA 0x1f4 + +#define OMAP3430_SR1_SENPAVGWEIGHT 0x03 +#define OMAP3430_SR1_SENNAVGWEIGHT 0x03 + +#define OMAP3430_SR2_SENPAVGWEIGHT 0x01 +#define OMAP3430_SR2_SENNAVGWEIGHT 0x01 + +#define OMAP3430_SR_ERRWEIGHT 0x04 +#define OMAP3430_SR_ERRMAXLIMIT 0x02 + +/** + * struct omap_sr_pmic_data - Strucutre to be populated by pmic code to pass + * pmic specific info to smartreflex driver + * + * @sr_pmic_init: API to initialize smartreflex on the PMIC side. + */ +struct omap_sr_pmic_data { + void (*sr_pmic_init) (void); +}; + +/** + * struct omap_smartreflex_dev_attr - Smartreflex Device attribute. + * + * @sensor_voltdm_name: Name of voltdomain of SR instance + */ +struct omap_smartreflex_dev_attr { + const char *sensor_voltdm_name; +}; + +#ifdef CONFIG_OMAP_SMARTREFLEX +/* + * The smart reflex driver supports CLASS1 CLASS2 and CLASS3 SR. + * The smartreflex class driver should pass the class type. + * Should be used to populate the class_type field of the + * omap_smartreflex_class_data structure. + */ +#define SR_CLASS1 0x1 +#define SR_CLASS2 0x2 +#define SR_CLASS3 0x3 + +/** + * struct omap_sr_class_data - Smartreflex class driver info + * + * @enable: API to enable a particular class smaartreflex. + * @disable: API to disable a particular class smartreflex. + * @configure: API to configure a particular class smartreflex. + * @notify: API to notify the class driver about an event in SR. + * Not needed for class3. + * @notify_flags: specify the events to be notified to the class driver + * @class_type: specify which smartreflex class. + * Can be used by the SR driver to take any class + * based decisions. + */ +struct omap_sr_class_data { + int (*enable)(struct voltagedomain *voltdm); + int (*disable)(struct voltagedomain *voltdm, int is_volt_reset); + int (*configure)(struct voltagedomain *voltdm); + int (*notify)(struct voltagedomain *voltdm, u32 status); + u8 notify_flags; + u8 class_type; +}; + +/** + * struct omap_sr_nvalue_table - Smartreflex n-target value info + * + * @efuse_offs: The offset of the efuse where n-target values are stored. + * @nvalue: The n-target value. + */ +struct omap_sr_nvalue_table { + u32 efuse_offs; + u32 nvalue; +}; + +/** + * struct omap_sr_data - Smartreflex platform data. + * + * @ip_type: Smartreflex IP type. + * @senp_mod: SENPENABLE value for the sr + * @senn_mod: SENNENABLE value for sr + * @nvalue_count: Number of distinct nvalues in the nvalue table + * @enable_on_init: whether this sr module needs to enabled at + * boot up or not. + * @nvalue_table: table containing the efuse offsets and nvalues + * corresponding to them. + * @voltdm: Pointer to the voltage domain associated with the SR + */ +struct omap_sr_data { + int ip_type; + u32 senp_mod; + u32 senn_mod; + int nvalue_count; + bool enable_on_init; + struct omap_sr_nvalue_table *nvalue_table; + struct voltagedomain *voltdm; +}; + +/* Smartreflex module enable/disable interface */ +void omap_sr_enable(struct voltagedomain *voltdm); +void omap_sr_disable(struct voltagedomain *voltdm); +void omap_sr_disable_reset_volt(struct voltagedomain *voltdm); + +/* API to register the pmic specific data with the smartreflex driver. */ +void omap_sr_register_pmic(struct omap_sr_pmic_data *pmic_data); + +/* Smartreflex driver hooks to be called from Smartreflex class driver */ +int sr_enable(struct voltagedomain *voltdm, unsigned long volt); +void sr_disable(struct voltagedomain *voltdm); +int sr_configure_errgen(struct voltagedomain *voltdm); +int sr_disable_errgen(struct voltagedomain *voltdm); +int sr_configure_minmax(struct voltagedomain *voltdm); + +/* API to register the smartreflex class driver with the smartreflex driver */ +int sr_register_class(struct omap_sr_class_data *class_data); +#else +static inline void omap_sr_enable(struct voltagedomain *voltdm) {} +static inline void omap_sr_disable(struct voltagedomain *voltdm) {} +static inline void omap_sr_disable_reset_volt( + struct voltagedomain *voltdm) {} +static inline void omap_sr_register_pmic( + struct omap_sr_pmic_data *pmic_data) {} +#endif +#endif -- cgit v1.2.3-59-g8ed1b From 80821c9c90427dd0f9274a82f9d69e43300d10bb Mon Sep 17 00:00:00 2001 From: Jean Pihet Date: Tue, 24 Apr 2012 10:22:12 +0530 Subject: ARM: OMAP3+: SmartReflex: class drivers should use struct omap_sr * Convert SmartReflex "class" functions to take a struct omap_sr *, rather than a struct voltagedomain *. SmartReflex code should be driver code and not tightly coupled to OMAP subarchitecture-specific structures. Based on Paul's original code for the SmartReflex driver conversion. Signed-off-by: Jean Pihet Signed-off-by: J Keerthy Reviewed-by: Kevin Hilman Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/smartreflex-class3.c | 24 ++++++++++----------- arch/arm/mach-omap2/smartreflex.c | 37 ++++++-------------------------- include/linux/power/smartreflex.h | 31 ++++++++++++++++++++++---- 3 files changed, 46 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-omap2/smartreflex-class3.c b/arch/arm/mach-omap2/smartreflex-class3.c index ab8cf83e853e..9381654e869b 100644 --- a/arch/arm/mach-omap2/smartreflex-class3.c +++ b/arch/arm/mach-omap2/smartreflex-class3.c @@ -14,34 +14,34 @@ #include #include "voltage.h" -static int sr_class3_enable(struct voltagedomain *voltdm) +static int sr_class3_enable(struct omap_sr *sr) { - unsigned long volt = voltdm_get_voltage(voltdm); + unsigned long volt = voltdm_get_voltage(sr->voltdm); if (!volt) { pr_warning("%s: Curr voltage unknown. Cannot enable sr_%s\n", - __func__, voltdm->name); + __func__, sr->voltdm->name); return -ENODATA; } - omap_vp_enable(voltdm); - return sr_enable(voltdm, volt); + omap_vp_enable(sr->voltdm); + return sr_enable(sr->voltdm, volt); } -static int sr_class3_disable(struct voltagedomain *voltdm, int is_volt_reset) +static int sr_class3_disable(struct omap_sr *sr, int is_volt_reset) { - sr_disable_errgen(voltdm); - omap_vp_disable(voltdm); - sr_disable(voltdm); + sr_disable_errgen(sr->voltdm); + omap_vp_disable(sr->voltdm); + sr_disable(sr->voltdm); if (is_volt_reset) - voltdm_reset(voltdm); + voltdm_reset(sr->voltdm); return 0; } -static int sr_class3_configure(struct voltagedomain *voltdm) +static int sr_class3_configure(struct omap_sr *sr) { - return sr_configure_errgen(voltdm); + return sr_configure_errgen(sr->voltdm); } /* SR class3 structure */ diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c index 98309d32ba98..82bdd2838a17 100644 --- a/arch/arm/mach-omap2/smartreflex.c +++ b/arch/arm/mach-omap2/smartreflex.c @@ -34,29 +34,6 @@ #define NVALUE_NAME_LEN 40 #define SR_DISABLE_TIMEOUT 200 -struct omap_sr { - struct list_head node; - struct platform_device *pdev; - struct omap_sr_nvalue_table *nvalue_table; - struct voltagedomain *voltdm; - struct dentry *dbg_dir; - unsigned int irq; - int srid; - int ip_type; - int nvalue_count; - bool autocomp_active; - u32 clk_length; - u32 err_weight; - u32 err_minlimit; - u32 err_maxlimit; - u32 accum_data; - u32 senn_avgweight; - u32 senp_avgweight; - u32 senp_mod; - u32 senn_mod; - void __iomem *base; -}; - /* sr_list contains all the instances of smartreflex module */ static LIST_HEAD(sr_list); @@ -147,7 +124,7 @@ static irqreturn_t sr_interrupt(int irq, void *data) } if (sr_class->notify) - sr_class->notify(sr_info->voltdm, status); + sr_class->notify(sr_info, status); return IRQ_HANDLED; } @@ -225,7 +202,7 @@ static void sr_start_vddautocomp(struct omap_sr *sr) return; } - if (!sr_class->enable(sr->voltdm)) + if (!sr_class->enable(sr)) sr->autocomp_active = true; } @@ -239,7 +216,7 @@ static void sr_stop_vddautocomp(struct omap_sr *sr) } if (sr->autocomp_active) { - sr_class->disable(sr->voltdm, 1); + sr_class->disable(sr, 1); sr->autocomp_active = false; } } @@ -654,7 +631,7 @@ int sr_enable(struct voltagedomain *voltdm, unsigned long volt) return 0; /* Configure SR */ - ret = sr_class->configure(voltdm); + ret = sr_class->configure(sr); if (ret) return ret; @@ -772,7 +749,7 @@ void omap_sr_enable(struct voltagedomain *voltdm) return; } - sr_class->enable(voltdm); + sr_class->enable(sr); } /** @@ -805,7 +782,7 @@ void omap_sr_disable(struct voltagedomain *voltdm) return; } - sr_class->disable(voltdm, 0); + sr_class->disable(sr, 0); } /** @@ -838,7 +815,7 @@ void omap_sr_disable_reset_volt(struct voltagedomain *voltdm) return; } - sr_class->disable(voltdm, 1); + sr_class->disable(sr, 1); } /** diff --git a/include/linux/power/smartreflex.h b/include/linux/power/smartreflex.h index 69eb270c6297..4224698cf8bd 100644 --- a/include/linux/power/smartreflex.h +++ b/include/linux/power/smartreflex.h @@ -143,6 +143,29 @@ #define OMAP3430_SR_ERRWEIGHT 0x04 #define OMAP3430_SR_ERRMAXLIMIT 0x02 +struct omap_sr { + struct list_head node; + struct platform_device *pdev; + struct omap_sr_nvalue_table *nvalue_table; + struct voltagedomain *voltdm; + struct dentry *dbg_dir; + unsigned int irq; + int srid; + int ip_type; + int nvalue_count; + bool autocomp_active; + u32 clk_length; + u32 err_weight; + u32 err_minlimit; + u32 err_maxlimit; + u32 accum_data; + u32 senn_avgweight; + u32 senp_avgweight; + u32 senp_mod; + u32 senn_mod; + void __iomem *base; +}; + /** * struct omap_sr_pmic_data - Strucutre to be populated by pmic code to pass * pmic specific info to smartreflex driver @@ -187,10 +210,10 @@ struct omap_smartreflex_dev_attr { * based decisions. */ struct omap_sr_class_data { - int (*enable)(struct voltagedomain *voltdm); - int (*disable)(struct voltagedomain *voltdm, int is_volt_reset); - int (*configure)(struct voltagedomain *voltdm); - int (*notify)(struct voltagedomain *voltdm, u32 status); + int (*enable)(struct omap_sr *sr); + int (*disable)(struct omap_sr *sr, int is_volt_reset); + int (*configure)(struct omap_sr *sr); + int (*notify)(struct omap_sr *sr, u32 status); u8 notify_flags; u8 class_type; }; -- cgit v1.2.3-59-g8ed1b From 8b765d727d711650ab3521411fd48a0d8f62a84c Mon Sep 17 00:00:00 2001 From: Jean Pihet Date: Tue, 24 Apr 2012 10:41:27 +0530 Subject: ARM: OMAP2+: smartreflex: Use the names from hwmod data instead of voltage domains. Associate a name with each SmartReflex instance from the hwmod data, rather than attempting to reuse the name of a voltage domain. The name from hwmod better reflects the smartreflex integration in the system. Also have the name passed to the drivers using pdata, which helps to remove any dependencies on SoC-specific structures. Signed-off-by: Jean Pihet Signed-off-by: J Keerthy Reviewed-by: Kevin Hilman Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/smartreflex-class3.c | 4 +- arch/arm/mach-omap2/smartreflex.c | 65 +++++++++++++------------------- arch/arm/mach-omap2/sr_device.c | 1 + include/linux/power/smartreflex.h | 3 ++ 4 files changed, 32 insertions(+), 41 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-omap2/smartreflex-class3.c b/arch/arm/mach-omap2/smartreflex-class3.c index 9381654e869b..1da8f03c479e 100644 --- a/arch/arm/mach-omap2/smartreflex-class3.c +++ b/arch/arm/mach-omap2/smartreflex-class3.c @@ -19,8 +19,8 @@ static int sr_class3_enable(struct omap_sr *sr) unsigned long volt = voltdm_get_voltage(sr->voltdm); if (!volt) { - pr_warning("%s: Curr voltage unknown. Cannot enable sr_%s\n", - __func__, sr->voltdm->name); + pr_warning("%s: Curr voltage unknown. Cannot enable %s\n", + __func__, sr->name); return -ENODATA; } diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c index 82bdd2838a17..2edd1e2e4622 100644 --- a/arch/arm/mach-omap2/smartreflex.c +++ b/arch/arm/mach-omap2/smartreflex.c @@ -183,7 +183,7 @@ static void sr_set_regfields(struct omap_sr *sr) sr->err_weight = OMAP3430_SR_ERRWEIGHT; sr->err_maxlimit = OMAP3430_SR_ERRMAXLIMIT; sr->accum_data = OMAP3430_SR_ACCUMDATA; - if (!(strcmp(sr->voltdm->name, "mpu"))) { + if (!(strcmp(sr->name, "sr1"))) { sr->senn_avgweight = OMAP3430_SR1_SENNAVGWEIGHT; sr->senp_avgweight = OMAP3430_SR1_SENPAVGWEIGHT; } else { @@ -234,19 +234,13 @@ static void sr_stop_vddautocomp(struct omap_sr *sr) */ static int sr_late_init(struct omap_sr *sr_info) { - char *name; struct omap_sr_data *pdata = sr_info->pdev->dev.platform_data; struct resource *mem; int ret = 0; if (sr_class->notify && sr_class->notify_flags && sr_info->irq) { - name = kasprintf(GFP_KERNEL, "sr_%s", sr_info->voltdm->name); - if (name == NULL) { - ret = -ENOMEM; - goto error; - } ret = request_irq(sr_info->irq, sr_interrupt, - 0, name, sr_info); + 0, sr_info->name, sr_info); if (ret) goto error; disable_irq(sr_info->irq); @@ -265,7 +259,6 @@ error: dev_err(&sr_info->pdev->dev, "%s: ERROR in registering" "interrupt handler. Smartreflex will" "not function as desired\n", __func__); - kfree(name); kfree(sr_info); return ret; @@ -395,8 +388,7 @@ int sr_configure_errgen(struct voltagedomain *voltdm) struct omap_sr *sr = _sr_lookup(voltdm); if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for sr_%s not found\n", - __func__, voltdm->name); + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); return PTR_ERR(sr); } @@ -463,8 +455,7 @@ int sr_disable_errgen(struct voltagedomain *voltdm) struct omap_sr *sr = _sr_lookup(voltdm); if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for sr_%s not found\n", - __func__, voltdm->name); + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); return PTR_ERR(sr); } @@ -514,8 +505,7 @@ int sr_configure_minmax(struct voltagedomain *voltdm) struct omap_sr *sr = _sr_lookup(voltdm); if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for sr_%s not found\n", - __func__, voltdm->name); + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); return PTR_ERR(sr); } @@ -600,8 +590,7 @@ int sr_enable(struct voltagedomain *voltdm, unsigned long volt) int ret; if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for sr_%s not found\n", - __func__, voltdm->name); + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); return PTR_ERR(sr); } @@ -654,8 +643,7 @@ void sr_disable(struct voltagedomain *voltdm) struct omap_sr *sr = _sr_lookup(voltdm); if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for sr_%s not found\n", - __func__, voltdm->name); + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); return; } @@ -735,8 +723,7 @@ void omap_sr_enable(struct voltagedomain *voltdm) struct omap_sr *sr = _sr_lookup(voltdm); if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for sr_%s not found\n", - __func__, voltdm->name); + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); return; } @@ -768,8 +755,7 @@ void omap_sr_disable(struct voltagedomain *voltdm) struct omap_sr *sr = _sr_lookup(voltdm); if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for sr_%s not found\n", - __func__, voltdm->name); + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); return; } @@ -801,8 +787,7 @@ void omap_sr_disable_reset_volt(struct voltagedomain *voltdm) struct omap_sr *sr = _sr_lookup(voltdm); if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for sr_%s not found\n", - __func__, voltdm->name); + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); return; } @@ -889,7 +874,6 @@ static int __init omap_sr_probe(struct platform_device *pdev) struct dentry *nvalue_dir; struct omap_volt_data *volt_data; int i, ret = 0; - char *name; sr_info = kzalloc(sizeof(struct omap_sr), GFP_KERNEL); if (!sr_info) { @@ -926,6 +910,14 @@ static int __init omap_sr_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_irq_safe(&pdev->dev); + sr_info->name = kasprintf(GFP_KERNEL, "%s", pdata->name); + if (!sr_info->name) { + dev_err(&pdev->dev, "%s: Unable to alloc SR instance name\n", + __func__); + ret = -ENOMEM; + goto err_release_region; + } + sr_info->pdev = pdev; sr_info->srid = pdev->id; sr_info->voltdm = pdata->voltdm; @@ -973,20 +965,12 @@ static int __init omap_sr_probe(struct platform_device *pdev) } } - name = kasprintf(GFP_KERNEL, "sr_%s", sr_info->voltdm->name); - if (!name) { - dev_err(&pdev->dev, "%s: Unable to alloc debugfs name\n", - __func__); - ret = -ENOMEM; - goto err_iounmap; - } - sr_info->dbg_dir = debugfs_create_dir(name, sr_dbg_dir); - kfree(name); + sr_info->dbg_dir = debugfs_create_dir(sr_info->name, sr_dbg_dir); if (IS_ERR_OR_NULL(sr_info->dbg_dir)) { dev_err(&pdev->dev, "%s: Unable to create debugfs directory\n", __func__); ret = PTR_ERR(sr_info->dbg_dir); - goto err_iounmap; + goto err_free_name; } (void) debugfs_create_file("autocomp", S_IRUGO | S_IWUSR, @@ -1008,10 +992,10 @@ static int __init omap_sr_probe(struct platform_device *pdev) omap_voltage_get_volttable(sr_info->voltdm, &volt_data); if (!volt_data) { - dev_warn(&pdev->dev, "%s: No Voltage table for the" - " corresponding vdd vdd_%s. Cannot create debugfs" + dev_warn(&pdev->dev, "%s: %s: No Voltage table for the" + " corresponding vdd. Cannot create debugfs" "entries for n-values\n", - __func__, sr_info->voltdm->name); + __func__, sr_info->name); ret = -ENODATA; goto err_debugfs; } @@ -1029,6 +1013,8 @@ static int __init omap_sr_probe(struct platform_device *pdev) err_debugfs: debugfs_remove_recursive(sr_info->dbg_dir); +err_free_name: + kfree(sr_info->name); err_iounmap: list_del(&sr_info->node); iounmap(sr_info->base); @@ -1065,6 +1051,7 @@ static int __devexit omap_sr_remove(struct platform_device *pdev) list_del(&sr_info->node); iounmap(sr_info->base); + kfree(sr_info->name); kfree(sr_info); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(mem->start, resource_size(mem)); diff --git a/arch/arm/mach-omap2/sr_device.c b/arch/arm/mach-omap2/sr_device.c index 86e438e75105..e081174f28af 100644 --- a/arch/arm/mach-omap2/sr_device.c +++ b/arch/arm/mach-omap2/sr_device.c @@ -93,6 +93,7 @@ static int __init sr_dev_init(struct omap_hwmod *oh, void *user) goto exit; } + sr_data->name = oh->name; sr_data->ip_type = oh->class->rev; sr_data->senn_mod = 0x1; sr_data->senp_mod = 0x1; diff --git a/include/linux/power/smartreflex.h b/include/linux/power/smartreflex.h index 4224698cf8bd..884eaeea96be 100644 --- a/include/linux/power/smartreflex.h +++ b/include/linux/power/smartreflex.h @@ -144,6 +144,7 @@ #define OMAP3430_SR_ERRMAXLIMIT 0x02 struct omap_sr { + char *name; struct list_head node; struct platform_device *pdev; struct omap_sr_nvalue_table *nvalue_table; @@ -232,6 +233,7 @@ struct omap_sr_nvalue_table { /** * struct omap_sr_data - Smartreflex platform data. * + * @name: instance name * @ip_type: Smartreflex IP type. * @senp_mod: SENPENABLE value for the sr * @senn_mod: SENNENABLE value for sr @@ -243,6 +245,7 @@ struct omap_sr_nvalue_table { * @voltdm: Pointer to the voltage domain associated with the SR */ struct omap_sr_data { + const char *name; int ip_type; u32 senp_mod; u32 senn_mod; -- cgit v1.2.3-59-g8ed1b From 50e4a7d0b26c86628300edf4625cc5ff16a7a227 Mon Sep 17 00:00:00 2001 From: Jean Pihet Date: Tue, 24 Apr 2012 10:56:40 +0530 Subject: ARM: OMAP2+: SmartReflex: introduce a busy loop condition test macro Now that omap_test_timeout is only accessible from mach-omap2/, introduce a similar function for SR. This change makes the SmartReflex implementation ready for the move to drivers/. Signed-off-by: Jean Pihet Signed-off-by: J Keerthy Reviewed-by: Kevin Hilman Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/smartreflex.c | 12 ++++++------ include/linux/power/smartreflex.h | 23 ++++++++++++++++++++++- 2 files changed, 28 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c index d8592771838f..acef08d837cc 100644 --- a/arch/arm/mach-omap2/smartreflex.c +++ b/arch/arm/mach-omap2/smartreflex.c @@ -289,9 +289,9 @@ static void sr_v1_disable(struct omap_sr *sr) * Wait for SR to be disabled. * wait until ERRCONFIG.MCUDISACKINTST = 1. Typical latency is 1us. */ - omap_test_timeout((sr_read_reg(sr, ERRCONFIG_V1) & - ERRCONFIG_MCUDISACKINTST), SR_DISABLE_TIMEOUT, - timeout); + sr_test_cond_timeout((sr_read_reg(sr, ERRCONFIG_V1) & + ERRCONFIG_MCUDISACKINTST), SR_DISABLE_TIMEOUT, + timeout); if (timeout >= SR_DISABLE_TIMEOUT) dev_warn(&sr->pdev->dev, "%s: Smartreflex disable timedout\n", @@ -334,9 +334,9 @@ static void sr_v2_disable(struct omap_sr *sr) * Wait for SR to be disabled. * wait until IRQSTATUS.MCUDISACKINTST = 1. Typical latency is 1us. */ - omap_test_timeout((sr_read_reg(sr, IRQSTATUS) & - IRQSTATUS_MCUDISABLEACKINT), SR_DISABLE_TIMEOUT, - timeout); + sr_test_cond_timeout((sr_read_reg(sr, IRQSTATUS) & + IRQSTATUS_MCUDISABLEACKINT), SR_DISABLE_TIMEOUT, + timeout); if (timeout >= SR_DISABLE_TIMEOUT) dev_warn(&sr->pdev->dev, "%s: Smartreflex disable timedout\n", diff --git a/include/linux/power/smartreflex.h b/include/linux/power/smartreflex.h index 884eaeea96be..78b795ea2709 100644 --- a/include/linux/power/smartreflex.h +++ b/include/linux/power/smartreflex.h @@ -22,7 +22,7 @@ #include #include - +#include #include /* @@ -167,6 +167,27 @@ struct omap_sr { void __iomem *base; }; +/** + * test_cond_timeout - busy-loop, testing a condition + * @cond: condition to test until it evaluates to true + * @timeout: maximum number of microseconds in the timeout + * @index: loop index (integer) + * + * Loop waiting for @cond to become true or until at least @timeout + * microseconds have passed. To use, define some integer @index in the + * calling code. After running, if @index == @timeout, then the loop has + * timed out. + * + * Copied from omap_test_timeout */ +#define sr_test_cond_timeout(cond, timeout, index) \ +({ \ + for (index = 0; index < timeout; index++) { \ + if (cond) \ + break; \ + udelay(1); \ + } \ +}) + /** * struct omap_sr_pmic_data - Strucutre to be populated by pmic code to pass * pmic specific info to smartreflex driver -- cgit v1.2.3-59-g8ed1b From 5e7f2e12e4ea14a34fb9b5941d60a4464fc8d40a Mon Sep 17 00:00:00 2001 From: Jean Pihet Date: Wed, 25 Apr 2012 11:19:44 +0530 Subject: ARM: OMAP2+: SmartReflex: Use per-OPP data structure The SmartReflex driver incorrectly treats some per-OPP data as data common to all OPPs (e.g., ERRMINLIMIT). Move this data into a per-OPP data structure. Furthermore, in order to make the SmartReflex implementation ready for the move to drivers/, remove the dependency from the SR driver code to the voltage layer by querying the data tables only from the SR device init code. Based on Paul's original code for the SmartReflex driver conversion. Signed-off-by: Jean Pihet Signed-off-by: J Keerthy Reviewed-by: Kevin Hilman Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/smartreflex.c | 38 ++++++++++++++++++-------------------- arch/arm/mach-omap2/sr_device.c | 36 ++++++++++++++++++++++++++++++------ include/linux/power/smartreflex.h | 8 ++++++-- 3 files changed, 54 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c index acef08d837cc..20075de13868 100644 --- a/arch/arm/mach-omap2/smartreflex.c +++ b/arch/arm/mach-omap2/smartreflex.c @@ -347,22 +347,23 @@ static void sr_v2_disable(struct omap_sr *sr) sr_write_reg(sr, IRQSTATUS, IRQSTATUS_MCUDISABLEACKINT); } -static u32 sr_retrieve_nvalue(struct omap_sr *sr, u32 efuse_offs) +static struct omap_sr_nvalue_table *sr_retrieve_nvalue_row( + struct omap_sr *sr, u32 efuse_offs) { int i; if (!sr->nvalue_table) { dev_warn(&sr->pdev->dev, "%s: Missing ntarget value table\n", __func__); - return 0; + return NULL; } for (i = 0; i < sr->nvalue_count; i++) { if (sr->nvalue_table[i].efuse_offs == efuse_offs) - return sr->nvalue_table[i].nvalue; + return &sr->nvalue_table[i]; } - return 0; + return NULL; } /* Public Functions */ @@ -586,7 +587,7 @@ int sr_enable(struct voltagedomain *voltdm, unsigned long volt) { struct omap_volt_data *volt_data; struct omap_sr *sr = _sr_lookup(voltdm); - u32 nvalue_reciprocal; + struct omap_sr_nvalue_table *nvalue_row; int ret; if (IS_ERR(sr)) { @@ -602,16 +603,16 @@ int sr_enable(struct voltagedomain *voltdm, unsigned long volt) return PTR_ERR(volt_data); } - nvalue_reciprocal = sr_retrieve_nvalue(sr, volt_data->sr_efuse_offs); + nvalue_row = sr_retrieve_nvalue_row(sr, volt_data->sr_efuse_offs); - if (!nvalue_reciprocal) { - dev_warn(&sr->pdev->dev, "%s: NVALUE = 0 at voltage %ld\n", - __func__, volt); + if (!nvalue_row) { + dev_warn(&sr->pdev->dev, "%s: failure getting SR data for this voltage %ld\n", + __func__, volt); return -ENODATA; } /* errminlimit is opp dependent and hence linked to voltage */ - sr->err_minlimit = volt_data->sr_errminlimit; + sr->err_minlimit = nvalue_row->errminlimit; pm_runtime_get_sync(&sr->pdev->dev); @@ -624,7 +625,7 @@ int sr_enable(struct voltagedomain *voltdm, unsigned long volt) if (ret) return ret; - sr_write_reg(sr, NVALUERECIPROCAL, nvalue_reciprocal); + sr_write_reg(sr, NVALUERECIPROCAL, nvalue_row->nvalue); /* SRCONFIG - enable SR */ sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, SRCONFIG_SRENABLE); @@ -872,7 +873,6 @@ static int __init omap_sr_probe(struct platform_device *pdev) struct omap_sr_data *pdata = pdev->dev.platform_data; struct resource *mem, *irq; struct dentry *nvalue_dir; - struct omap_volt_data *volt_data; int i, ret = 0; sr_info = kzalloc(sizeof(struct omap_sr), GFP_KERNEL); @@ -990,12 +990,10 @@ static int __init omap_sr_probe(struct platform_device *pdev) goto err_debugfs; } - omap_voltage_get_volttable(sr_info->voltdm, &volt_data); - if (!volt_data) { - dev_warn(&pdev->dev, "%s: %s: No Voltage table for the" - " corresponding vdd. Cannot create debugfs" - "entries for n-values\n", - __func__, sr_info->name); + if (sr_info->nvalue_count == 0 || !sr_info->nvalue_table) { + dev_warn(&pdev->dev, "%s: %s: No Voltage table for the corresponding vdd. Cannot create debugfs entries for n-values\n", + __func__, sr_info->name); + ret = -ENODATA; goto err_debugfs; } @@ -1003,8 +1001,8 @@ static int __init omap_sr_probe(struct platform_device *pdev) for (i = 0; i < sr_info->nvalue_count; i++) { char name[NVALUE_NAME_LEN + 1]; - snprintf(name, sizeof(name), "volt_%d", - volt_data[i].volt_nominal); + snprintf(name, sizeof(name), "volt_%lu", + sr_info->nvalue_table[i].volt_nominal); (void) debugfs_create_x32(name, S_IRUGO | S_IWUSR, nvalue_dir, &(sr_info->nvalue_table[i].nvalue)); } diff --git a/arch/arm/mach-omap2/sr_device.c b/arch/arm/mach-omap2/sr_device.c index e081174f28af..e107e3915a8a 100644 --- a/arch/arm/mach-omap2/sr_device.c +++ b/arch/arm/mach-omap2/sr_device.c @@ -36,7 +36,10 @@ static void __init sr_set_nvalues(struct omap_volt_data *volt_data, struct omap_sr_data *sr_data) { struct omap_sr_nvalue_table *nvalue_table; - int i, count = 0; + int i, j, count = 0; + + sr_data->nvalue_count = 0; + sr_data->nvalue_table = NULL; while (volt_data[count].volt_nominal) count++; @@ -44,8 +47,14 @@ static void __init sr_set_nvalues(struct omap_volt_data *volt_data, nvalue_table = kzalloc(sizeof(struct omap_sr_nvalue_table)*count, GFP_KERNEL); - for (i = 0; i < count; i++) { + if (!nvalue_table) { + pr_err("OMAP: SmartReflex: cannot allocate memory for n-value table\n"); + return; + } + + for (i = 0, j = 0; i < count; i++) { u32 v; + /* * In OMAP4 the efuse registers are 24 bit aligned. * A __raw_readl will fail for non-32 bit aligned address @@ -58,15 +67,30 @@ static void __init sr_set_nvalues(struct omap_volt_data *volt_data, omap_ctrl_readb(offset + 1) << 8 | omap_ctrl_readb(offset + 2) << 16; } else { - v = omap_ctrl_readl(volt_data[i].sr_efuse_offs); + v = omap_ctrl_readl(volt_data[i].sr_efuse_offs); } - nvalue_table[i].efuse_offs = volt_data[i].sr_efuse_offs; - nvalue_table[i].nvalue = v; + /* + * Many OMAP SoCs don't have the eFuse values set. + * For example, pretty much all OMAP3xxx before + * ES3.something. + * + * XXX There needs to be some way for board files or + * userspace to add these in. + */ + if (v == 0) + continue; + + nvalue_table[j].nvalue = v; + nvalue_table[j].efuse_offs = volt_data[i].sr_efuse_offs; + nvalue_table[j].errminlimit = volt_data[i].sr_errminlimit; + nvalue_table[j].volt_nominal = volt_data[i].volt_nominal; + + j++; } sr_data->nvalue_table = nvalue_table; - sr_data->nvalue_count = count; + sr_data->nvalue_count = j; } static int __init sr_dev_init(struct omap_hwmod *oh, void *user) diff --git a/include/linux/power/smartreflex.h b/include/linux/power/smartreflex.h index 78b795ea2709..222f90183712 100644 --- a/include/linux/power/smartreflex.h +++ b/include/linux/power/smartreflex.h @@ -243,12 +243,16 @@ struct omap_sr_class_data { /** * struct omap_sr_nvalue_table - Smartreflex n-target value info * - * @efuse_offs: The offset of the efuse where n-target values are stored. - * @nvalue: The n-target value. + * @efuse_offs: The offset of the efuse where n-target values are stored. + * @nvalue: The n-target value. + * @errminlimit: The value of the ERRMINLIMIT bitfield for this n-target + * @volt_nominal: microvolts DC that the VDD is initially programmed to */ struct omap_sr_nvalue_table { u32 efuse_offs; u32 nvalue; + u32 errminlimit; + unsigned long volt_nominal; }; /** -- cgit v1.2.3-59-g8ed1b From 7fb149ffe357d6ad672cf9325181530b4c478a81 Mon Sep 17 00:00:00 2001 From: Jean Pihet Date: Tue, 24 Apr 2012 11:38:50 +0530 Subject: ARM: OMAP2+: SmartReflex: add POWER_AVS Kconfig options Add a Kconfig menu (POWER_AVS) and rename the Kconfig options for the OMAP SmartReflex implementation: CONFIG_OMAP_SMARTREFLEX renames to CONFIG_POWER_AVS_OMAP CONFIG_OMAP_SMARTREFLEX_CLASS3 renames to CONFIG_POWER_AVS_OMAP_CLASS3 This change makes the SmartReflex implementation ready for the move to drivers/. Signed-off-by: Jean Pihet Signed-off-by: J Keerthy Reviewed-by: Kevin Hilman Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/Makefile | 5 +++-- arch/arm/mach-omap2/pm.h | 2 +- arch/arm/plat-omap/Kconfig | 45 ++++++++++++++++++++++++++------------- include/linux/power/smartreflex.h | 2 +- 4 files changed, 35 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 385c083d24b2..518444acc90f 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -69,8 +69,9 @@ obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o \ obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o omap-mpuss-lowpower.o \ cpuidle44xx.o obj-$(CONFIG_PM_DEBUG) += pm-debug.o -obj-$(CONFIG_OMAP_SMARTREFLEX) += sr_device.o smartreflex.o -obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS3) += smartreflex-class3.o + +obj-$(CONFIG_POWER_AVS_OMAP) += sr_device.o smartreflex.o +obj-$(CONFIG_POWER_AVS_OMAP_CLASS3) += smartreflex-class3.o AFLAGS_sleep24xx.o :=-Wa,-march=armv6 AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a$(plus_sec) diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h index 78564895e914..9fac67d6c985 100644 --- a/arch/arm/mach-omap2/pm.h +++ b/arch/arm/mach-omap2/pm.h @@ -88,7 +88,7 @@ extern void enable_omap3630_toggle_l2_on_restore(void); static inline void enable_omap3630_toggle_l2_on_restore(void) { } #endif /* defined(CONFIG_PM) && defined(CONFIG_ARCH_OMAP3) */ -#ifdef CONFIG_OMAP_SMARTREFLEX +#ifdef CONFIG_POWER_AVS_OMAP extern int omap_devinit_smartreflex(void); extern void omap_enable_smartreflex_on_init(void); #else diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index ad95c7a5d009..bba384dfbcf6 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig @@ -45,37 +45,52 @@ config OMAP_DEBUG_LEDS depends on OMAP_DEBUG_DEVICES default y if LEDS_CLASS -config OMAP_SMARTREFLEX - bool "SmartReflex support" - depends on (ARCH_OMAP3 || ARCH_OMAP4) && PM +menuconfig POWER_AVS + tristate "Adaptive Voltage Scaling class support" help - Say Y if you want to enable SmartReflex. + AVS(Adaptive Voltage Scaling) is a power management technique which + finely controls the operating voltage of a device in order to optimize + (i.e. reduce) its power consumption. + At a given operating point the voltage is adapted depending on + static factors (chip manufacturing process) and dynamic factors + (temperature depending performance). + AVS is also called SmartReflex on OMAP devices. + + Say Y here to enable Adaptive Voltage Scaling class support. + +if POWER_AVS - SmartReflex can perform continuous dynamic voltage - scaling around the nominal operating point voltage - according to silicon characteristics and operating - conditions. Enabling SmartReflex reduces power - consumption. +config POWER_AVS_OMAP + bool "AVS(Adaptive Voltage Scaling) support for OMAP IP versions 1&2" + depends on (ARCH_OMAP3 || ARCH_OMAP4) && PM + help + Say Y to enable AVS support on OMAP containing the version 1 or + version 2 of the SmartReflex IP. + V1 is the 65nm version used in OMAP3430. + V2 is the update for the 45nm version of the IP used in OMAP3630 + and OMAP4430 Please note, that by default SmartReflex is only - initialized. To enable the automatic voltage - compensation for vdd mpu and vdd core from user space, + initialized and not enabled. To enable the automatic voltage + compensation for vdd mpu and vdd core from user space, user must write 1 to - /debug/voltage/vdd_/smartreflex/autocomp, - where X is mpu or core for OMAP3. + /debug/smartreflex/sr_/autocomp, + where X is mpu_iva or core for OMAP3. Optionally autocompensation can be enabled in the kernel by default during system init via the enable_on_init flag which an be passed as platform data to the smartreflex driver. -config OMAP_SMARTREFLEX_CLASS3 +config POWER_AVS_OMAP_CLASS3 bool "Class 3 mode of Smartreflex Implementation" - depends on OMAP_SMARTREFLEX && TWL4030_CORE + depends on POWER_AVS_OMAP && TWL4030_CORE help Say Y to enable Class 3 implementation of Smartreflex Class 3 implementation of Smartreflex employs continuous hardware voltage calibration. +endif # POWER_AVS + config OMAP_RESET_CLOCKS bool "Reset unused clocks during boot" depends on ARCH_OMAP diff --git a/include/linux/power/smartreflex.h b/include/linux/power/smartreflex.h index 222f90183712..3101e62a1213 100644 --- a/include/linux/power/smartreflex.h +++ b/include/linux/power/smartreflex.h @@ -207,7 +207,7 @@ struct omap_smartreflex_dev_attr { const char *sensor_voltdm_name; }; -#ifdef CONFIG_OMAP_SMARTREFLEX +#ifdef CONFIG_POWER_AVS_OMAP /* * The smart reflex driver supports CLASS1 CLASS2 and CLASS3 SR. * The smartreflex class driver should pass the class type. -- cgit v1.2.3-59-g8ed1b From 141eba2e006dd8145bed2e49fae3de5af65ab9b0 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 24 May 2012 10:47:26 -0600 Subject: regmap: allow busses to request formatting with specific endianness Add a field to struct regmap_bus that allows bus drivers to request that register addresses and values be formatted with a specific endianness. The default endianness is unchanged from current operation: Big. Implement native endian formatting/parsing for 16- and 32-bit values. This will be enough to support regmap-mmio.c. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 101 ++++++++++++++++++++++++++++++++++++++----- include/linux/regmap.h | 25 +++++++++++ 2 files changed, 115 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 0bcda488f11c..1cf721421bec 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -119,13 +119,19 @@ static void regmap_format_8(void *buf, unsigned int val, unsigned int shift) b[0] = val << shift; } -static void regmap_format_16(void *buf, unsigned int val, unsigned int shift) +static void regmap_format_16_be(void *buf, unsigned int val, unsigned int shift) { __be16 *b = buf; b[0] = cpu_to_be16(val << shift); } +static void regmap_format_16_native(void *buf, unsigned int val, + unsigned int shift) +{ + *(u16 *)buf = val << shift; +} + static void regmap_format_24(void *buf, unsigned int val, unsigned int shift) { u8 *b = buf; @@ -137,13 +143,19 @@ static void regmap_format_24(void *buf, unsigned int val, unsigned int shift) b[2] = val; } -static void regmap_format_32(void *buf, unsigned int val, unsigned int shift) +static void regmap_format_32_be(void *buf, unsigned int val, unsigned int shift) { __be32 *b = buf; b[0] = cpu_to_be32(val << shift); } +static void regmap_format_32_native(void *buf, unsigned int val, + unsigned int shift) +{ + *(u32 *)buf = val << shift; +} + static unsigned int regmap_parse_8(void *buf) { u8 *b = buf; @@ -151,7 +163,7 @@ static unsigned int regmap_parse_8(void *buf) return b[0]; } -static unsigned int regmap_parse_16(void *buf) +static unsigned int regmap_parse_16_be(void *buf) { __be16 *b = buf; @@ -160,6 +172,11 @@ static unsigned int regmap_parse_16(void *buf) return b[0]; } +static unsigned int regmap_parse_16_native(void *buf) +{ + return *(u16 *)buf; +} + static unsigned int regmap_parse_24(void *buf) { u8 *b = buf; @@ -170,7 +187,7 @@ static unsigned int regmap_parse_24(void *buf) return ret; } -static unsigned int regmap_parse_32(void *buf) +static unsigned int regmap_parse_32_be(void *buf) { __be32 *b = buf; @@ -179,6 +196,11 @@ static unsigned int regmap_parse_32(void *buf) return b[0]; } +static unsigned int regmap_parse_32_native(void *buf) +{ + return *(u32 *)buf; +} + static void regmap_lock_mutex(struct regmap *map) { mutex_lock(&map->mutex); @@ -227,6 +249,7 @@ struct regmap *regmap_init(struct device *dev, { struct regmap *map, **m; int ret = -EINVAL; + enum regmap_endian reg_endian, val_endian; if (!bus || !config) goto err; @@ -275,6 +298,18 @@ struct regmap *regmap_init(struct device *dev, map->read_flag_mask = bus->read_flag_mask; } + reg_endian = config->reg_format_endian; + if (reg_endian == REGMAP_ENDIAN_DEFAULT) + reg_endian = bus->reg_format_endian_default; + if (reg_endian == REGMAP_ENDIAN_DEFAULT) + reg_endian = REGMAP_ENDIAN_BIG; + + val_endian = config->val_format_endian; + if (val_endian == REGMAP_ENDIAN_DEFAULT) + val_endian = bus->val_format_endian_default; + if (val_endian == REGMAP_ENDIAN_DEFAULT) + val_endian = REGMAP_ENDIAN_BIG; + switch (config->reg_bits + map->reg_shift) { case 2: switch (config->val_bits) { @@ -321,11 +356,29 @@ struct regmap *regmap_init(struct device *dev, break; case 16: - map->format.format_reg = regmap_format_16; + switch (reg_endian) { + case REGMAP_ENDIAN_BIG: + map->format.format_reg = regmap_format_16_be; + break; + case REGMAP_ENDIAN_NATIVE: + map->format.format_reg = regmap_format_16_native; + break; + default: + goto err_map; + } break; case 32: - map->format.format_reg = regmap_format_32; + switch (reg_endian) { + case REGMAP_ENDIAN_BIG: + map->format.format_reg = regmap_format_32_be; + break; + case REGMAP_ENDIAN_NATIVE: + map->format.format_reg = regmap_format_32_native; + break; + default: + goto err_map; + } break; default: @@ -338,21 +391,47 @@ struct regmap *regmap_init(struct device *dev, map->format.parse_val = regmap_parse_8; break; case 16: - map->format.format_val = regmap_format_16; - map->format.parse_val = regmap_parse_16; + switch (val_endian) { + case REGMAP_ENDIAN_BIG: + map->format.format_val = regmap_format_16_be; + map->format.parse_val = regmap_parse_16_be; + break; + case REGMAP_ENDIAN_NATIVE: + map->format.format_val = regmap_format_16_native; + map->format.parse_val = regmap_parse_16_native; + break; + default: + goto err_map; + } break; case 24: + if (val_endian != REGMAP_ENDIAN_BIG) + goto err_map; map->format.format_val = regmap_format_24; map->format.parse_val = regmap_parse_24; break; case 32: - map->format.format_val = regmap_format_32; - map->format.parse_val = regmap_parse_32; + switch (val_endian) { + case REGMAP_ENDIAN_BIG: + map->format.format_val = regmap_format_32_be; + map->format.parse_val = regmap_parse_32_be; + break; + case REGMAP_ENDIAN_NATIVE: + map->format.format_val = regmap_format_32_native; + map->format.parse_val = regmap_parse_32_native; + break; + default: + goto err_map; + } break; } - if (map->format.format_write) + if (map->format.format_write) { + if ((reg_endian != REGMAP_ENDIAN_BIG) || + (val_endian != REGMAP_ENDIAN_BIG)) + goto err_map; map->use_single_rw = true; + } if (!map->format.format_write && !(map->format.format_reg && map->format.format_val)) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 56af22ec9aba..26136b577009 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -43,6 +43,14 @@ struct reg_default { #ifdef CONFIG_REGMAP +enum regmap_endian { + /* Unspecified -> 0 -> Backwards compatible default */ + REGMAP_ENDIAN_DEFAULT = 0, + REGMAP_ENDIAN_BIG, + REGMAP_ENDIAN_LITTLE, + REGMAP_ENDIAN_NATIVE, +}; + /** * Configuration for the register map of a device. * @@ -84,6 +92,12 @@ struct reg_default { * @reg_defaults_raw: Power on reset values for registers (for use with * register cache support). * @num_reg_defaults_raw: Number of elements in reg_defaults_raw. + * @reg_format_endian: Endianness for formatted register addresses. If this is + * DEFAULT, the @reg_format_endian_default value from the + * regmap bus is used. + * @val_format_endian: Endianness for formatted register values. If this is + * DEFAULT, the @reg_format_endian_default value from the + * regmap bus is used. */ struct regmap_config { const char *name; @@ -109,6 +123,9 @@ struct regmap_config { u8 write_flag_mask; bool use_single_rw; + + enum regmap_endian reg_format_endian; + enum regmap_endian val_format_endian; }; typedef int (*regmap_hw_write)(void *context, const void *data, @@ -133,6 +150,12 @@ typedef void (*regmap_hw_free_context)(void *context); * data. * @read_flag_mask: Mask to be set in the top byte of the register when doing * a read. + * @reg_format_endian_default: Default endianness for formatted register + * addresses. Used when the regmap_config specifies DEFAULT. If this is + * DEFAULT, BIG is assumed. + * @val_format_endian_default: Default endianness for formatted register + * values. Used when the regmap_config specifies DEFAULT. If this is + * DEFAULT, BIG is assumed. */ struct regmap_bus { bool fast_io; @@ -141,6 +164,8 @@ struct regmap_bus { regmap_hw_read read; regmap_hw_free_context free_context; u8 read_flag_mask; + enum regmap_endian reg_format_endian_default; + enum regmap_endian val_format_endian_default; }; struct regmap *regmap_init(struct device *dev, -- cgit v1.2.3-59-g8ed1b From b026ddbbd25e3560c8d69beb96a5980d96c59b43 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 31 May 2012 21:01:46 +0100 Subject: regmap: Constify regmap_irq_chip We should never be modifying it and it lets drivers declare it const. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 6 +++--- include/linux/regmap.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 4fac4b9be88f..c190229daa59 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -24,7 +24,7 @@ struct regmap_irq_chip_data { struct mutex lock; struct regmap *map; - struct regmap_irq_chip *chip; + const struct regmap_irq_chip *chip; int irq_base; struct irq_domain *domain; @@ -103,7 +103,7 @@ static struct irq_chip regmap_irq_chip = { static irqreturn_t regmap_irq_thread(int irq, void *d) { struct regmap_irq_chip_data *data = d; - struct regmap_irq_chip *chip = data->chip; + const struct regmap_irq_chip *chip = data->chip; struct regmap *map = data->map; int ret, i; bool handled = false; @@ -195,7 +195,7 @@ static struct irq_domain_ops regmap_domain_ops = { * register values used by the IRQ controller over suspend and resume. */ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, - int irq_base, struct regmap_irq_chip *chip, + int irq_base, const struct regmap_irq_chip *chip, struct regmap_irq_chip_data **data) { struct regmap_irq_chip_data *d; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 26136b577009..f9b624c83464 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -268,7 +268,7 @@ struct regmap_irq_chip { struct regmap_irq_chip_data; int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, - int irq_base, struct regmap_irq_chip *chip, + int irq_base, const struct regmap_irq_chip *chip, struct regmap_irq_chip_data **data); void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *data); int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data); -- cgit v1.2.3-59-g8ed1b From cffc9592fde309deafce12362e0a285108cfa3c8 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 20 May 2012 10:30:21 +0800 Subject: regulator: core: Allow drivers to set voltage mapping table in regulator_desc Some regulator hardware use table based mapping can set volt_table in regulator_desc and use regulator_list_voltage_table() for their list_voltage callback. Signed-off-by: Axel Lin Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- drivers/regulator/core.c | 25 +++++++++++++++++++++++++ include/linux/regulator/driver.h | 5 +++++ 2 files changed, 30 insertions(+) (limited to 'include/linux') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 7584a74eec8a..333b7ebe7cae 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1882,6 +1882,31 @@ int regulator_list_voltage_linear(struct regulator_dev *rdev, } EXPORT_SYMBOL_GPL(regulator_list_voltage_linear); +/** + * regulator_list_voltage_table - List voltages with table based mapping + * + * @rdev: Regulator device + * @selector: Selector to convert into a voltage + * + * Regulators with table based mapping between voltages and + * selectors can set volt_table in the regulator descriptor + * and then use this function as their list_voltage() operation. + */ +int regulator_list_voltage_table(struct regulator_dev *rdev, + unsigned int selector) +{ + if (!rdev->desc->volt_table) { + BUG_ON(!rdev->desc->volt_table); + return -EINVAL; + } + + if (selector >= rdev->desc->n_voltages) + return -EINVAL; + + return rdev->desc->volt_table[selector]; +} +EXPORT_SYMBOL_GPL(regulator_list_voltage_table); + /** * regulator_list_voltage - enumerate supported voltages * @regulator: regulator source diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index b0432cc2b169..80226383e561 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -170,6 +170,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) + * @volt_table: Voltage mapping table (if table based mapping) * * @vsel_reg: Register for selector when using regulator_regmap_X_voltage_ * @vsel_mask: Mask for register bitfield used for selector @@ -189,6 +190,8 @@ struct regulator_desc { unsigned int min_uV; unsigned int uV_step; + const unsigned int *volt_table; + unsigned int vsel_reg; unsigned int vsel_mask; unsigned int enable_reg; @@ -271,6 +274,8 @@ int regulator_mode_to_status(unsigned int); int regulator_list_voltage_linear(struct regulator_dev *rdev, unsigned int selector); +int regulator_list_voltage_table(struct regulator_dev *rdev, + unsigned int selector); int regulator_map_voltage_linear(struct regulator_dev *rdev, int min_uV, int max_uV); int regulator_map_voltage_iterate(struct regulator_dev *rdev, -- cgit v1.2.3-59-g8ed1b From d594e987c6f5417cc63dd7e107a2a03a7eeee03f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 4 Jun 2012 03:50:35 +0000 Subject: sock_diag: add SK_MEMINFO_BACKLOG Adding socket backlog len in INET_DIAG_SKMEMINFO is really useful to diagnose various TCP problems. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/sock_diag.h | 1 + net/core/sock_diag.c | 1 + 2 files changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sock_diag.h b/include/linux/sock_diag.h index db4bae78bda9..6793fac5eab5 100644 --- a/include/linux/sock_diag.h +++ b/include/linux/sock_diag.h @@ -18,6 +18,7 @@ enum { SK_MEMINFO_FWD_ALLOC, SK_MEMINFO_WMEM_QUEUED, SK_MEMINFO_OPTMEM, + SK_MEMINFO_BACKLOG, SK_MEMINFO_VARS, }; diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index 5fd146720f39..0d934ce1075f 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -46,6 +46,7 @@ int sock_diag_put_meminfo(struct sock *sk, struct sk_buff *skb, int attrtype) mem[SK_MEMINFO_FWD_ALLOC] = sk->sk_forward_alloc; mem[SK_MEMINFO_WMEM_QUEUED] = sk->sk_wmem_queued; mem[SK_MEMINFO_OPTMEM] = atomic_read(&sk->sk_omem_alloc); + mem[SK_MEMINFO_BACKLOG] = sk->sk_backlog.len; return 0; -- cgit v1.2.3-59-g8ed1b From 29a6b6c060445eb46528785d51a2d8b0e6d898c4 Mon Sep 17 00:00:00 2001 From: Raffaele Recalcati Date: Sun, 3 Jun 2012 10:43:43 +0000 Subject: net/ethernet: ks8851_mll mac address configuration support added Signed-off-by: Raffaele Recalcati Signed-off-by: David S. Miller --- drivers/net/ethernet/micrel/ks8851_mll.c | 25 +++++++++++++++++------- include/linux/ks8851_mll.h | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 include/linux/ks8851_mll.h (limited to 'include/linux') diff --git a/drivers/net/ethernet/micrel/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c index 5ffde23ac8fb..70bd329882c6 100644 --- a/drivers/net/ethernet/micrel/ks8851_mll.c +++ b/drivers/net/ethernet/micrel/ks8851_mll.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #define DRV_NAME "ks8851_mll" @@ -1515,6 +1515,7 @@ static int __devinit ks8851_probe(struct platform_device *pdev) struct net_device *netdev; struct ks_net *ks; u16 id, data; + struct ks8851_mll_platform_data *pdata; io_d = platform_get_resource(pdev, IORESOURCE_MEM, 0); io_c = platform_get_resource(pdev, IORESOURCE_MEM, 1); @@ -1596,17 +1597,27 @@ static int __devinit ks8851_probe(struct platform_device *pdev) ks_disable_qmu(ks); ks_setup(ks); ks_setup_int(ks); - memcpy(netdev->dev_addr, ks->mac_addr, 6); data = ks_rdreg16(ks, KS_OBCR); ks_wrreg16(ks, KS_OBCR, data | OBCR_ODS_16MA); - /** - * If you want to use the default MAC addr, - * comment out the 2 functions below. - */ + /* overwriting the default MAC address */ + pdata = pdev->dev.platform_data; + if (!pdata) { + netdev_err(netdev, "No platform data\n"); + err = -ENODEV; + goto err_register; + } + memcpy(ks->mac_addr, pdata->mac_addr, 6); + if (!is_valid_ether_addr(ks->mac_addr)) { + /* Use random MAC address if none passed */ + random_ether_addr(ks->mac_addr); + netdev_info(netdev, "Using random mac address\n"); + } + netdev_info(netdev, "Mac address is: %pM\n", ks->mac_addr); + + memcpy(netdev->dev_addr, ks->mac_addr, 6); - random_ether_addr(netdev->dev_addr); ks_set_mac(ks, netdev->dev_addr); id = ks_rdreg16(ks, KS_CIDER); diff --git a/include/linux/ks8851_mll.h b/include/linux/ks8851_mll.h new file mode 100644 index 000000000000..e9ccfb59ed30 --- /dev/null +++ b/include/linux/ks8851_mll.h @@ -0,0 +1,33 @@ +/* + * ks8861_mll platform data struct definition + * Copyright (c) 2012 BTicino S.p.A. + * + * 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. + */ + +#ifndef _LINUX_KS8851_MLL_H +#define _LINUX_KS8851_MLL_H + +#include + +/** + * struct ks8851_mll_platform_data - Platform data of the KS8851_MLL network driver + * @macaddr: The MAC address of the device, set to all 0:s to use the on in + * the chip. + */ +struct ks8851_mll_platform_data { + u8 mac_addr[ETH_ALEN]; +}; + +#endif -- cgit v1.2.3-59-g8ed1b From fe7c580073280c15bb4eb4f82bf20dddc1a68383 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 15 May 2012 15:57:06 +0200 Subject: NFC: Add target mode protocols to the polling loop startup routine Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 39 +++++++++++++++++++++++++++++---------- drivers/nfc/pn544_hci.c | 10 ++++++---- include/linux/nfc.h | 4 ++++ include/net/nfc/hci.h | 3 ++- include/net/nfc/nfc.h | 3 ++- include/net/nfc/shdlc.h | 3 ++- net/nfc/core.c | 10 +++++----- net/nfc/hci/core.c | 5 +++-- net/nfc/hci/shdlc.c | 6 ++++-- net/nfc/nci/core.c | 7 ++++--- net/nfc/netlink.c | 19 +++++++++++++++---- net/nfc/nfc.h | 2 +- 12 files changed, 77 insertions(+), 34 deletions(-) (limited to 'include/linux') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 19110f0eb15f..38a523c62132 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1078,27 +1078,23 @@ stop_poll: return 0; } -static int pn533_start_poll(struct nfc_dev *nfc_dev, u32 protocols) +static int pn533_init_target(struct nfc_dev *nfc_dev, u32 protocols) +{ + return 0; +} + +static int pn533_start_im_poll(struct nfc_dev *nfc_dev, u32 protocols) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); struct pn533_poll_modulations *start_mod; int rc; - nfc_dev_dbg(&dev->interface->dev, "%s - protocols=0x%x", __func__, - protocols); - if (dev->poll_mod_count) { nfc_dev_err(&dev->interface->dev, "Polling operation already" " active"); return -EBUSY; } - if (dev->tgt_active_prot) { - nfc_dev_err(&dev->interface->dev, "Cannot poll with a target" - " already activated"); - return -EBUSY; - } - pn533_poll_create_mod_list(dev, protocols); if (!dev->poll_mod_count) { @@ -1135,6 +1131,29 @@ error: return rc; } +static int pn533_start_poll(struct nfc_dev *nfc_dev, + u32 im_protocols, u32 tm_protocols) +{ + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + + nfc_dev_dbg(&dev->interface->dev, + "%s: im protocols 0x%x tm protocols 0x%x", + __func__, im_protocols, tm_protocols); + + if (dev->tgt_active_prot) { + nfc_dev_err(&dev->interface->dev, + "Cannot poll with a target already activated"); + return -EBUSY; + } + + if (!tm_protocols) + return pn533_start_im_poll(nfc_dev, im_protocols); + else if (!im_protocols) + return pn533_init_target(nfc_dev, tm_protocols); + else + return -EINVAL; +} + static void pn533_stop_poll(struct nfc_dev *nfc_dev) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index 281f18c2fb82..457eac35dc74 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -576,7 +576,8 @@ static int pn544_hci_xmit(struct nfc_shdlc *shdlc, struct sk_buff *skb) return pn544_hci_i2c_write(client, skb->data, skb->len); } -static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, u32 protocols) +static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, + u32 im_protocols, u32 tm_protocols) { struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); u8 phases = 0; @@ -584,7 +585,8 @@ static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, u32 protocols) u8 duration[2]; u8 activated; - pr_info(DRIVER_DESC ": %s protocols = %d\n", __func__, protocols); + pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n", + __func__, im_protocols, tm_protocols); r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, NFC_HCI_EVT_END_OPERATION, NULL, 0); @@ -604,10 +606,10 @@ static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, u32 protocols) if (r < 0) return r; - if (protocols & (NFC_PROTO_ISO14443_MASK | NFC_PROTO_MIFARE_MASK | + if (im_protocols & (NFC_PROTO_ISO14443_MASK | NFC_PROTO_MIFARE_MASK | NFC_PROTO_JEWEL_MASK)) phases |= 1; /* Type A */ - if (protocols & NFC_PROTO_FELICA_MASK) { + if (im_protocols & NFC_PROTO_FELICA_MASK) { phases |= (1 << 2); /* Type F 212 */ phases |= (1 << 3); /* Type F 424 */ } diff --git a/include/linux/nfc.h b/include/linux/nfc.h index 0ae9b5857c83..548715881fb0 100644 --- a/include/linux/nfc.h +++ b/include/linux/nfc.h @@ -94,6 +94,8 @@ enum nfc_commands { * @NFC_ATTR_TARGET_SENSF_RES: NFC-F targets extra information, max 18 bytes * @NFC_ATTR_COMM_MODE: Passive or active mode * @NFC_ATTR_RF_MODE: Initiator or target + * @NFC_ATTR_IM_PROTOCOLS: Initiator mode protocols to poll for + * @NFC_ATTR_TM_PROTOCOLS: Target mode protocols to listen for */ enum nfc_attrs { NFC_ATTR_UNSPEC, @@ -109,6 +111,8 @@ enum nfc_attrs { NFC_ATTR_COMM_MODE, NFC_ATTR_RF_MODE, NFC_ATTR_DEVICE_POWERED, + NFC_ATTR_IM_PROTOCOLS, + NFC_ATTR_TM_PROTOCOLS, /* private: internal use only */ __NFC_ATTR_AFTER_LAST }; diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 4467c9460857..e30e6a869714 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -31,7 +31,8 @@ struct nfc_hci_ops { void (*close) (struct nfc_hci_dev *hdev); int (*hci_ready) (struct nfc_hci_dev *hdev); int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb); - int (*start_poll) (struct nfc_hci_dev *hdev, u32 protocols); + int (*start_poll) (struct nfc_hci_dev *hdev, + u32 im_protocols, u32 tm_protocols); int (*target_from_gate) (struct nfc_hci_dev *hdev, u8 gate, struct nfc_target *target); int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate, diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 3116f923f607..97aa0e81aa83 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -53,7 +53,8 @@ struct nfc_target; struct nfc_ops { int (*dev_up)(struct nfc_dev *dev); int (*dev_down)(struct nfc_dev *dev); - int (*start_poll)(struct nfc_dev *dev, u32 protocols); + int (*start_poll)(struct nfc_dev *dev, + u32 im_protocols, u32 tm_protocols); void (*stop_poll)(struct nfc_dev *dev); int (*dep_link_up)(struct nfc_dev *dev, struct nfc_target *target, u8 comm_mode, u8 *gb, size_t gb_len); diff --git a/include/net/nfc/shdlc.h b/include/net/nfc/shdlc.h index ab06afd462da..35e930d2f638 100644 --- a/include/net/nfc/shdlc.h +++ b/include/net/nfc/shdlc.h @@ -27,7 +27,8 @@ struct nfc_shdlc_ops { void (*close) (struct nfc_shdlc *shdlc); int (*hci_ready) (struct nfc_shdlc *shdlc); int (*xmit) (struct nfc_shdlc *shdlc, struct sk_buff *skb); - int (*start_poll) (struct nfc_shdlc *shdlc, u32 protocols); + int (*start_poll) (struct nfc_shdlc *shdlc, + u32 im_protocols, u32 tm_protocols); int (*target_from_gate) (struct nfc_shdlc *shdlc, u8 gate, struct nfc_target *target); int (*complete_target_discovered) (struct nfc_shdlc *shdlc, u8 gate, diff --git a/net/nfc/core.c b/net/nfc/core.c index f5a43f701a9e..c83717bfcb8a 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -121,14 +121,14 @@ error: * The device remains polling for targets until a target is found or * the nfc_stop_poll function is called. */ -int nfc_start_poll(struct nfc_dev *dev, u32 protocols) +int nfc_start_poll(struct nfc_dev *dev, u32 im_protocols, u32 tm_protocols) { int rc; - pr_debug("dev_name=%s protocols=0x%x\n", - dev_name(&dev->dev), protocols); + pr_debug("dev_name %s initiator protocols 0x%x target protocols 0x%x\n", + dev_name(&dev->dev), im_protocols, tm_protocols); - if (!protocols) + if (!im_protocols && !tm_protocols) return -EINVAL; device_lock(&dev->dev); @@ -143,7 +143,7 @@ int nfc_start_poll(struct nfc_dev *dev, u32 protocols) goto error; } - rc = dev->ops->start_poll(dev, protocols); + rc = dev->ops->start_poll(dev, im_protocols, tm_protocols); if (!rc) dev->polling = true; diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index e1a640d2b588..281f3a3bec00 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -481,12 +481,13 @@ static int hci_dev_down(struct nfc_dev *nfc_dev) return 0; } -static int hci_start_poll(struct nfc_dev *nfc_dev, u32 protocols) +static int hci_start_poll(struct nfc_dev *nfc_dev, + u32 im_protocols, u32 tm_protocols) { struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); if (hdev->ops->start_poll) - return hdev->ops->start_poll(hdev, protocols); + return hdev->ops->start_poll(hdev, im_protocols, tm_protocols); else return nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, NFC_HCI_EVT_READER_REQUESTED, NULL, 0); diff --git a/net/nfc/hci/shdlc.c b/net/nfc/hci/shdlc.c index 5665dc6d893a..6b836e6242b7 100644 --- a/net/nfc/hci/shdlc.c +++ b/net/nfc/hci/shdlc.c @@ -765,14 +765,16 @@ static int nfc_shdlc_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb) return 0; } -static int nfc_shdlc_start_poll(struct nfc_hci_dev *hdev, u32 protocols) +static int nfc_shdlc_start_poll(struct nfc_hci_dev *hdev, + u32 im_protocols, u32 tm_protocols) { struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev); pr_debug("\n"); if (shdlc->ops->start_poll) - return shdlc->ops->start_poll(shdlc, protocols); + return shdlc->ops->start_poll(shdlc, + im_protocols, tm_protocols); return 0; } diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index d560e6f13072..0f718982f808 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -387,7 +387,8 @@ static int nci_dev_down(struct nfc_dev *nfc_dev) return nci_close_device(ndev); } -static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols) +static int nci_start_poll(struct nfc_dev *nfc_dev, + __u32 im_protocols, __u32 tm_protocols) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); int rc; @@ -413,11 +414,11 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols) return -EBUSY; } - rc = nci_request(ndev, nci_rf_discover_req, protocols, + rc = nci_request(ndev, nci_rf_discover_req, im_protocols, msecs_to_jiffies(NCI_RF_DISC_TIMEOUT)); if (!rc) - ndev->poll_prots = protocols; + ndev->poll_prots = im_protocols; return rc; } diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 581d419083aa..a18fd56798fc 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -49,6 +49,8 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = { [NFC_ATTR_COMM_MODE] = { .type = NLA_U8 }, [NFC_ATTR_RF_MODE] = { .type = NLA_U8 }, [NFC_ATTR_DEVICE_POWERED] = { .type = NLA_U8 }, + [NFC_ATTR_IM_PROTOCOLS] = { .type = NLA_U32 }, + [NFC_ATTR_TM_PROTOCOLS] = { .type = NLA_U32 }, }; static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target, @@ -519,16 +521,25 @@ static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info) struct nfc_dev *dev; int rc; u32 idx; - u32 protocols; + u32 im_protocols = 0, tm_protocols = 0; pr_debug("Poll start\n"); if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || - !info->attrs[NFC_ATTR_PROTOCOLS]) + ((!info->attrs[NFC_ATTR_IM_PROTOCOLS] && + !info->attrs[NFC_ATTR_PROTOCOLS]) && + !info->attrs[NFC_ATTR_TM_PROTOCOLS])) return -EINVAL; idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); - protocols = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]); + + if (info->attrs[NFC_ATTR_TM_PROTOCOLS]) + tm_protocols = nla_get_u32(info->attrs[NFC_ATTR_TM_PROTOCOLS]); + else if (info->attrs[NFC_ATTR_PROTOCOLS]) + tm_protocols = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]); + + if (info->attrs[NFC_ATTR_IM_PROTOCOLS]) + im_protocols = nla_get_u32(info->attrs[NFC_ATTR_IM_PROTOCOLS]); dev = nfc_get_device(idx); if (!dev) @@ -536,7 +547,7 @@ static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info) mutex_lock(&dev->genl_data.genl_data_mutex); - rc = nfc_start_poll(dev, protocols); + rc = nfc_start_poll(dev, im_protocols, tm_protocols); if (!rc) dev->genl_data.poll_req_pid = info->snd_pid; diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index 3dd4232ae664..7d9708f2a66c 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -158,7 +158,7 @@ int nfc_dev_up(struct nfc_dev *dev); int nfc_dev_down(struct nfc_dev *dev); -int nfc_start_poll(struct nfc_dev *dev, u32 protocols); +int nfc_start_poll(struct nfc_dev *dev, u32 im_protocols, u32 tm_protocols); int nfc_stop_poll(struct nfc_dev *dev); -- cgit v1.2.3-59-g8ed1b From fc40a8c1a06ab7db45da790693dd9802612a055c Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 1 Jun 2012 13:21:13 +0200 Subject: NFC: Add target mode activation netlink event Userspace gets a netlink event upon target mode activation. The LLCP layer is also signaled when we get an ATR_REQ in order to get the remote general bytes. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 27 ++++++++++++++++++++-- include/linux/nfc.h | 7 ++++++ include/net/nfc/nfc.h | 4 ++++ net/nfc/core.c | 35 +++++++++++++++++++++++++++++ net/nfc/netlink.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ net/nfc/nfc.h | 3 +++ 6 files changed, 136 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 605a08a62e45..c6b9bc5ac6e6 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -260,6 +260,10 @@ struct pn533_cmd_jump_dep_response { #define PN533_INIT_TARGET_PASSIVE 0x1 #define PN533_INIT_TARGET_DEP 0x2 +#define PN533_INIT_TARGET_RESP_FRAME_MASK 0x3 +#define PN533_INIT_TARGET_RESP_ACTIVE 0x1 +#define PN533_INIT_TARGET_RESP_DEP 0x4 + struct pn533_cmd_init_target { u8 mode; u8 mifare[6]; @@ -1128,10 +1132,13 @@ static int pn533_init_target_frame(struct pn533_frame *frame, return 0; } +#define ATR_REQ_GB_OFFSET 17 static int pn533_init_target_complete(struct pn533 *dev, void *arg, u8 *params, int params_len) { struct pn533_cmd_init_target_response *resp; + u8 frame, comm_mode = NFC_COMM_PASSIVE, *gb; + size_t gb_len; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); @@ -1143,11 +1150,27 @@ static int pn533_init_target_complete(struct pn533 *dev, void *arg, return params_len; } + if (params_len < ATR_REQ_GB_OFFSET + 1) + return -EINVAL; + resp = (struct pn533_cmd_init_target_response *) params; - nfc_dev_dbg(&dev->interface->dev, "Target mode 0x%x\n", resp->mode); + nfc_dev_dbg(&dev->interface->dev, "Target mode 0x%x param len %d\n", + resp->mode, params_len); - return 0; + frame = resp->mode & PN533_INIT_TARGET_RESP_FRAME_MASK; + if (frame == PN533_INIT_TARGET_RESP_ACTIVE) + comm_mode = NFC_COMM_ACTIVE; + + /* Again, only DEP */ + if ((resp->mode & PN533_INIT_TARGET_RESP_DEP) == 0) + return -EOPNOTSUPP; + + gb = resp->cmd + ATR_REQ_GB_OFFSET; + gb_len = params_len - (ATR_REQ_GB_OFFSET + 1); + + return nfc_tm_activated(dev->nfc_dev, NFC_PROTO_NFC_DEP_MASK, + comm_mode, gb, gb_len); } static int pn533_init_target(struct nfc_dev *nfc_dev, u32 protocols) diff --git a/include/linux/nfc.h b/include/linux/nfc.h index 548715881fb0..d124e9273fcb 100644 --- a/include/linux/nfc.h +++ b/include/linux/nfc.h @@ -56,6 +56,10 @@ * %NFC_ATTR_PROTOCOLS) * @NFC_EVENT_DEVICE_REMOVED: event emitted when a device is removed * (it sends %NFC_ATTR_DEVICE_INDEX) + * @NFC_EVENT_TM_ACTIVATED: event emitted when the adapter is activated in + * target mode. + * @NFC_EVENT_DEVICE_DEACTIVATED: event emitted when the adapter is deactivated + * from target mode. */ enum nfc_commands { NFC_CMD_UNSPEC, @@ -71,6 +75,8 @@ enum nfc_commands { NFC_EVENT_DEVICE_ADDED, NFC_EVENT_DEVICE_REMOVED, NFC_EVENT_TARGET_LOST, + NFC_EVENT_TM_ACTIVATED, + NFC_EVENT_TM_DEACTIVATED, /* private: internal use only */ __NFC_CMD_AFTER_LAST }; @@ -122,6 +128,7 @@ enum nfc_attrs { #define NFC_NFCID1_MAXSIZE 10 #define NFC_SENSB_RES_MAXSIZE 12 #define NFC_SENSF_RES_MAXSIZE 18 +#define NFC_GB_MAXSIZE 48 /* NFC protocols */ #define NFC_PROTO_JEWEL 1 diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 97aa0e81aa83..41573b4bd78a 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -198,4 +198,8 @@ int nfc_target_lost(struct nfc_dev *dev, u32 target_idx); int nfc_dep_link_is_up(struct nfc_dev *dev, u32 target_idx, u8 comm_mode, u8 rf_mode); +int nfc_tm_activated(struct nfc_dev *dev, u32 protocol, u8 comm_mode, + u8 *gb, size_t gb_len); +int nfc_tm_deactivated(struct nfc_dev *dev); + #endif /* __NET_NFC_H */ diff --git a/net/nfc/core.c b/net/nfc/core.c index c83717bfcb8a..17f147430b7c 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -455,6 +455,41 @@ u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len) } EXPORT_SYMBOL(nfc_get_local_general_bytes); +int nfc_tm_activated(struct nfc_dev *dev, u32 protocol, u8 comm_mode, + u8 *gb, size_t gb_len) +{ + int rc; + + device_lock(&dev->dev); + + dev->polling = false; + + if (gb != NULL) { + rc = nfc_set_remote_general_bytes(dev, gb, gb_len); + if (rc < 0) + goto out; + } + + if (protocol == NFC_PROTO_NFC_DEP_MASK) + nfc_dep_link_is_up(dev, 0, comm_mode, NFC_RF_TARGET); + + rc = nfc_genl_tm_activated(dev, protocol); + +out: + device_unlock(&dev->dev); + + return rc; +} +EXPORT_SYMBOL(nfc_tm_activated); + +int nfc_tm_deactivated(struct nfc_dev *dev) +{ + dev->dep_link_up = false; + + return nfc_genl_tm_deactivated(dev); +} +EXPORT_SYMBOL(nfc_tm_deactivated); + /** * nfc_alloc_send_skb - allocate a skb for data exchange responses * diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index a18fd56798fc..21eaa9b5c6bf 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -221,6 +221,68 @@ free_msg: return -EMSGSIZE; } +int nfc_genl_tm_activated(struct nfc_dev *dev, u32 protocol) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, + NFC_EVENT_TM_ACTIVATED); + if (!hdr) + goto free_msg; + + if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx)) + goto nla_put_failure; + if (nla_put_u32(msg, NFC_ATTR_TM_PROTOCOLS, protocol)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + + genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL); + + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); +free_msg: + nlmsg_free(msg); + return -EMSGSIZE; +} + +int nfc_genl_tm_deactivated(struct nfc_dev *dev) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, + NFC_EVENT_TM_DEACTIVATED); + if (!hdr) + goto free_msg; + + if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + + genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL); + + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); +free_msg: + nlmsg_free(msg); + return -EMSGSIZE; +} + int nfc_genl_device_added(struct nfc_dev *dev) { struct sk_buff *msg; diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index 7d9708f2a66c..cd9fcbe57464 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -128,6 +128,9 @@ int nfc_genl_dep_link_up_event(struct nfc_dev *dev, u32 target_idx, u8 comm_mode, u8 rf_mode); int nfc_genl_dep_link_down_event(struct nfc_dev *dev); +int nfc_genl_tm_activated(struct nfc_dev *dev, u32 protocol); +int nfc_genl_tm_deactivated(struct nfc_dev *dev); + struct nfc_dev *nfc_get_device(unsigned int idx); static inline void nfc_put_device(struct nfc_dev *dev) -- cgit v1.2.3-59-g8ed1b From f212ad5e993e7efb996fc8ce94a5de8f0bd06d41 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 31 May 2012 00:02:26 +0200 Subject: NFC: Set the NFC device RF mode appropriately Signed-off-by: Samuel Ortiz --- include/linux/nfc.h | 1 + include/net/nfc/nfc.h | 2 +- net/nfc/core.c | 14 ++++++++++---- 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nfc.h b/include/linux/nfc.h index d124e9273fcb..f4e6dd915b1c 100644 --- a/include/linux/nfc.h +++ b/include/linux/nfc.h @@ -146,6 +146,7 @@ enum nfc_attrs { /* NFC RF modes */ #define NFC_RF_INITIATOR 0 #define NFC_RF_TARGET 1 +#define NFC_RF_NONE 2 /* NFC protocols masks used in bitsets */ #define NFC_PROTO_JEWEL_MASK (1 << NFC_PROTO_JEWEL) diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 41573b4bd78a..a6a7b49a3e2d 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -100,10 +100,10 @@ struct nfc_dev { int targets_generation; struct device dev; bool dev_up; + u8 rf_mode; bool polling; struct nfc_target *active_target; bool dep_link_up; - u32 dep_rf_mode; struct nfc_genl_data genl_data; u32 supported_protocols; diff --git a/net/nfc/core.c b/net/nfc/core.c index 17f147430b7c..722a0c76c669 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -144,8 +144,10 @@ int nfc_start_poll(struct nfc_dev *dev, u32 im_protocols, u32 tm_protocols) } rc = dev->ops->start_poll(dev, im_protocols, tm_protocols); - if (!rc) + if (!rc) { dev->polling = true; + dev->rf_mode = NFC_RF_NONE; + } error: device_unlock(&dev->dev); @@ -235,8 +237,10 @@ int nfc_dep_link_up(struct nfc_dev *dev, int target_index, u8 comm_mode) } rc = dev->ops->dep_link_up(dev, target, comm_mode, gb, gb_len); - if (!rc) + if (!rc) { dev->active_target = target; + dev->rf_mode = NFC_RF_INITIATOR; + } error: device_unlock(&dev->dev); @@ -264,7 +268,7 @@ int nfc_dep_link_down(struct nfc_dev *dev) goto error; } - if (dev->dep_rf_mode == NFC_RF_TARGET) { + if (dev->rf_mode == NFC_RF_TARGET) { rc = -EOPNOTSUPP; goto error; } @@ -286,7 +290,6 @@ int nfc_dep_link_is_up(struct nfc_dev *dev, u32 target_idx, u8 comm_mode, u8 rf_mode) { dev->dep_link_up = true; - dev->dep_rf_mode = rf_mode; nfc_llcp_mac_is_up(dev, target_idx, comm_mode, rf_mode); @@ -330,6 +333,7 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol) rc = dev->ops->activate_target(dev, target, protocol); if (!rc) { dev->active_target = target; + dev->rf_mode = NFC_RF_INITIATOR; if (dev->ops->check_presence) mod_timer(&dev->check_pres_timer, jiffies + @@ -470,6 +474,8 @@ int nfc_tm_activated(struct nfc_dev *dev, u32 protocol, u8 comm_mode, goto out; } + dev->rf_mode = NFC_RF_TARGET; + if (protocol == NFC_PROTO_NFC_DEP_MASK) nfc_dep_link_is_up(dev, 0, comm_mode, NFC_RF_TARGET); -- cgit v1.2.3-59-g8ed1b From c1a7b32a14138f908df52d7c53b5ce3415ec6b50 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Sun, 20 May 2012 13:15:07 +0900 Subject: KVM: Avoid wasting pages for small lpage_info arrays lpage_info is created for each large level even when the memory slot is not for RAM. This means that when we add one slot for a PCI device, we end up allocating at least KVM_NR_PAGE_SIZES - 1 pages by vmalloc(). To make things worse, there is an increasing number of devices which would result in more pages being wasted this way. This patch mitigates this problem by using kvm_kvzalloc(). Signed-off-by: Takuya Yoshikawa Signed-off-by: Avi Kivity --- arch/x86/kvm/x86.c | 4 ++-- include/linux/kvm_host.h | 3 +++ virt/kvm/kvm_main.c | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index be6d54929fa7..f12a52408cda 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6304,7 +6304,7 @@ void kvm_arch_free_memslot(struct kvm_memory_slot *free, for (i = 0; i < KVM_NR_PAGE_SIZES - 1; ++i) { if (!dont || free->arch.lpage_info[i] != dont->arch.lpage_info[i]) { - vfree(free->arch.lpage_info[i]); + kvm_kvfree(free->arch.lpage_info[i]); free->arch.lpage_info[i] = NULL; } } @@ -6323,7 +6323,7 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) slot->base_gfn, level) + 1; slot->arch.lpage_info[i] = - vzalloc(lpages * sizeof(*slot->arch.lpage_info[i])); + kvm_kvzalloc(lpages * sizeof(*slot->arch.lpage_info[i])); if (!slot->arch.lpage_info[i]) goto out_free; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index c4464356b35b..19b83f6efa49 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -535,6 +535,9 @@ int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu); void kvm_free_physmem(struct kvm *kvm); +void *kvm_kvzalloc(unsigned long size); +void kvm_kvfree(const void *addr); + #ifndef __KVM_HAVE_ARCH_VM_ALLOC static inline struct kvm *kvm_arch_alloc_vm(void) { diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 1148c96a4817..02cb440f802d 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -520,7 +520,7 @@ out_err_nodisable: * Avoid using vmalloc for a small buffer. * Should not be used when the size is statically known. */ -static void *kvm_kvzalloc(unsigned long size) +void *kvm_kvzalloc(unsigned long size) { if (size > PAGE_SIZE) return vzalloc(size); @@ -528,7 +528,7 @@ static void *kvm_kvzalloc(unsigned long size) return kzalloc(size, GFP_KERNEL); } -static void kvm_kvfree(const void *addr) +void kvm_kvfree(const void *addr) { if (is_vmalloc_addr(addr)) vfree(addr); -- cgit v1.2.3-59-g8ed1b From a43fd50dc99a5f65505f174eca5a421707d73b4c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 5 Jun 2012 14:34:03 +0100 Subject: regmap: Implement support for wake IRQs Allow chips to provide a bank of registers for controlling the wake state in a similar fashion to the masks and propagate the wake count to the parent interrupt controller. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 47 ++++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 2 ++ 2 files changed, 49 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index b74e14c4dff4..b480b529f020 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -29,9 +29,13 @@ struct regmap_irq_chip_data { int irq_base; struct irq_domain *domain; + int irq; + int wake_count; + unsigned int *status_buf; unsigned int *mask_buf; unsigned int *mask_buf_def; + unsigned int *wake_buf; unsigned int irq_reg_stride; }; @@ -71,6 +75,16 @@ static void regmap_irq_sync_unlock(struct irq_data *data) d->chip->mask_base + (i * map->reg_stride)); } + /* If we've changed our wakeup count propagate it to the parent */ + if (d->wake_count < 0) + for (i = d->wake_count; i < 0; i++) + irq_set_irq_wake(d->irq, 0); + else if (d->wake_count > 0) + for (i = 0; i < d->wake_count; i++) + irq_set_irq_wake(d->irq, 1); + + d->wake_count = 0; + mutex_unlock(&d->lock); } @@ -92,12 +106,35 @@ static void regmap_irq_disable(struct irq_data *data) d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; } +static int regmap_irq_set_wake(struct irq_data *data, unsigned int on) +{ + struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); + struct regmap *map = d->map; + const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); + + if (!d->chip->wake_base) + return -EINVAL; + + if (on) { + d->wake_buf[irq_data->reg_offset / map->reg_stride] + &= ~irq_data->mask; + d->wake_count++; + } else { + d->wake_buf[irq_data->reg_offset / map->reg_stride] + |= irq_data->mask; + d->wake_count--; + } + + return 0; +} + static struct irq_chip regmap_irq_chip = { .name = "regmap", .irq_bus_lock = regmap_irq_lock, .irq_bus_sync_unlock = regmap_irq_sync_unlock, .irq_disable = regmap_irq_disable, .irq_enable = regmap_irq_enable, + .irq_set_wake = regmap_irq_set_wake, }; static irqreturn_t regmap_irq_thread(int irq, void *d) @@ -240,6 +277,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, if (!d->mask_buf_def) goto err_alloc; + if (chip->wake_base) { + d->wake_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, + GFP_KERNEL); + if (!d->wake_buf) + goto err_alloc; + } + + d->irq = irq; d->map = map; d->chip = chip; d->irq_base = irq_base; @@ -294,6 +339,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, err_domain: /* Should really dispose of the domain but... */ err_alloc: + kfree(d->wake_buf); kfree(d->mask_buf_def); kfree(d->mask_buf); kfree(d->status_buf); @@ -315,6 +361,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) free_irq(irq, d); /* We should unmap the domain but... */ + kfree(d->wake_buf); kfree(d->mask_buf_def); kfree(d->mask_buf); kfree(d->status_buf); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 56af22ec9aba..58ec0cba0ae6 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -219,6 +219,7 @@ struct regmap_irq { * @status_base: Base status register address. * @mask_base: Base mask register address. * @ack_base: Base ack address. If zero then the chip is clear on read. + * @wake_base: Base address for wake enables. If zero unsupported. * @irq_reg_stride: Stride to use for chips where registers are not contiguous. * * @num_regs: Number of registers in each control bank. @@ -232,6 +233,7 @@ struct regmap_irq_chip { unsigned int status_base; unsigned int mask_base; unsigned int ack_base; + unsigned int wake_base; unsigned int irq_reg_stride; int num_regs; -- cgit v1.2.3-59-g8ed1b From 8feb8e896d77439146d2e2ab3d0ab55bb5baf5fc Mon Sep 17 00:00:00 2001 From: Yong Zhang Date: Tue, 29 May 2012 15:16:05 +0800 Subject: smp: Remove ipi_call_lock[_irq]()/ipi_call_unlock[_irq]() There is no user of those APIs anymore, just remove it. Signed-off-by: Yong Zhang Cc: ralf@linux-mips.org Cc: sshtylyov@mvista.com Cc: david.daney@cavium.com Cc: nikunj@linux.vnet.ibm.com Cc: paulmck@linux.vnet.ibm.com Cc: axboe@kernel.dk Cc: Andrew Morton Link: http://lkml.kernel.org/r/1338275765-3217-11-git-send-email-yong.zhang0@gmail.com Acked-by: Srivatsa S. Bhat Acked-by: Peter Zijlstra Signed-off-by: Thomas Gleixner --- include/linux/smp.h | 4 ---- kernel/smp.c | 20 -------------------- 2 files changed, 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/smp.h b/include/linux/smp.h index 717fb746c9a8..a34d4f15430d 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -90,10 +90,6 @@ void kick_all_cpus_sync(void); void __init call_function_init(void); void generic_smp_call_function_single_interrupt(void); void generic_smp_call_function_interrupt(void); -void ipi_call_lock(void); -void ipi_call_unlock(void); -void ipi_call_lock_irq(void); -void ipi_call_unlock_irq(void); #else static inline void call_function_init(void) { } #endif diff --git a/kernel/smp.c b/kernel/smp.c index d0ae5b24875e..29dd40a9f2f4 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -581,26 +581,6 @@ int smp_call_function(smp_call_func_t func, void *info, int wait) return 0; } EXPORT_SYMBOL(smp_call_function); - -void ipi_call_lock(void) -{ - raw_spin_lock(&call_function.lock); -} - -void ipi_call_unlock(void) -{ - raw_spin_unlock(&call_function.lock); -} - -void ipi_call_lock_irq(void) -{ - raw_spin_lock_irq(&call_function.lock); -} - -void ipi_call_unlock_irq(void) -{ - raw_spin_unlock_irq(&call_function.lock); -} #endif /* USE_GENERIC_SMP_HELPERS */ /* Setup configured maximum number of CPUs to activate */ -- cgit v1.2.3-59-g8ed1b From 43cc7e86f3200b094e2960b732623aeec00b482d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 15 May 2012 17:26:16 +0200 Subject: smp: Remove num_booting_cpus() No users. Signed-off-by: Thomas Gleixner Cc: Srivatsa S. Bhat Cc: Rusty Russell --- arch/m32r/include/asm/smp.h | 5 ----- arch/x86/include/asm/smp.h | 5 ----- include/linux/smp.h | 1 - 3 files changed, 11 deletions(-) (limited to 'include/linux') diff --git a/arch/m32r/include/asm/smp.h b/arch/m32r/include/asm/smp.h index cf7829a61551..c689b828dfe2 100644 --- a/arch/m32r/include/asm/smp.h +++ b/arch/m32r/include/asm/smp.h @@ -79,11 +79,6 @@ static __inline__ int cpu_number_map(int cpu) return cpu; } -static __inline__ unsigned int num_booting_cpus(void) -{ - return cpumask_weight(&cpu_callout_map); -} - extern void smp_send_timer(void); extern unsigned long send_IPI_mask_phys(const cpumask_t*, int, int); diff --git a/arch/x86/include/asm/smp.h b/arch/x86/include/asm/smp.h index f48394513c37..2ffa95dc2333 100644 --- a/arch/x86/include/asm/smp.h +++ b/arch/x86/include/asm/smp.h @@ -169,11 +169,6 @@ void x86_idle_thread_init(unsigned int cpu, struct task_struct *idle); void smp_store_cpu_info(int id); #define cpu_physical_id(cpu) per_cpu(x86_cpu_to_apicid, cpu) -/* We don't mark CPUs online until __cpu_up(), so we need another measure */ -static inline int num_booting_cpus(void) -{ - return cpumask_weight(cpu_callout_mask); -} #else /* !CONFIG_SMP */ #define wbinvd_on_cpu(cpu) wbinvd() static inline int wbinvd_on_all_cpus(void) diff --git a/include/linux/smp.h b/include/linux/smp.h index a34d4f15430d..dd6f06be3c9f 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -177,7 +177,6 @@ static inline int up_smp_call_function(smp_call_func_t func, void *info) } while (0) static inline void smp_send_reschedule(int cpu) { } -#define num_booting_cpus() 1 #define smp_prepare_boot_cpu() do {} while (0) #define smp_call_function_many(mask, func, info, wait) \ (up_smp_call_function(func, info)) -- cgit v1.2.3-59-g8ed1b From 5dad021dee7c3c601a9e17d86139e586c05c2ee2 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 15 May 2012 14:50:58 +0300 Subject: nl80211: add new rssi event to indicate beacon loss Tell userspace about beacon loss event. This event doesn't replace the deauth/disassoc that might come if the AP is not available. The driver can send this event in order to hint userspace what might follow (which in turn can use it as roaming trigger). Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- include/linux/nl80211.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index a6959f72745e..6930dddad18a 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -2534,10 +2534,14 @@ enum nl80211_attr_cqm { * configured threshold * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the * configured threshold + * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: The device experienced beacon loss. + * (Note that deauth/disassoc will still follow if the AP is not + * available. This event might get used as roaming event, etc.) */ enum nl80211_cqm_rssi_threshold_event { NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + NL80211_CQM_RSSI_BEACON_LOSS_EVENT, }; -- cgit v1.2.3-59-g8ed1b From aa430da41019c1694f6a8e3b8bef1d12ed52b0ad Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 May 2012 23:50:18 +0200 Subject: cfg80211: provide channel to start_ap function Instead of setting the channel first and then starting the AP, let cfg80211 store the channel and provide it as one of the AP settings. This means that now you have to set the channel before you can start an AP interface, but since hostapd/wpa_supplicant always do that we're OK with this change. Alternatively, it's now possible to give the channel as an attribute to the start-ap nl80211 command, overriding any preset channel. Cc: Kalle Valo Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 36 ++------------------ drivers/net/wireless/ath/ath6kl/core.h | 3 -- drivers/net/wireless/ath/ath6kl/main.c | 1 - include/linux/nl80211.h | 2 ++ include/net/cfg80211.h | 12 ++++++- net/mac80211/cfg.c | 5 +++ net/wireless/nl80211.c | 53 ++++++++++++++++++++++++++++-- 7 files changed, 72 insertions(+), 40 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index b869a358ce43..f27e9732951d 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2585,35 +2585,6 @@ static int ath6kl_set_ies(struct ath6kl_vif *vif, return 0; } -static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) -{ - struct ath6kl_vif *vif; - - /* - * 'dev' could be NULL if a channel change is required for the hardware - * device itself, instead of a particular VIF. - * - * FIXME: To be handled properly when monitor mode is supported. - */ - if (!dev) - return -EBUSY; - - vif = netdev_priv(dev); - - if (!ath6kl_cfg80211_ready(vif)) - return -EIO; - - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n", - __func__, chan->center_freq, chan->hw_value); - vif->next_chan = chan->center_freq; - vif->next_ch_type = channel_type; - vif->next_ch_band = chan->band; - - return 0; -} - static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon, u8 *rsn_capab) { @@ -2791,7 +2762,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, p.ssid_len = vif->ssid_len; memcpy(p.ssid, vif->ssid, vif->ssid_len); p.dot11_auth_mode = vif->dot11_auth_mode; - p.ch = cpu_to_le16(vif->next_chan); + p.ch = cpu_to_le16(info->channel->center_freq); /* Enable uAPSD support by default */ res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true); @@ -2815,8 +2786,8 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, return res; } - if (ath6kl_set_htcap(vif, vif->next_ch_band, - vif->next_ch_type != NL80211_CHAN_NO_HT)) + if (ath6kl_set_htcap(vif, info->channel->band, + info->channel_type != NL80211_CHAN_NO_HT)) return -EIO; /* @@ -3271,7 +3242,6 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .suspend = __ath6kl_cfg80211_suspend, .resume = __ath6kl_cfg80211_resume, #endif - .set_channel = ath6kl_set_channel, .start_ap = ath6kl_start_ap, .change_beacon = ath6kl_change_beacon, .stop_ap = ath6kl_stop_ap, diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 4d9c6f142698..8443b2a4133e 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -553,9 +553,6 @@ struct ath6kl_vif { u32 last_cancel_roc_id; u32 send_action_id; bool probe_req_report; - u16 next_chan; - enum nl80211_channel_type next_ch_type; - enum ieee80211_band next_ch_band; u16 assoc_bss_beacon_int; u16 listen_intvl_t; u16 bmiss_time_t; diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index e5524470529c..b836f2795114 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -598,7 +598,6 @@ static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel) struct ath6kl *ar = vif->ar; - vif->next_chan = channel; vif->profile.ch = cpu_to_le16(channel); switch (vif->nw_type) { diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 6930dddad18a..85e5037a218d 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -170,6 +170,8 @@ * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, * %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT. + * The channel to use can be set on the interface or be given using the + * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_WIPHY_CHANNEL_TYPE attrs. * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a8496f4ff5f1..a54fb895f613 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -404,6 +404,8 @@ struct cfg80211_beacon_data { * * Used to configure an AP interface. * + * @channel: the channel to start the AP on + * @channel_type: the channel type to use * @beacon: beacon data * @beacon_interval: beacon interval * @dtim_period: DTIM period @@ -417,6 +419,9 @@ struct cfg80211_beacon_data { * @inactivity_timeout: time in seconds to determine station's inactivity. */ struct cfg80211_ap_settings { + struct ieee80211_channel *channel; + enum nl80211_channel_type channel_type; + struct cfg80211_beacon_data beacon; int beacon_interval, dtim_period; @@ -2263,7 +2268,10 @@ struct cfg80211_cached_keys; * @netdev: (private) Used to reference back to the netdev * @current_bss: (private) Used by the internal configuration code * @channel: (private) Used by the internal configuration code to track - * user-set AP, monitor and WDS channels for wireless extensions + * the user-set AP, monitor and WDS channel + * @preset_chan: (private) Used by the internal configuration code to + * track the channel to be used for AP later + * @preset_chantype: (private) the corresponding channel type * @bssid: (private) Used by the internal configuration code * @ssid: (private) Used by the internal configuration code * @ssid_len: (private) Used by the internal configuration code @@ -2314,6 +2322,8 @@ struct wireless_dev { struct cfg80211_internal_bss *current_bss; /* associated / joined */ struct ieee80211_channel *channel; + struct ieee80211_channel *preset_chan; + enum nl80211_channel_type preset_chantype; bool ps; int ps_timeout; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 9aab849fd6cf..8e9d525c4653 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -823,6 +823,11 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, if (old) return -EALREADY; + err = ieee80211_set_channel(wiphy, dev, params->channel, + params->channel_type); + if (err) + return err; + /* * Apply control port protocol, this allows us to * not encrypt dynamic WEP control frames. diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 206465dc0cab..74f4a8f93935 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -921,7 +921,11 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS)) goto nla_put_failure; } - CMD(set_channel, SET_CHANNEL); + if (dev->ops->set_channel || dev->ops->start_ap) { + i++; + if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL)) + goto nla_put_failure; + } CMD(set_wds_peer, SET_WDS_PEER); if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { CMD(tdls_mgmt, TDLS_MGMT); @@ -1170,6 +1174,9 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) * Monitors are special as they are normally slaved to * whatever else is going on, so they behave as though * you tried setting the wiphy channel itself. + * + * For AP/GO modes, it's only for compatibility, you can + * also give the channel to the start-AP command. */ return !wdev || wdev->iftype == NL80211_IFTYPE_AP || @@ -1204,6 +1211,7 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct genl_info *info) { + struct ieee80211_channel *channel; enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; u32 freq; int result; @@ -1221,7 +1229,25 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); mutex_lock(&rdev->devlist_mtx); - if (wdev) { + if (wdev) switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + if (wdev->beacon_interval) { + result = -EBUSY; + break; + } + channel = rdev_freq_to_chan(rdev, freq, channel_type); + if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, + channel, + channel_type)) { + result = -EINVAL; + break; + } + wdev->preset_chan = channel; + wdev->preset_chantype = channel_type; + result = 0; + break; + default: wdev_lock(wdev); result = cfg80211_set_freq(rdev, wdev, freq, channel_type); wdev_unlock(wdev); @@ -2299,6 +2325,29 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]); } + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { + enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; + + if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && + !nl80211_valid_channel_type(info, &channel_type)) + return -EINVAL; + + params.channel = rdev_freq_to_chan(rdev, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), + channel_type); + if (!params.channel) + return -EINVAL; + params.channel_type = channel_type; + } else if (wdev->preset_chan) { + params.channel = wdev->preset_chan; + params.channel_type = wdev->preset_chantype; + } else + return -EINVAL; + + if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, params.channel, + params.channel_type)) + return -EINVAL; + err = rdev->ops->start_ap(&rdev->wiphy, dev, ¶ms); if (!err) wdev->beacon_interval = params.beacon_interval; -- cgit v1.2.3-59-g8ed1b From 23d0bb834e264f38335f19fe601564b8422431e7 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 28 May 2012 15:26:56 +0100 Subject: GFS2: Add "top dir" flag support This patch adds support for the "top dir" flag. Currently this is unused but a subsequent patch is planned which will add support for the Orlov allocation policy when allocating subdirectories in a parent with this flag set. In order to ensure backward compatible behaviour, mkfs.gfs2 does not currently tag the root directory with this flag, it must always be set manually. Signed-off-by: Steven Whitehouse --- fs/gfs2/file.c | 4 ++++ include/linux/gfs2_ondisk.h | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 26e2905070ed..6fbf3cbd974d 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -142,6 +142,7 @@ static const u32 fsflags_to_gfs2[32] = { [7] = GFS2_DIF_NOATIME, [12] = GFS2_DIF_EXHASH, [14] = GFS2_DIF_INHERIT_JDATA, + [17] = GFS2_DIF_TOPDIR, }; static const u32 gfs2_to_fsflags[32] = { @@ -150,6 +151,7 @@ static const u32 gfs2_to_fsflags[32] = { [gfs2fl_AppendOnly] = FS_APPEND_FL, [gfs2fl_NoAtime] = FS_NOATIME_FL, [gfs2fl_ExHash] = FS_INDEX_FL, + [gfs2fl_TopLevel] = FS_TOPDIR_FL, [gfs2fl_InheritJdata] = FS_JOURNAL_DATA_FL, }; @@ -203,6 +205,7 @@ void gfs2_set_inode_flags(struct inode *inode) GFS2_DIF_NOATIME| \ GFS2_DIF_SYNC| \ GFS2_DIF_SYSTEM| \ + GFS2_DIF_TOPDIR| \ GFS2_DIF_INHERIT_JDATA) /** @@ -298,6 +301,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr) gfsflags = fsflags_cvt(fsflags_to_gfs2, fsflags); if (!S_ISDIR(inode->i_mode)) { + gfsflags &= ~GFS2_DIF_TOPDIR; if (gfsflags & GFS2_DIF_INHERIT_JDATA) gfsflags ^= (GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA); return do_gfs2_set_flags(filp, gfsflags, ~0); diff --git a/include/linux/gfs2_ondisk.h b/include/linux/gfs2_ondisk.h index fa98bdb073b9..e8ccf6ff3b4d 100644 --- a/include/linux/gfs2_ondisk.h +++ b/include/linux/gfs2_ondisk.h @@ -214,6 +214,7 @@ enum { gfs2fl_NoAtime = 7, gfs2fl_Sync = 8, gfs2fl_System = 9, + gfs2fl_TopLevel = 10, gfs2fl_TruncInProg = 29, gfs2fl_InheritDirectio = 30, gfs2fl_InheritJdata = 31, @@ -230,8 +231,9 @@ enum { #define GFS2_DIF_NOATIME 0x00000080 #define GFS2_DIF_SYNC 0x00000100 #define GFS2_DIF_SYSTEM 0x00000200 /* New in gfs2 */ +#define GFS2_DIF_TOPDIR 0x00000400 /* New in gfs2 */ #define GFS2_DIF_TRUNC_IN_PROG 0x20000000 /* New in gfs2 */ -#define GFS2_DIF_INHERIT_DIRECTIO 0x40000000 +#define GFS2_DIF_INHERIT_DIRECTIO 0x40000000 /* only in gfs1 */ #define GFS2_DIF_INHERIT_JDATA 0x80000000 struct gfs2_dinode { -- cgit v1.2.3-59-g8ed1b From a737f256bf14adf94920aa70d150ab4dcd145109 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Sun, 3 Jun 2012 21:17:48 +0300 Subject: KVM: Cleanup the kvm_print functions and introduce pr_XX wrappers Introduces a couple of print functions, which are essentially wrappers around standard printk functions, with a KVM: prefix. Functions introduced or modified are: - kvm_err(fmt, ...) - kvm_info(fmt, ...) - kvm_debug(fmt, ...) - kvm_pr_unimpl(fmt, ...) - pr_unimpl(vcpu, fmt, ...) -> vcpu_unimpl(vcpu, fmt, ...) Signed-off-by: Christoffer Dall Signed-off-by: Avi Kivity --- arch/x86/kvm/svm.c | 6 +++--- arch/x86/kvm/vmx.c | 2 +- arch/x86/kvm/x86.c | 54 ++++++++++++++++++++++++------------------------ include/linux/kvm_host.h | 18 ++++++++++------ 4 files changed, 43 insertions(+), 37 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index f75af406b268..7a418783259d 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -3185,8 +3185,8 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 data) break; case MSR_IA32_DEBUGCTLMSR: if (!boot_cpu_has(X86_FEATURE_LBRV)) { - pr_unimpl(vcpu, "%s: MSR_IA32_DEBUGCTL 0x%llx, nop\n", - __func__, data); + vcpu_unimpl(vcpu, "%s: MSR_IA32_DEBUGCTL 0x%llx, nop\n", + __func__, data); break; } if (data & DEBUGCTL_RESERVED_BITS) @@ -3205,7 +3205,7 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 data) case MSR_VM_CR: return svm_set_vm_cr(vcpu, data); case MSR_VM_IGNNE: - pr_unimpl(vcpu, "unimplemented wrmsr: 0x%x data 0x%llx\n", ecx, data); + vcpu_unimpl(vcpu, "unimplemented wrmsr: 0x%x data 0x%llx\n", ecx, data); break; default: return kvm_set_msr_common(vcpu, ecx, data); diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index f78662ec8677..eeeb4a25aed6 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -4549,7 +4549,7 @@ static int handle_cr(struct kvm_vcpu *vcpu) break; } vcpu->run->exit_reason = 0; - pr_unimpl(vcpu, "unhandled control register: op %d cr %d\n", + vcpu_unimpl(vcpu, "unhandled control register: op %d cr %d\n", (int)(exit_qualification >> 4) & 3, cr); return 0; } diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index f12a52408cda..a01a4241bc6b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1437,8 +1437,8 @@ static int set_msr_hyperv_pw(struct kvm_vcpu *vcpu, u32 msr, u64 data) break; } default: - pr_unimpl(vcpu, "HYPER-V unimplemented wrmsr: 0x%x " - "data 0x%llx\n", msr, data); + vcpu_unimpl(vcpu, "HYPER-V unimplemented wrmsr: 0x%x " + "data 0x%llx\n", msr, data); return 1; } return 0; @@ -1470,8 +1470,8 @@ static int set_msr_hyperv(struct kvm_vcpu *vcpu, u32 msr, u64 data) case HV_X64_MSR_TPR: return kvm_hv_vapic_msr_write(vcpu, APIC_TASKPRI, data); default: - pr_unimpl(vcpu, "HYPER-V unimplemented wrmsr: 0x%x " - "data 0x%llx\n", msr, data); + vcpu_unimpl(vcpu, "HYPER-V unimplemented wrmsr: 0x%x " + "data 0x%llx\n", msr, data); return 1; } @@ -1551,15 +1551,15 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) data &= ~(u64)0x100; /* ignore ignne emulation enable */ data &= ~(u64)0x8; /* ignore TLB cache disable */ if (data != 0) { - pr_unimpl(vcpu, "unimplemented HWCR wrmsr: 0x%llx\n", - data); + vcpu_unimpl(vcpu, "unimplemented HWCR wrmsr: 0x%llx\n", + data); return 1; } break; case MSR_FAM10H_MMIO_CONF_BASE: if (data != 0) { - pr_unimpl(vcpu, "unimplemented MMIO_CONF_BASE wrmsr: " - "0x%llx\n", data); + vcpu_unimpl(vcpu, "unimplemented MMIO_CONF_BASE wrmsr: " + "0x%llx\n", data); return 1; } break; @@ -1574,8 +1574,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) thus reserved and should throw a #GP */ return 1; } - pr_unimpl(vcpu, "%s: MSR_IA32_DEBUGCTLMSR 0x%llx, nop\n", - __func__, data); + vcpu_unimpl(vcpu, "%s: MSR_IA32_DEBUGCTLMSR 0x%llx, nop\n", + __func__, data); break; case MSR_IA32_UCODE_REV: case MSR_IA32_UCODE_WRITE: @@ -1671,8 +1671,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) case MSR_K7_EVNTSEL2: case MSR_K7_EVNTSEL3: if (data != 0) - pr_unimpl(vcpu, "unimplemented perfctr wrmsr: " - "0x%x data 0x%llx\n", msr, data); + vcpu_unimpl(vcpu, "unimplemented perfctr wrmsr: " + "0x%x data 0x%llx\n", msr, data); break; /* at least RHEL 4 unconditionally writes to the perfctr registers, * so we ignore writes to make it happy. @@ -1681,8 +1681,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) case MSR_K7_PERFCTR1: case MSR_K7_PERFCTR2: case MSR_K7_PERFCTR3: - pr_unimpl(vcpu, "unimplemented perfctr wrmsr: " - "0x%x data 0x%llx\n", msr, data); + vcpu_unimpl(vcpu, "unimplemented perfctr wrmsr: " + "0x%x data 0x%llx\n", msr, data); break; case MSR_P6_PERFCTR0: case MSR_P6_PERFCTR1: @@ -1693,8 +1693,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) return kvm_pmu_set_msr(vcpu, msr, data); if (pr || data != 0) - pr_unimpl(vcpu, "disabled perfctr wrmsr: " - "0x%x data 0x%llx\n", msr, data); + vcpu_unimpl(vcpu, "disabled perfctr wrmsr: " + "0x%x data 0x%llx\n", msr, data); break; case MSR_K7_CLK_CTL: /* @@ -1720,7 +1720,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) /* Drop writes to this legacy MSR -- see rdmsr * counterpart for further detail. */ - pr_unimpl(vcpu, "ignored wrmsr: 0x%x data %llx\n", msr, data); + vcpu_unimpl(vcpu, "ignored wrmsr: 0x%x data %llx\n", msr, data); break; case MSR_AMD64_OSVW_ID_LENGTH: if (!guest_cpuid_has_osvw(vcpu)) @@ -1738,12 +1738,12 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) if (kvm_pmu_msr(vcpu, msr)) return kvm_pmu_set_msr(vcpu, msr, data); if (!ignore_msrs) { - pr_unimpl(vcpu, "unhandled wrmsr: 0x%x data %llx\n", - msr, data); + vcpu_unimpl(vcpu, "unhandled wrmsr: 0x%x data %llx\n", + msr, data); return 1; } else { - pr_unimpl(vcpu, "ignored wrmsr: 0x%x data %llx\n", - msr, data); + vcpu_unimpl(vcpu, "ignored wrmsr: 0x%x data %llx\n", + msr, data); break; } } @@ -1846,7 +1846,7 @@ static int get_msr_hyperv_pw(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) data = kvm->arch.hv_hypercall; break; default: - pr_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr); + vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr); return 1; } @@ -1877,7 +1877,7 @@ static int get_msr_hyperv(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) data = vcpu->arch.hv_vapic; break; default: - pr_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr); + vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr); return 1; } *pdata = data; @@ -2030,10 +2030,10 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) if (kvm_pmu_msr(vcpu, msr)) return kvm_pmu_get_msr(vcpu, msr, pdata); if (!ignore_msrs) { - pr_unimpl(vcpu, "unhandled rdmsr: 0x%x\n", msr); + vcpu_unimpl(vcpu, "unhandled rdmsr: 0x%x\n", msr); return 1; } else { - pr_unimpl(vcpu, "ignored rdmsr: 0x%x\n", msr); + vcpu_unimpl(vcpu, "ignored rdmsr: 0x%x\n", msr); data = 0; } break; @@ -4116,7 +4116,7 @@ static unsigned long emulator_get_cr(struct x86_emulate_ctxt *ctxt, int cr) value = kvm_get_cr8(vcpu); break; default: - vcpu_printf(vcpu, "%s: unexpected cr %u\n", __func__, cr); + kvm_err("%s: unexpected cr %u\n", __func__, cr); return 0; } @@ -4145,7 +4145,7 @@ static int emulator_set_cr(struct x86_emulate_ctxt *ctxt, int cr, ulong val) res = kvm_set_cr8(vcpu, val); break; default: - vcpu_printf(vcpu, "%s: unexpected cr %u\n", __func__, cr); + kvm_err("%s: unexpected cr %u\n", __func__, cr); res = -1; } diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 19b83f6efa49..27ac8a4767fa 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -314,13 +314,19 @@ struct kvm { long tlbs_dirty; }; -/* The guest did something we don't support. */ -#define pr_unimpl(vcpu, fmt, ...) \ - pr_err_ratelimited("kvm: %i: cpu%i " fmt, \ - current->tgid, (vcpu)->vcpu_id , ## __VA_ARGS__) +#define kvm_err(fmt, ...) \ + pr_err("kvm [%i]: " fmt, task_pid_nr(current), ## __VA_ARGS__) +#define kvm_info(fmt, ...) \ + pr_info("kvm [%i]: " fmt, task_pid_nr(current), ## __VA_ARGS__) +#define kvm_debug(fmt, ...) \ + pr_debug("kvm [%i]: " fmt, task_pid_nr(current), ## __VA_ARGS__) +#define kvm_pr_unimpl(fmt, ...) \ + pr_err_ratelimited("kvm [%i]: " fmt, \ + task_tgid_nr(current), ## __VA_ARGS__) -#define kvm_printf(kvm, fmt ...) printk(KERN_DEBUG fmt) -#define vcpu_printf(vcpu, fmt...) kvm_printf(vcpu->kvm, fmt) +/* The guest did something we don't support. */ +#define vcpu_unimpl(vcpu, fmt, ...) \ + kvm_pr_unimpl("vcpu%i " fmt, (vcpu)->vcpu_id, ## __VA_ARGS__) static inline struct kvm_vcpu *kvm_get_vcpu(struct kvm *kvm, int i) { -- cgit v1.2.3-59-g8ed1b From 778b032d96909690c19d84f8d17c13be65ed6f8e Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 29 May 2012 21:30:08 +0200 Subject: uprobes: Kill uprobes_srcu/uprobe_srcu_id Kill the no longer needed uprobes_srcu/uprobe_srcu_id code. It doesn't really work anyway. synchronize_srcu() can only synchronize with the code "inside" the srcu_read_lock/srcu_read_unlock section, while uprobe_pre_sstep_notifier() does srcu_read_lock() _after_ we already hit the breakpoint. I guess this probably works "in practice". synchronize_srcu() is slow and it implies synchronize_sched(), and the probed task enters the non- preemptible section at the start of exception handler. Still this is not right at least in theory, and task->uprobe_srcu_id blows task_struct. Signed-off-by: Oleg Nesterov Acked-by: Srikar Dronamraju Cc: Ananth N Mavinakayanahalli Cc: Anton Arapov Cc: Linus Torvalds Cc: Masami Hiramatsu Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120529193008.GG8057@redhat.com Signed-off-by: Ingo Molnar --- include/linux/sched.h | 1 - kernel/events/uprobes.c | 22 +++------------------- 2 files changed, 3 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 6029d8c54476..6bd19655c1a7 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1569,7 +1569,6 @@ struct task_struct { #endif #ifdef CONFIG_UPROBES struct uprobe_task *utask; - int uprobe_srcu_id; #endif }; diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 1f02e3bbfc1d..8c5e043cd309 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -38,7 +38,6 @@ #define UINSNS_PER_PAGE (PAGE_SIZE/UPROBE_XOL_SLOT_BYTES) #define MAX_UPROBE_XOL_SLOTS UINSNS_PER_PAGE -static struct srcu_struct uprobes_srcu; static struct rb_root uprobes_tree = RB_ROOT; static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */ @@ -738,20 +737,14 @@ remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, loff_t vaddr) } /* - * There could be threads that have hit the breakpoint and are entering the - * notifier code and trying to acquire the uprobes_treelock. The thread - * calling delete_uprobe() that is removing the uprobe from the rb_tree can - * race with these threads and might acquire the uprobes_treelock compared - * to some of the breakpoint hit threads. In such a case, the breakpoint - * hit threads will not find the uprobe. The current unregistering thread - * waits till all other threads have hit a breakpoint, to acquire the - * uprobes_treelock before the uprobe is removed from the rbtree. + * There could be threads that have already hit the breakpoint. They + * will recheck the current insn and restart if find_uprobe() fails. + * See find_active_uprobe(). */ static void delete_uprobe(struct uprobe *uprobe) { unsigned long flags; - synchronize_srcu(&uprobes_srcu); spin_lock_irqsave(&uprobes_treelock, flags); rb_erase(&uprobe->rb_node, &uprobes_tree); spin_unlock_irqrestore(&uprobes_treelock, flags); @@ -1388,9 +1381,6 @@ void uprobe_free_utask(struct task_struct *t) { struct uprobe_task *utask = t->utask; - if (t->uprobe_srcu_id != -1) - srcu_read_unlock_raw(&uprobes_srcu, t->uprobe_srcu_id); - if (!utask) return; @@ -1408,7 +1398,6 @@ void uprobe_free_utask(struct task_struct *t) void uprobe_copy_process(struct task_struct *t) { t->utask = NULL; - t->uprobe_srcu_id = -1; } /* @@ -1513,9 +1502,6 @@ static struct uprobe *find_active_uprobe(unsigned long bp_vaddr, int *is_swbp) } else { *is_swbp = -EFAULT; } - - srcu_read_unlock_raw(&uprobes_srcu, current->uprobe_srcu_id); - current->uprobe_srcu_id = -1; up_read(&mm->mmap_sem); return uprobe; @@ -1656,7 +1642,6 @@ int uprobe_pre_sstep_notifier(struct pt_regs *regs) utask->state = UTASK_BP_HIT; set_thread_flag(TIF_UPROBE); - current->uprobe_srcu_id = srcu_read_lock_raw(&uprobes_srcu); return 1; } @@ -1691,7 +1676,6 @@ static int __init init_uprobes(void) mutex_init(&uprobes_mutex[i]); mutex_init(&uprobes_mmap_mutex[i]); } - init_srcu_struct(&uprobes_srcu); return register_die_notifier(&uprobe_exception_nb); } -- cgit v1.2.3-59-g8ed1b From c48b60538c3ba05a7a2713c4791b25405525431b Mon Sep 17 00:00:00 2001 From: Vince Weaver Date: Thu, 1 Mar 2012 17:28:14 -0500 Subject: perf/x86: Use rdpmc() rather than rdmsr() when possible in the kernel The rdpmc instruction is faster than the equivelant rdmsr call, so use it when possible in the kernel. The perfctr kernel patches did this, after extensive testing showed rdpmc to always be faster (One can look in etc/costs in the perfctr-2.6 package to see a historical list of the overhead). I have done some tests on a 3.2 kernel, the kernel module I used was included in the first posting of this patch: rdmsr rdpmc Core2 T9900: 203.9 cycles 30.9 cycles AMD fam0fh: 56.2 cycles 9.8 cycles Atom 6/28/2: 129.7 cycles 50.6 cycles The speedup of using rdpmc is large. [ It's probably possible (and desirable) to do this without requiring a new field in the hw_perf_event structure, but the fixed events make this tricky. ] Signed-off-by: Vince Weaver Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/alpine.DEB.2.00.1203011724030.26934@cl320.eecs.utk.edu Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event.c | 4 +++- include/linux/perf_event.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 43c2017347e7..000a4746c7ce 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -75,7 +75,7 @@ u64 x86_perf_event_update(struct perf_event *event) */ again: prev_raw_count = local64_read(&hwc->prev_count); - rdmsrl(hwc->event_base, new_raw_count); + rdpmcl(hwc->event_base_rdpmc, new_raw_count); if (local64_cmpxchg(&hwc->prev_count, prev_raw_count, new_raw_count) != prev_raw_count) @@ -819,9 +819,11 @@ static inline void x86_assign_hw_event(struct perf_event *event, } else if (hwc->idx >= X86_PMC_IDX_FIXED) { hwc->config_base = MSR_ARCH_PERFMON_FIXED_CTR_CTRL; hwc->event_base = MSR_ARCH_PERFMON_FIXED_CTR0 + (hwc->idx - X86_PMC_IDX_FIXED); + hwc->event_base_rdpmc = (hwc->idx - X86_PMC_IDX_FIXED) | 1<<30; } else { hwc->config_base = x86_pmu_config_addr(hwc->idx); hwc->event_base = x86_pmu_event_addr(hwc->idx); + hwc->event_base_rdpmc = x86_pmu_addr_offset(hwc->idx); } } diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 45db49f64bb4..1ce887abcc5c 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -677,6 +677,7 @@ struct hw_perf_event { u64 last_tag; unsigned long config_base; unsigned long event_base; + int event_base_rdpmc; int idx; int last_cpu; -- cgit v1.2.3-59-g8ed1b From ccaf8c32d5ed08bfb4c45492f8f1c145fd45f4e8 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Thu, 31 May 2012 22:38:22 +0200 Subject: ssb: recognize ARM Cortex M3 I found this core on a BCM4322, a PCI card in the Linksys WRT610N V1. This core is not used by the driver, this patch just makes ssb show the correct name. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/ssb/scan.c | 2 ++ include/linux/ssb/ssb.h | 1 + 2 files changed, 3 insertions(+) (limited to 'include/linux') diff --git a/drivers/ssb/scan.c b/drivers/ssb/scan.c index 266c7c5c86dc..ab4627cf1114 100644 --- a/drivers/ssb/scan.c +++ b/drivers/ssb/scan.c @@ -90,6 +90,8 @@ const char *ssb_core_name(u16 coreid) return "ARM 1176"; case SSB_DEV_ARM_7TDMI: return "ARM 7TDMI"; + case SSB_DEV_ARM_CM3: + return "ARM Cortex M3"; } return "UNKNOWN"; } diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index bc14bd738ade..bb674c02f306 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -243,6 +243,7 @@ struct ssb_bus_ops { #define SSB_DEV_MINI_MACPHY 0x823 #define SSB_DEV_ARM_1176 0x824 #define SSB_DEV_ARM_7TDMI 0x825 +#define SSB_DEV_ARM_CM3 0x82A /* Vendor-ID values */ #define SSB_VENDOR_BROADCOM 0x4243 -- cgit v1.2.3-59-g8ed1b From ebf348fcd088e3ffc6e76f6f349e27d30604865b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 1 Jun 2012 12:50:54 +0200 Subject: cfg80211: validate remain-on-channel time better The remain-on-channel time validation shouldn't depend on the value of HZ, as it does now with the check against jiffies, since then you might use a value that works on one system but not on another. Fix it by checking against a minimum that's fixed. Also add validation of the wait duration for a management frame TX since this also translates into remain-on-channel internally. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 2 ++ net/wireless/nl80211.c | 23 ++++++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 85e5037a218d..970afdf5a605 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1522,6 +1522,8 @@ enum nl80211_attrs { #define NL80211_MAX_NR_CIPHER_SUITES 5 #define NL80211_MAX_NR_AKM_SUITES 2 +#define NL80211_MIN_REMAIN_ON_CHANNEL_TIME 10 + /** * enum nl80211_iftype - (virtual) interface types * diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5e29bd38e7df..7ae54b82291f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5545,18 +5545,18 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); + if (!rdev->ops->remain_on_channel || + !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)) + return -EOPNOTSUPP; + /* - * We should be on that channel for at least one jiffie, - * and more than 5 seconds seems excessive. + * We should be on that channel for at least a minimum amount of + * time (10ms) but no longer than the driver supports. */ - if (!duration || !msecs_to_jiffies(duration) || + if (duration < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || duration > rdev->wiphy.max_remain_on_channel_duration) return -EINVAL; - if (!rdev->ops->remain_on_channel || - !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)) - return -EOPNOTSUPP; - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && !nl80211_valid_channel_type(info, &channel_type)) return -EINVAL; @@ -5827,6 +5827,15 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) return -EINVAL; wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); + + /* + * We should wait on the channel for at least a minimum amount + * of time (10ms) but no longer than the driver supports. + */ + if (wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || + wait > rdev->wiphy.max_remain_on_channel_duration) + return -EINVAL; + } if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { -- cgit v1.2.3-59-g8ed1b From 6be96a5c905178637ec06a5d456a76b2b304fca3 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 6 Jun 2012 19:12:30 -0700 Subject: cgroup: remove hierarchy_mutex It was introduced for memcg to iterate cgroup hierarchy without holding cgroup_mutex, but soon after that it was replaced with a lockless way in memcg. No one used hierarchy_mutex since that, so remove it. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- Documentation/cgroups/cgroups.txt | 2 +- include/linux/cgroup.h | 17 ++------------- kernel/cgroup.c | 45 --------------------------------------- 3 files changed, 3 insertions(+), 61 deletions(-) (limited to 'include/linux') diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt index 8e74980ab385..e86faaea7d66 100644 --- a/Documentation/cgroups/cgroups.txt +++ b/Documentation/cgroups/cgroups.txt @@ -656,7 +656,7 @@ example in cpusets, no task may attach before 'cpus' and 'mems' are set up. void bind(struct cgroup *root) -(cgroup_mutex and ss->hierarchy_mutex held by caller) +(cgroup_mutex held by caller) Called when a cgroup subsystem is rebound to a different hierarchy and root cgroup. Currently this will only involve movement between diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index d3f5fba2c159..c90eaa803440 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -499,22 +499,9 @@ struct cgroup_subsys { #define MAX_CGROUP_TYPE_NAMELEN 32 const char *name; - /* - * Protects sibling/children links of cgroups in this - * hierarchy, plus protects which hierarchy (or none) the - * subsystem is a part of (i.e. root/sibling). To avoid - * potential deadlocks, the following operations should not be - * undertaken while holding any hierarchy_mutex: - * - * - allocating memory - * - initiating hotplug events - */ - struct mutex hierarchy_mutex; - struct lock_class_key subsys_key; - /* * Link to parent, and list entry in parent's children. - * Protected by this->hierarchy_mutex and cgroup_lock() + * Protected by cgroup_lock() */ struct cgroupfs_root *root; struct list_head sibling; @@ -602,7 +589,7 @@ int cgroup_attach_task_all(struct task_struct *from, struct task_struct *); * the lifetime of cgroup_subsys_state is subsys's matter. * * Looking up and scanning function should be called under rcu_read_lock(). - * Taking cgroup_mutex()/hierarchy_mutex() is not necessary for following calls. + * Taking cgroup_mutex is not necessary for following calls. * But the css returned by this routine can be "not populated yet" or "being * destroyed". The caller should check css and cgroup's status. */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index ceeafe874b3f..dec62f5936ef 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1073,28 +1073,24 @@ static int rebind_subsystems(struct cgroupfs_root *root, BUG_ON(cgrp->subsys[i]); BUG_ON(!dummytop->subsys[i]); BUG_ON(dummytop->subsys[i]->cgroup != dummytop); - mutex_lock(&ss->hierarchy_mutex); cgrp->subsys[i] = dummytop->subsys[i]; cgrp->subsys[i]->cgroup = cgrp; list_move(&ss->sibling, &root->subsys_list); ss->root = root; if (ss->bind) ss->bind(cgrp); - mutex_unlock(&ss->hierarchy_mutex); /* refcount was already taken, and we're keeping it */ } else if (bit & removed_bits) { /* We're removing this subsystem */ BUG_ON(ss == NULL); BUG_ON(cgrp->subsys[i] != dummytop->subsys[i]); BUG_ON(cgrp->subsys[i]->cgroup != cgrp); - mutex_lock(&ss->hierarchy_mutex); if (ss->bind) ss->bind(dummytop); dummytop->subsys[i]->cgroup = dummytop; cgrp->subsys[i] = NULL; subsys[i]->root = &rootnode; list_move(&ss->sibling, &rootnode.subsys_list); - mutex_unlock(&ss->hierarchy_mutex); /* subsystem is now free - drop reference on module */ module_put(ss->module); } else if (bit & final_bits) { @@ -3917,37 +3913,6 @@ static void init_cgroup_css(struct cgroup_subsys_state *css, set_bit(CSS_CLEAR_CSS_REFS, &css->flags); } -static void cgroup_lock_hierarchy(struct cgroupfs_root *root) -{ - /* We need to take each hierarchy_mutex in a consistent order */ - int i; - - /* - * No worry about a race with rebind_subsystems that might mess up the - * locking order, since both parties are under cgroup_mutex. - */ - for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { - struct cgroup_subsys *ss = subsys[i]; - if (ss == NULL) - continue; - if (ss->root == root) - mutex_lock(&ss->hierarchy_mutex); - } -} - -static void cgroup_unlock_hierarchy(struct cgroupfs_root *root) -{ - int i; - - for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { - struct cgroup_subsys *ss = subsys[i]; - if (ss == NULL) - continue; - if (ss->root == root) - mutex_unlock(&ss->hierarchy_mutex); - } -} - /* * cgroup_create - create a cgroup * @parent: cgroup that will be parent of the new cgroup @@ -4008,9 +3973,7 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, ss->post_clone(cgrp); } - cgroup_lock_hierarchy(root); list_add(&cgrp->sibling, &cgrp->parent->children); - cgroup_unlock_hierarchy(root); root->number_of_cgroups++; err = cgroup_create_dir(cgrp, dentry, mode); @@ -4037,9 +4000,7 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, err_remove: - cgroup_lock_hierarchy(root); list_del(&cgrp->sibling); - cgroup_unlock_hierarchy(root); root->number_of_cgroups--; err_destroy: @@ -4247,10 +4208,8 @@ again: list_del_init(&cgrp->release_list); raw_spin_unlock(&release_list_lock); - cgroup_lock_hierarchy(cgrp->root); /* delete this cgroup from parent->children */ list_del_init(&cgrp->sibling); - cgroup_unlock_hierarchy(cgrp->root); list_del_init(&cgrp->allcg_node); @@ -4324,8 +4283,6 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss) * need to invoke fork callbacks here. */ BUG_ON(!list_empty(&init_task.tasks)); - mutex_init(&ss->hierarchy_mutex); - lockdep_set_class(&ss->hierarchy_mutex, &ss->subsys_key); ss->active = 1; /* this function shouldn't be used with modular subsystems, since they @@ -4452,8 +4409,6 @@ int __init_or_module cgroup_load_subsys(struct cgroup_subsys *ss) } write_unlock(&css_set_lock); - mutex_init(&ss->hierarchy_mutex); - lockdep_set_class(&ss->hierarchy_mutex, &ss->subsys_key); ss->active = 1; /* success! */ -- cgit v1.2.3-59-g8ed1b From 24214449b00b94328e239d3c35cda3e6fe0f931b Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 4 May 2012 18:28:21 +0200 Subject: x86, amd_nb: Export model 0x10 and later PCI id Add the F3 PCI id of F15h, model 0x10 to pci_ids.h and to the amd_nb code which generates the list of northbridges on an AMD box. Shorten define name while at it so that it fits into pci_ids.h. Acked-by: Clemens Ladisch Cc: Bjorn Helgaas Acked-by: Andreas Herrmann Signed-off-by: Borislav Petkov --- arch/x86/kernel/amd_nb.c | 1 + drivers/hwmon/k10temp.c | 5 +---- include/linux/pci_ids.h | 1 + 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/amd_nb.c b/arch/x86/kernel/amd_nb.c index be16854591cc..153a0ee88fb1 100644 --- a/arch/x86/kernel/amd_nb.c +++ b/arch/x86/kernel/amd_nb.c @@ -16,6 +16,7 @@ const struct pci_device_id amd_nb_misc_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) }, {} }; EXPORT_SYMBOL(amd_nb_misc_ids); diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index 7356b5ec8f67..f2fe8078633b 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -33,9 +33,6 @@ static bool force; module_param(force, bool, 0444); MODULE_PARM_DESC(force, "force loading on processors with erratum 319"); -/* PCI-IDs for Northbridge devices not used anywhere else */ -#define PCI_DEVICE_ID_AMD_15H_M10H_NB_F3 0x1403 - /* CPUID function 0x80000001, ebx */ #define CPUID_PKGTYPE_MASK 0xf0000000 #define CPUID_PKGTYPE_F 0x00000000 @@ -213,7 +210,7 @@ static DEFINE_PCI_DEVICE_TABLE(k10temp_id_table) = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) }, - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_NB_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) }, {} }; MODULE_DEVICE_TABLE(pci, k10temp_id_table); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index ab741b0d0074..05fd02e44993 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -517,6 +517,7 @@ #define PCI_DEVICE_ID_AMD_11H_NB_DRAM 0x1302 #define PCI_DEVICE_ID_AMD_11H_NB_MISC 0x1303 #define PCI_DEVICE_ID_AMD_11H_NB_LINK 0x1304 +#define PCI_DEVICE_ID_AMD_15H_M10H_F3 0x1403 #define PCI_DEVICE_ID_AMD_15H_NB_F0 0x1600 #define PCI_DEVICE_ID_AMD_15H_NB_F1 0x1601 #define PCI_DEVICE_ID_AMD_15H_NB_F2 0x1602 -- cgit v1.2.3-59-g8ed1b From 7a74c1a18d1f03462f9f766ee8213535ce131b0f Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Sat, 19 May 2012 04:39:00 +0000 Subject: netfilter: remove include/linux/netfilter_ipv4/ipt_addrtype.h It was scheduled to be removed. Acked-by: Florian Westphal Signed-off-by: Cong Wang Signed-off-by: Pablo Neira Ayuso --- Documentation/feature-removal-schedule.txt | 8 -------- include/linux/netfilter_ipv4/Kbuild | 1 - include/linux/netfilter_ipv4/ipt_addrtype.h | 27 --------------------------- 3 files changed, 36 deletions(-) delete mode 100644 include/linux/netfilter_ipv4/ipt_addrtype.h (limited to 'include/linux') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 56000b33340b..08e7e71b266c 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -421,14 +421,6 @@ Files: net/netfilter/xt_connlimit.c ---------------------------- -What: ipt_addrtype match include file -When: 2012 -Why: superseded by xt_addrtype -Who: Florian Westphal -Files: include/linux/netfilter_ipv4/ipt_addrtype.h - ----------------------------- - What: i2c_driver.attach_adapter i2c_driver.detach_adapter When: September 2011 diff --git a/include/linux/netfilter_ipv4/Kbuild b/include/linux/netfilter_ipv4/Kbuild index c61b8fb1a9ef..8ba0c5b72ea9 100644 --- a/include/linux/netfilter_ipv4/Kbuild +++ b/include/linux/netfilter_ipv4/Kbuild @@ -5,7 +5,6 @@ header-y += ipt_LOG.h header-y += ipt_REJECT.h header-y += ipt_TTL.h header-y += ipt_ULOG.h -header-y += ipt_addrtype.h header-y += ipt_ah.h header-y += ipt_ecn.h header-y += ipt_ttl.h diff --git a/include/linux/netfilter_ipv4/ipt_addrtype.h b/include/linux/netfilter_ipv4/ipt_addrtype.h deleted file mode 100644 index 0da42237c8da..000000000000 --- a/include/linux/netfilter_ipv4/ipt_addrtype.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef _IPT_ADDRTYPE_H -#define _IPT_ADDRTYPE_H - -#include - -enum { - IPT_ADDRTYPE_INVERT_SOURCE = 0x0001, - IPT_ADDRTYPE_INVERT_DEST = 0x0002, - IPT_ADDRTYPE_LIMIT_IFACE_IN = 0x0004, - IPT_ADDRTYPE_LIMIT_IFACE_OUT = 0x0008, -}; - -struct ipt_addrtype_info_v1 { - __u16 source; /* source-type mask */ - __u16 dest; /* dest-type mask */ - __u32 flags; -}; - -/* revision 0 */ -struct ipt_addrtype_info { - __u16 source; /* source-type mask */ - __u16 dest; /* dest-type mask */ - __u32 invert_source; - __u32 invert_dest; -}; - -#endif -- cgit v1.2.3-59-g8ed1b From 68c07cb6d8aa05daf38ab47d5bb674d81a2066fb Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Sat, 19 May 2012 04:39:01 +0000 Subject: netfilter: xt_connlimit: remove revision 0 It was scheduled to be removed. Cc: Jan Engelhardt Signed-off-by: Cong Wang Signed-off-by: Pablo Neira Ayuso --- Documentation/feature-removal-schedule.txt | 7 ------ include/linux/netfilter/xt_connlimit.h | 9 ++------ net/netfilter/xt_connlimit.c | 35 ++++++++++-------------------- 3 files changed, 13 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 08e7e71b266c..24ac00f4ae4b 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -414,13 +414,6 @@ Who: Jean Delvare ---------------------------- -What: xt_connlimit rev 0 -When: 2012 -Who: Jan Engelhardt -Files: net/netfilter/xt_connlimit.c - ----------------------------- - What: i2c_driver.attach_adapter i2c_driver.detach_adapter When: September 2011 diff --git a/include/linux/netfilter/xt_connlimit.h b/include/linux/netfilter/xt_connlimit.h index d1366f05d1b2..f1656096121e 100644 --- a/include/linux/netfilter/xt_connlimit.h +++ b/include/linux/netfilter/xt_connlimit.h @@ -22,13 +22,8 @@ struct xt_connlimit_info { #endif }; unsigned int limit; - union { - /* revision 0 */ - unsigned int inverse; - - /* revision 1 */ - __u32 flags; - }; + /* revision 1 */ + __u32 flags; /* Used internally by the kernel */ struct xt_connlimit_data *data __attribute__((aligned(8))); diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index c6d5a83450c9..70b5591a2586 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -274,38 +274,25 @@ static void connlimit_mt_destroy(const struct xt_mtdtor_param *par) kfree(info->data); } -static struct xt_match connlimit_mt_reg[] __read_mostly = { - { - .name = "connlimit", - .revision = 0, - .family = NFPROTO_UNSPEC, - .checkentry = connlimit_mt_check, - .match = connlimit_mt, - .matchsize = sizeof(struct xt_connlimit_info), - .destroy = connlimit_mt_destroy, - .me = THIS_MODULE, - }, - { - .name = "connlimit", - .revision = 1, - .family = NFPROTO_UNSPEC, - .checkentry = connlimit_mt_check, - .match = connlimit_mt, - .matchsize = sizeof(struct xt_connlimit_info), - .destroy = connlimit_mt_destroy, - .me = THIS_MODULE, - }, +static struct xt_match connlimit_mt_reg __read_mostly = { + .name = "connlimit", + .revision = 1, + .family = NFPROTO_UNSPEC, + .checkentry = connlimit_mt_check, + .match = connlimit_mt, + .matchsize = sizeof(struct xt_connlimit_info), + .destroy = connlimit_mt_destroy, + .me = THIS_MODULE, }; static int __init connlimit_mt_init(void) { - return xt_register_matches(connlimit_mt_reg, - ARRAY_SIZE(connlimit_mt_reg)); + return xt_register_match(&connlimit_mt_reg); } static void __exit connlimit_mt_exit(void) { - xt_unregister_matches(connlimit_mt_reg, ARRAY_SIZE(connlimit_mt_reg)); + xt_unregister_match(&connlimit_mt_reg); } module_init(connlimit_mt_init); -- cgit v1.2.3-59-g8ed1b From fdb694a01f1fcd30fd16d8aa290c34699fe98a17 Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Thu, 24 May 2012 03:56:44 +0000 Subject: netfilter: Add fail-open support Implement a new "fail-open" mode where packets are not dropped upon queue-full condition. This mode can be enabled/disabled per queue using netlink NFQA_CFG_FLAGS & NFQA_CFG_MASK attributes. Signed-off-by: Krishna Kumar Signed-off-by: Vivek Kashyap Signed-off-by: Sridhar Samudrala --- include/linux/netfilter/nfnetlink_queue.h | 5 ++++ net/netfilter/nfnetlink_queue.c | 40 ++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h index 24b32e6c009e..a6c1ddac05cc 100644 --- a/include/linux/netfilter/nfnetlink_queue.h +++ b/include/linux/netfilter/nfnetlink_queue.h @@ -84,8 +84,13 @@ enum nfqnl_attr_config { NFQA_CFG_CMD, /* nfqnl_msg_config_cmd */ NFQA_CFG_PARAMS, /* nfqnl_msg_config_params */ NFQA_CFG_QUEUE_MAXLEN, /* __u32 */ + NFQA_CFG_MASK, /* identify which flags to change */ + NFQA_CFG_FLAGS, /* value of these flags (__u32) */ __NFQA_CFG_MAX }; #define NFQA_CFG_MAX (__NFQA_CFG_MAX-1) +/* Flags for NFQA_CFG_FLAGS */ +#define NFQA_CFG_F_FAIL_OPEN (1 << 0) + #endif /* _NFNETLINK_QUEUE_H */ diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 4162437b8361..630da3d2c62a 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -52,6 +52,7 @@ struct nfqnl_instance { u_int16_t queue_num; /* number of this queue */ u_int8_t copy_mode; + u_int32_t flags; /* Set using NFQA_CFG_FLAGS */ /* * Following fields are dirtied for each queued packet, * keep them in same cache line if possible. @@ -406,6 +407,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) struct nfqnl_instance *queue; int err = -ENOBUFS; __be32 *packet_id_ptr; + int failopen = 0; /* rcu_read_lock()ed by nf_hook_slow() */ queue = instance_lookup(queuenum); @@ -431,9 +433,14 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) goto err_out_free_nskb; } if (queue->queue_total >= queue->queue_maxlen) { - queue->queue_dropped++; - net_warn_ratelimited("nf_queue: full at %d entries, dropping packets(s)\n", - queue->queue_total); + if (queue->flags & NFQA_CFG_F_FAIL_OPEN) { + failopen = 1; + err = 0; + } else { + queue->queue_dropped++; + net_warn_ratelimited("nf_queue: full at %d entries, dropping packets(s)\n", + queue->queue_total); + } goto err_out_free_nskb; } entry->id = ++queue->id_sequence; @@ -455,6 +462,8 @@ err_out_free_nskb: kfree_skb(nskb); err_out_unlock: spin_unlock_bh(&queue->lock); + if (failopen) + nf_reinject(entry, NF_ACCEPT); err_out: return err; } @@ -858,6 +867,31 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, spin_unlock_bh(&queue->lock); } + if (nfqa[NFQA_CFG_FLAGS]) { + __u32 flags, mask; + + if (!queue) { + ret = -ENODEV; + goto err_out_unlock; + } + + if (!nfqa[NFQA_CFG_MASK]) { + /* A mask is needed to specify which flags are being + * changed. + */ + ret = -EINVAL; + goto err_out_unlock; + } + + flags = ntohl(nla_get_be32(nfqa[NFQA_CFG_FLAGS])); + mask = ntohl(nla_get_be32(nfqa[NFQA_CFG_MASK])); + + spin_lock_bh(&queue->lock); + queue->flags &= ~mask; + queue->flags |= flags & mask; + spin_unlock_bh(&queue->lock); + } + err_out_unlock: rcu_read_unlock(); return ret; -- cgit v1.2.3-59-g8ed1b From efdedd5426a94b00d23483a1bcb4af3a91c894db Mon Sep 17 00:00:00 2001 From: Denys Fedoryshchenko Date: Thu, 17 May 2012 23:08:57 +0300 Subject: netfilter: xt_recent: add address masking option The mask option allows you put all address belonging that mask into the same recent slot. This can be useful in case that recent is used to detect attacks from the same network segment. Tested for backward compatibility. Signed-off-by: Denys Fedoryshchenko Signed-off-by: Pablo Neira Ayuso --- Documentation/feature-removal-schedule.txt | 7 ++++ include/linux/netfilter.h | 10 +++++ include/linux/netfilter/xt_recent.h | 10 +++++ net/netfilter/xt_recent.c | 62 +++++++++++++++++++++++++----- 4 files changed, 80 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 24ac00f4ae4b..bc4b9c6eb80e 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -574,6 +574,13 @@ Why: Remount currently allows changing bound subsystems and ---------------------------- +What: xt_recent rev 0 +When: 2013 +Who: Pablo Neira Ayuso +Files: net/netfilter/xt_recent.c + +---------------------------- + What: KVM debugfs statistics When: 2013 Why: KVM tracepoints provide mostly equivalent information in a much more diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index ff9c84c29b28..4541f33dbfc3 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -94,6 +94,16 @@ static inline int nf_inet_addr_cmp(const union nf_inet_addr *a1, a1->all[3] == a2->all[3]; } +static inline void nf_inet_addr_mask(const union nf_inet_addr *a1, + union nf_inet_addr *result, + const union nf_inet_addr *mask) +{ + result->all[0] = a1->all[0] & mask->all[0]; + result->all[1] = a1->all[1] & mask->all[1]; + result->all[2] = a1->all[2] & mask->all[2]; + result->all[3] = a1->all[3] & mask->all[3]; +} + extern void netfilter_init(void); /* Largest hook number + 1 */ diff --git a/include/linux/netfilter/xt_recent.h b/include/linux/netfilter/xt_recent.h index 83318e01425e..6ef36c113e89 100644 --- a/include/linux/netfilter/xt_recent.h +++ b/include/linux/netfilter/xt_recent.h @@ -32,4 +32,14 @@ struct xt_recent_mtinfo { __u8 side; }; +struct xt_recent_mtinfo_v1 { + __u32 seconds; + __u32 hit_count; + __u8 check_set; + __u8 invert; + char name[XT_RECENT_NAME_LEN]; + __u8 side; + union nf_inet_addr mask; +}; + #endif /* _LINUX_NETFILTER_XT_RECENT_H */ diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index fc0d6dbe5d17..ae2ad1eec8d0 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -75,6 +75,7 @@ struct recent_entry { struct recent_table { struct list_head list; char name[XT_RECENT_NAME_LEN]; + union nf_inet_addr mask; unsigned int refcnt; unsigned int entries; struct list_head lru_list; @@ -228,10 +229,10 @@ recent_mt(const struct sk_buff *skb, struct xt_action_param *par) { struct net *net = dev_net(par->in ? par->in : par->out); struct recent_net *recent_net = recent_pernet(net); - const struct xt_recent_mtinfo *info = par->matchinfo; + const struct xt_recent_mtinfo_v1 *info = par->matchinfo; struct recent_table *t; struct recent_entry *e; - union nf_inet_addr addr = {}; + union nf_inet_addr addr = {}, addr_mask; u_int8_t ttl; bool ret = info->invert; @@ -261,12 +262,15 @@ recent_mt(const struct sk_buff *skb, struct xt_action_param *par) spin_lock_bh(&recent_lock); t = recent_table_lookup(recent_net, info->name); - e = recent_entry_lookup(t, &addr, par->family, + + nf_inet_addr_mask(&addr, &addr_mask, &t->mask); + + e = recent_entry_lookup(t, &addr_mask, par->family, (info->check_set & XT_RECENT_TTL) ? ttl : 0); if (e == NULL) { if (!(info->check_set & XT_RECENT_SET)) goto out; - e = recent_entry_init(t, &addr, par->family, ttl); + e = recent_entry_init(t, &addr_mask, par->family, ttl); if (e == NULL) par->hotdrop = true; ret = !ret; @@ -306,10 +310,10 @@ out: return ret; } -static int recent_mt_check(const struct xt_mtchk_param *par) +static int recent_mt_check(const struct xt_mtchk_param *par, + const struct xt_recent_mtinfo_v1 *info) { struct recent_net *recent_net = recent_pernet(par->net); - const struct xt_recent_mtinfo *info = par->matchinfo; struct recent_table *t; #ifdef CONFIG_PROC_FS struct proc_dir_entry *pde; @@ -361,6 +365,8 @@ static int recent_mt_check(const struct xt_mtchk_param *par) goto out; } t->refcnt = 1; + + memcpy(&t->mask, &info->mask, sizeof(t->mask)); strcpy(t->name, info->name); INIT_LIST_HEAD(&t->lru_list); for (i = 0; i < ip_list_hash_size; i++) @@ -385,10 +391,28 @@ out: return ret; } +static int recent_mt_check_v0(const struct xt_mtchk_param *par) +{ + const struct xt_recent_mtinfo_v0 *info_v0 = par->matchinfo; + struct xt_recent_mtinfo_v1 info_v1; + + /* Copy revision 0 structure to revision 1 */ + memcpy(&info_v1, info_v0, sizeof(struct xt_recent_mtinfo)); + /* Set default mask to ensure backward compatible behaviour */ + memset(info_v1.mask.all, 0xFF, sizeof(info_v1.mask.all)); + + return recent_mt_check(par, &info_v1); +} + +static int recent_mt_check_v1(const struct xt_mtchk_param *par) +{ + return recent_mt_check(par, par->matchinfo); +} + static void recent_mt_destroy(const struct xt_mtdtor_param *par) { struct recent_net *recent_net = recent_pernet(par->net); - const struct xt_recent_mtinfo *info = par->matchinfo; + const struct xt_recent_mtinfo_v1 *info = par->matchinfo; struct recent_table *t; mutex_lock(&recent_mutex); @@ -625,7 +649,7 @@ static struct xt_match recent_mt_reg[] __read_mostly = { .family = NFPROTO_IPV4, .match = recent_mt, .matchsize = sizeof(struct xt_recent_mtinfo), - .checkentry = recent_mt_check, + .checkentry = recent_mt_check_v0, .destroy = recent_mt_destroy, .me = THIS_MODULE, }, @@ -635,10 +659,30 @@ static struct xt_match recent_mt_reg[] __read_mostly = { .family = NFPROTO_IPV6, .match = recent_mt, .matchsize = sizeof(struct xt_recent_mtinfo), - .checkentry = recent_mt_check, + .checkentry = recent_mt_check_v0, + .destroy = recent_mt_destroy, + .me = THIS_MODULE, + }, + { + .name = "recent", + .revision = 1, + .family = NFPROTO_IPV4, + .match = recent_mt, + .matchsize = sizeof(struct xt_recent_mtinfo_v1), + .checkentry = recent_mt_check_v1, .destroy = recent_mt_destroy, .me = THIS_MODULE, }, + { + .name = "recent", + .revision = 1, + .family = NFPROTO_IPV6, + .match = recent_mt, + .matchsize = sizeof(struct xt_recent_mtinfo_v1), + .checkentry = recent_mt_check_v1, + .destroy = recent_mt_destroy, + .me = THIS_MODULE, + } }; static int __init recent_mt_init(void) -- cgit v1.2.3-59-g8ed1b From 80f12eccce775dc6bb93dba9b52529740f929237 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Wed, 6 Jun 2012 17:13:06 +0000 Subject: Added kernel support in EEE Ethtool commands This patch extends the kernel's ethtool interface by adding support for 2 new EEE commands - get_eee and set_eee. Thanks goes to Giuseppe Cavallaro for his original patch adding this support. Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Reviewed-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/ethtool.h | 35 +++++++++++++++++++++++++++++++++++ net/core/ethtool.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index e17fa7140588..297370a6cb18 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -136,6 +136,35 @@ struct ethtool_eeprom { __u8 data[0]; }; +/** + * struct ethtool_eee - Energy Efficient Ethernet information + * @cmd: ETHTOOL_{G,S}EEE + * @supported: Mask of %SUPPORTED_* flags for the speed/duplex combinations + * for which there is EEE support. + * @advertised: Mask of %ADVERTISED_* flags for the speed/duplex combinations + * advertised as eee capable. + * @lp_advertised: Mask of %ADVERTISED_* flags for the speed/duplex + * combinations advertised by the link partner as eee capable. + * @eee_active: Result of the eee auto negotiation. + * @eee_enabled: EEE configured mode (enabled/disabled). + * @tx_lpi_enabled: Whether the interface should assert its tx lpi, given + * that eee was negotiated. + * @tx_lpi_timer: Time in microseconds the interface delays prior to asserting + * its tx lpi (after reaching 'idle' state). Effective only when eee + * was negotiated and tx_lpi_enabled was set. + */ +struct ethtool_eee { + __u32 cmd; + __u32 supported; + __u32 advertised; + __u32 lp_advertised; + __u32 eee_active; + __u32 eee_enabled; + __u32 tx_lpi_enabled; + __u32 tx_lpi_timer; + __u32 reserved[2]; +}; + /** * struct ethtool_modinfo - plugin module eeprom information * @cmd: %ETHTOOL_GMODULEINFO @@ -945,6 +974,8 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings) * @get_module_info: Get the size and type of the eeprom contained within * a plug-in module. * @get_module_eeprom: Get the eeprom information from the plug-in module + * @get_eee: Get Energy-Efficient (EEE) supported and status. + * @set_eee: Set EEE status (enable/disable) as well as LPI timers. * * All operations are optional (i.e. the function pointer may be set * to %NULL) and callers must take this into account. Callers must @@ -1011,6 +1042,8 @@ struct ethtool_ops { struct ethtool_modinfo *); int (*get_module_eeprom)(struct net_device *, struct ethtool_eeprom *, u8 *); + int (*get_eee)(struct net_device *, struct ethtool_eee *); + int (*set_eee)(struct net_device *, struct ethtool_eee *); }; @@ -1089,6 +1122,8 @@ struct ethtool_ops { #define ETHTOOL_GET_TS_INFO 0x00000041 /* Get time stamping and PHC info */ #define ETHTOOL_GMODULEINFO 0x00000042 /* Get plug-in module information */ #define ETHTOOL_GMODULEEEPROM 0x00000043 /* Get plug-in module eeprom */ +#define ETHTOOL_GEEE 0x00000044 /* Get EEE settings */ +#define ETHTOOL_SEEE 0x00000045 /* Set EEE settings */ /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 9c2afb480270..c73d0a59212c 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -729,6 +729,40 @@ static int ethtool_set_wol(struct net_device *dev, char __user *useraddr) return dev->ethtool_ops->set_wol(dev, &wol); } +static int ethtool_get_eee(struct net_device *dev, char __user *useraddr) +{ + struct ethtool_eee edata; + int rc; + + if (!dev->ethtool_ops->get_eee) + return -EOPNOTSUPP; + + memset(&edata, 0, sizeof(struct ethtool_eee)); + edata.cmd = ETHTOOL_GEEE; + rc = dev->ethtool_ops->get_eee(dev, &edata); + + if (rc) + return rc; + + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + + return 0; +} + +static int ethtool_set_eee(struct net_device *dev, char __user *useraddr) +{ + struct ethtool_eee edata; + + if (!dev->ethtool_ops->set_eee) + return -EOPNOTSUPP; + + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + + return dev->ethtool_ops->set_eee(dev, &edata); +} + static int ethtool_nway_reset(struct net_device *dev) { if (!dev->ethtool_ops->nway_reset) @@ -1471,6 +1505,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) rc = ethtool_set_value_void(dev, useraddr, dev->ethtool_ops->set_msglevel); break; + case ETHTOOL_GEEE: + rc = ethtool_get_eee(dev, useraddr); + break; + case ETHTOOL_SEEE: + rc = ethtool_set_eee(dev, useraddr); + break; case ETHTOOL_NWAY_RST: rc = ethtool_nway_reset(dev); break; -- cgit v1.2.3-59-g8ed1b From 4fd1e324b7b5f80bd521b58593ada74ef89e80c4 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 6 Jun 2012 10:55:26 +0530 Subject: dma: dmaengine: add slave req id in slave_config The DMA controller like Nvidia's Tegra Dma controller supports the different slave requestor id from different slave. This need to be configure in dma controller to handle the request properly. Adding the slave-id in the slave configuration so that information can be passed from client when configuring for slave. Signed-off-by: Laxman Dewangan Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 56377df39124..ccec62f8e501 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -338,6 +338,9 @@ enum dma_slave_buswidth { * @device_fc: Flow Controller Settings. Only valid for slave channels. Fill * with 'true' if peripheral should be flow controller. Direction will be * selected at Runtime. + * @slave_id: Slave requester id. Only valid for slave channels. The dma + * slave peripheral will have unique id as dma requester which need to be + * pass as slave config. * * This struct is passed in as configuration data to a DMA engine * in order to set up a certain channel for DMA transport at runtime. @@ -365,6 +368,7 @@ struct dma_slave_config { u32 src_maxburst; u32 dst_maxburst; bool device_fc; + unsigned int slave_id; }; static inline const char *dma_chan_name(struct dma_chan *chan) -- cgit v1.2.3-59-g8ed1b From 6f5cf52114dd87f9ed091678f7dfc8ff21bbe2b3 Mon Sep 17 00:00:00 2001 From: Donald Dutile Date: Mon, 4 Jun 2012 17:29:02 -0400 Subject: iommu/dmar: Reserve mmio space used by the IOMMU, if the BIOS forgets to Intel-iommu initialization doesn't currently reserve the memory used for the IOMMU registers. This can allow the pci resource allocator to assign a device BAR to the same address as the IOMMU registers. This can cause some not so nice side affects when the driver ioremap's that region. Introduced two helper functions to map & unmap the IOMMU registers as well as simplify the init and exit paths. Signed-off-by: Donald Dutile Acked-by: Chris Wright Cc: iommu@lists.linux-foundation.org Cc: suresh.b.siddha@intel.com Cc: dwmw2@infradead.org Link: http://lkml.kernel.org/r/1338845342-12464-3-git-send-email-ddutile@redhat.com Signed-off-by: Ingo Molnar --- drivers/iommu/dmar.c | 111 +++++++++++++++++++++++++++++++++----------- include/linux/intel-iommu.h | 2 + 2 files changed, 86 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 1e5a10de3471..9ab6ebf46f7a 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -570,14 +570,89 @@ int __init detect_intel_iommu(void) } +static void unmap_iommu(struct intel_iommu *iommu) +{ + iounmap(iommu->reg); + release_mem_region(iommu->reg_phys, iommu->reg_size); +} + +/** + * map_iommu: map the iommu's registers + * @iommu: the iommu to map + * @phys_addr: the physical address of the base resgister + * + * Memory map the iommu's registers. Start w/ a single page, and + * possibly expand if that turns out to be insufficent. + */ +static int map_iommu(struct intel_iommu *iommu, u64 phys_addr) +{ + int map_size, err=0; + + iommu->reg_phys = phys_addr; + iommu->reg_size = VTD_PAGE_SIZE; + + if (!request_mem_region(iommu->reg_phys, iommu->reg_size, iommu->name)) { + pr_err("IOMMU: can't reserve memory\n"); + err = -EBUSY; + goto out; + } + + iommu->reg = ioremap(iommu->reg_phys, iommu->reg_size); + if (!iommu->reg) { + pr_err("IOMMU: can't map the region\n"); + err = -ENOMEM; + goto release; + } + + iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG); + iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG); + + if (iommu->cap == (uint64_t)-1 && iommu->ecap == (uint64_t)-1) { + err = -EINVAL; + warn_invalid_dmar(phys_addr, " returns all ones"); + goto unmap; + } + + /* the registers might be more than one page */ + map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap), + cap_max_fault_reg_offset(iommu->cap)); + map_size = VTD_PAGE_ALIGN(map_size); + if (map_size > iommu->reg_size) { + iounmap(iommu->reg); + release_mem_region(iommu->reg_phys, iommu->reg_size); + iommu->reg_size = map_size; + if (!request_mem_region(iommu->reg_phys, iommu->reg_size, + iommu->name)) { + pr_err("IOMMU: can't reserve memory\n"); + err = -EBUSY; + goto out; + } + iommu->reg = ioremap(iommu->reg_phys, iommu->reg_size); + if (!iommu->reg) { + pr_err("IOMMU: can't map the region\n"); + err = -ENOMEM; + goto release; + } + } + err = 0; + goto out; + +unmap: + iounmap(iommu->reg); +release: + release_mem_region(iommu->reg_phys, iommu->reg_size); +out: + return err; +} + int alloc_iommu(struct dmar_drhd_unit *drhd) { struct intel_iommu *iommu; - int map_size; u32 ver; static int iommu_allocated = 0; int agaw = 0; int msagaw = 0; + int err; if (!drhd->reg_base_addr) { warn_invalid_dmar(0, ""); @@ -591,19 +666,13 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) iommu->seq_id = iommu_allocated++; sprintf (iommu->name, "dmar%d", iommu->seq_id); - iommu->reg = ioremap(drhd->reg_base_addr, VTD_PAGE_SIZE); - if (!iommu->reg) { - pr_err("IOMMU: can't map the region\n"); + err = map_iommu(iommu, drhd->reg_base_addr); + if (err) { + pr_err("IOMMU: failed to map %s\n", iommu->name); goto error; } - iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG); - iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG); - - if (iommu->cap == (uint64_t)-1 && iommu->ecap == (uint64_t)-1) { - warn_invalid_dmar(drhd->reg_base_addr, " returns all ones"); - goto err_unmap; - } + err = -EINVAL; agaw = iommu_calculate_agaw(iommu); if (agaw < 0) { pr_err("Cannot get a valid agaw for iommu (seq_id = %d)\n", @@ -621,19 +690,6 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) iommu->node = -1; - /* the registers might be more than one page */ - map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap), - cap_max_fault_reg_offset(iommu->cap)); - map_size = VTD_PAGE_ALIGN(map_size); - if (map_size > VTD_PAGE_SIZE) { - iounmap(iommu->reg); - iommu->reg = ioremap(drhd->reg_base_addr, map_size); - if (!iommu->reg) { - pr_err("IOMMU: can't map the region\n"); - goto error; - } - } - ver = readl(iommu->reg + DMAR_VER_REG); pr_info("IOMMU %d: reg_base_addr %llx ver %d:%d cap %llx ecap %llx\n", iommu->seq_id, @@ -648,10 +704,10 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) return 0; err_unmap: - iounmap(iommu->reg); + unmap_iommu(iommu); error: kfree(iommu); - return -1; + return err; } void free_iommu(struct intel_iommu *iommu) @@ -662,7 +718,8 @@ void free_iommu(struct intel_iommu *iommu) free_dmar_iommu(iommu); if (iommu->reg) - iounmap(iommu->reg); + unmap_iommu(iommu); + kfree(iommu); } diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index e6ca56de9936..78e2ada50cd5 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -308,6 +308,8 @@ enum { struct intel_iommu { void __iomem *reg; /* Pointer to hardware regs, virtual addr */ + u64 reg_phys; /* physical address of hw register set */ + u64 reg_size; /* size of hw register set */ u64 cap; u64 ecap; u32 gcmd; /* Holds TE, EAFL. Don't need SRTP, SFL, WBF */ -- cgit v1.2.3-59-g8ed1b From 90306c41dc3d8e5f12ecd0193dae99e0e7f6e896 Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Tue, 29 May 2012 23:01:09 -0500 Subject: GFS2: Use lvbs for storing rgrp information with mount option Instead of reading in the resource groups when gfs2 is checking for free space to allocate from, gfs2 can store the necessary infromation in the resource group's lvb. Also, instead of searching for unlinked inodes in every resource group that's checked for free space, gfs2 can store the number of unlinked but inodes in the lvb, and only check for unlinked inodes if it will find some. The first time a resource group is locked, the lvb must initialized. Since this involves counting the unlinked inodes in the resource group, this takes a little extra time. But after that, if the resource group is locked with GL_SKIP, the buffer head won't be read in unless it's actually needed. Enabling the resource groups lvbs is done via the rgrplvb mount option. If this option isn't set, the lvbs will still be set and updated, but they won't be verfied or used by the filesystem. To safely turn on this option, all of the nodes mounting the filesystem must be running code with this patch, and the filesystem must have been completely unmounted since they were updated. Signed-off-by: Benjamin Marzinski Signed-off-by: Steven Whitehouse --- fs/gfs2/glock.c | 1 + fs/gfs2/incore.h | 2 + fs/gfs2/rgrp.c | 147 +++++++++++++++++++++++++++++++++++++++++--- fs/gfs2/super.c | 12 ++++ include/linux/gfs2_ondisk.h | 10 +++ 5 files changed, 163 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 3ad8cb3eeb88..10ae1645d9a5 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -769,6 +769,7 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number, gl->gl_stats.stats[GFS2_LKS_DCOUNT] = 0; gl->gl_stats.stats[GFS2_LKS_QCOUNT] = 0; memset(&gl->gl_lksb, 0, sizeof(struct dlm_lksb)); + memset(gl->gl_lvb, 0, 32 * sizeof(char)); gl->gl_lksb.sb_lvbptr = gl->gl_lvb; gl->gl_tchange = jiffies; gl->gl_object = NULL; diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 5cda51a3e3bd..dc730700b3b4 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -89,6 +89,7 @@ struct gfs2_rgrpd { u64 rd_igeneration; struct gfs2_bitmap *rd_bits; struct gfs2_sbd *rd_sbd; + struct gfs2_rgrp_lvb *rd_rgl; u32 rd_last_alloc; u32 rd_flags; #define GFS2_RDF_CHECK 0x10000000 /* check for unlinked inodes */ @@ -470,6 +471,7 @@ struct gfs2_args { unsigned int ar_discard:1; /* discard requests */ unsigned int ar_errors:2; /* errors=withdraw | panic */ unsigned int ar_nobarrier:1; /* do not send barriers */ + unsigned int ar_rgrplvb:1; /* use lvbs for rgrp info */ int ar_commit; /* Commit interval */ int ar_statfs_quantum; /* The fast statfs interval */ int ar_quota_quantum; /* The quota interval */ diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 9eca6a9cff8f..3c6f7ed16a3b 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -660,6 +660,7 @@ static int read_rindex_entry(struct gfs2_inode *ip) goto fail; rgd->rd_gl->gl_object = rgd; + rgd->rd_rgl = (struct gfs2_rgrp_lvb *)rgd->rd_gl->gl_lvb; rgd->rd_flags &= ~GFS2_RDF_UPTODATE; if (rgd->rd_data > sdp->sd_max_rg_data) sdp->sd_max_rg_data = rgd->rd_data; @@ -769,9 +770,65 @@ static void gfs2_rgrp_out(struct gfs2_rgrpd *rgd, void *buf) memset(&str->rg_reserved, 0, sizeof(str->rg_reserved)); } +static int gfs2_rgrp_lvb_valid(struct gfs2_rgrpd *rgd) +{ + struct gfs2_rgrp_lvb *rgl = rgd->rd_rgl; + struct gfs2_rgrp *str = (struct gfs2_rgrp *)rgd->rd_bits[0].bi_bh->b_data; + + if (rgl->rl_flags != str->rg_flags || rgl->rl_free != str->rg_free || + rgl->rl_dinodes != str->rg_dinodes || + rgl->rl_igeneration != str->rg_igeneration) + return 0; + return 1; +} + +static void gfs2_rgrp_ondisk2lvb(struct gfs2_rgrp_lvb *rgl, const void *buf) +{ + const struct gfs2_rgrp *str = buf; + + rgl->rl_magic = cpu_to_be32(GFS2_MAGIC); + rgl->rl_flags = str->rg_flags; + rgl->rl_free = str->rg_free; + rgl->rl_dinodes = str->rg_dinodes; + rgl->rl_igeneration = str->rg_igeneration; + rgl->__pad = 0UL; +} + +static void update_rgrp_lvb_unlinked(struct gfs2_rgrpd *rgd, u32 change) +{ + struct gfs2_rgrp_lvb *rgl = rgd->rd_rgl; + u32 unlinked = be32_to_cpu(rgl->rl_unlinked) + change; + rgl->rl_unlinked = cpu_to_be32(unlinked); +} + +static u32 count_unlinked(struct gfs2_rgrpd *rgd) +{ + struct gfs2_bitmap *bi; + const u32 length = rgd->rd_length; + const u8 *buffer = NULL; + u32 i, goal, count = 0; + + for (i = 0, bi = rgd->rd_bits; i < length; i++, bi++) { + goal = 0; + buffer = bi->bi_bh->b_data + bi->bi_offset; + WARN_ON(!buffer_uptodate(bi->bi_bh)); + while (goal < bi->bi_len * GFS2_NBBY) { + goal = gfs2_bitfit(buffer, bi->bi_len, goal, + GFS2_BLKST_UNLINKED); + if (goal == BFITNOENT) + break; + count++; + goal++; + } + } + + return count; +} + + /** - * gfs2_rgrp_go_lock - Read in a RG's header and bitmaps - * @gh: The glock holder for the resource group + * gfs2_rgrp_bh_get - Read in a RG's header and bitmaps + * @rgd: the struct gfs2_rgrpd describing the RG to read in * * Read in all of a Resource Group's header and bitmap blocks. * Caller must eventually call gfs2_rgrp_relse() to free the bitmaps. @@ -779,9 +836,8 @@ static void gfs2_rgrp_out(struct gfs2_rgrpd *rgd, void *buf) * Returns: errno */ -int gfs2_rgrp_go_lock(struct gfs2_holder *gh) +int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd) { - struct gfs2_rgrpd *rgd = gh->gh_gl->gl_object; struct gfs2_sbd *sdp = rgd->rd_sbd; struct gfs2_glock *gl = rgd->rd_gl; unsigned int length = rgd->rd_length; @@ -789,6 +845,9 @@ int gfs2_rgrp_go_lock(struct gfs2_holder *gh) unsigned int x, y; int error; + if (rgd->rd_bits[0].bi_bh != NULL) + return 0; + for (x = 0; x < length; x++) { bi = rgd->rd_bits + x; error = gfs2_meta_read(gl, rgd->rd_addr + x, 0, &bi->bi_bh); @@ -815,7 +874,20 @@ int gfs2_rgrp_go_lock(struct gfs2_holder *gh) rgd->rd_flags |= (GFS2_RDF_UPTODATE | GFS2_RDF_CHECK); rgd->rd_free_clone = rgd->rd_free; } - + if (be32_to_cpu(GFS2_MAGIC) != rgd->rd_rgl->rl_magic) { + rgd->rd_rgl->rl_unlinked = cpu_to_be32(count_unlinked(rgd)); + gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, + rgd->rd_bits[0].bi_bh->b_data); + } + else if (sdp->sd_args.ar_rgrplvb) { + if (!gfs2_rgrp_lvb_valid(rgd)){ + gfs2_consist_rgrpd(rgd); + error = -EIO; + goto fail; + } + if (rgd->rd_rgl->rl_unlinked == 0) + rgd->rd_flags &= ~GFS2_RDF_CHECK; + } return 0; fail: @@ -829,6 +901,39 @@ fail: return error; } +int update_rgrp_lvb(struct gfs2_rgrpd *rgd) +{ + u32 rl_flags; + + if (rgd->rd_flags & GFS2_RDF_UPTODATE) + return 0; + + if (be32_to_cpu(GFS2_MAGIC) != rgd->rd_rgl->rl_magic) + return gfs2_rgrp_bh_get(rgd); + + rl_flags = be32_to_cpu(rgd->rd_rgl->rl_flags); + rl_flags &= ~GFS2_RDF_MASK; + rgd->rd_flags &= GFS2_RDF_MASK; + rgd->rd_flags |= (rl_flags | GFS2_RDF_UPTODATE | GFS2_RDF_CHECK); + if (rgd->rd_rgl->rl_unlinked == 0) + rgd->rd_flags &= ~GFS2_RDF_CHECK; + rgd->rd_free = be32_to_cpu(rgd->rd_rgl->rl_free); + rgd->rd_free_clone = rgd->rd_free; + rgd->rd_dinodes = be32_to_cpu(rgd->rd_rgl->rl_dinodes); + rgd->rd_igeneration = be64_to_cpu(rgd->rd_rgl->rl_igeneration); + return 0; +} + +int gfs2_rgrp_go_lock(struct gfs2_holder *gh) +{ + struct gfs2_rgrpd *rgd = gh->gh_gl->gl_object; + struct gfs2_sbd *sdp = rgd->rd_sbd; + + if (gh->gh_flags & GL_SKIP && sdp->sd_args.ar_rgrplvb) + return 0; + return gfs2_rgrp_bh_get((struct gfs2_rgrpd *)gh->gh_gl->gl_object); +} + /** * gfs2_rgrp_go_unlock - Release RG bitmaps read in with gfs2_rgrp_bh_get() * @gh: The glock holder for the resource group @@ -842,8 +947,10 @@ void gfs2_rgrp_go_unlock(struct gfs2_holder *gh) for (x = 0; x < length; x++) { struct gfs2_bitmap *bi = rgd->rd_bits + x; - brelse(bi->bi_bh); - bi->bi_bh = NULL; + if (bi->bi_bh) { + brelse(bi->bi_bh); + bi->bi_bh = NULL; + } } } @@ -987,6 +1094,7 @@ int gfs2_fitrim(struct file *filp, void __user *argp) rgd->rd_flags |= GFS2_RGF_TRIMMED; gfs2_trans_add_bh(rgd->rd_gl, bh, 1); gfs2_rgrp_out(rgd, bh->b_data); + gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, bh->b_data); gfs2_trans_end(sdp); } } @@ -1116,6 +1224,9 @@ static int get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked) int error, rg_locked, flags = LM_FLAG_TRY; int loops = 0; + if (sdp->sd_args.ar_rgrplvb) + flags |= GL_SKIP; + if (ip->i_rgd && rgrp_contains_block(ip->i_rgd, ip->i_goal)) rgd = begin = ip->i_rgd; else @@ -1133,22 +1244,34 @@ static int get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked) } else { error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, flags, &rs->rs_rgd_gh); + if (!error && sdp->sd_args.ar_rgrplvb) { + error = update_rgrp_lvb(rgd); + if (error) { + gfs2_glock_dq_uninit(&rs->rs_rgd_gh); + return error; + } + } } switch (error) { case 0: if (try_rgrp_fit(rgd, ip)) { + if (sdp->sd_args.ar_rgrplvb) + gfs2_rgrp_bh_get(rgd); ip->i_rgd = rgd; return 0; } - if (rgd->rd_flags & GFS2_RDF_CHECK) + if (rgd->rd_flags & GFS2_RDF_CHECK) { + if (sdp->sd_args.ar_rgrplvb) + gfs2_rgrp_bh_get(rgd); try_rgrp_unlink(rgd, last_unlinked, ip->i_no_addr); + } if (!rg_locked) gfs2_glock_dq_uninit(&rs->rs_rgd_gh); /* fall through */ case GLR_TRYFAILED: rgd = gfs2_rgrpd_get_next(rgd); if (rgd == begin) { - flags = 0; + flags &= ~LM_FLAG_TRY; loops++; } break; @@ -1529,6 +1652,7 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks, gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data); + gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, rgd->rd_bits[0].bi_bh->b_data); gfs2_statfs_change(sdp, 0, -(s64)*nblocks, dinode ? 1 : 0); if (dinode) @@ -1575,6 +1699,7 @@ void __gfs2_free_blocks(struct gfs2_inode *ip, u64 bstart, u32 blen, int meta) rgd->rd_flags &= ~GFS2_RGF_TRIMMED; gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data); + gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, rgd->rd_bits[0].bi_bh->b_data); /* Directories keep their data in the metadata address space */ if (meta || ip->i_depth) @@ -1611,6 +1736,8 @@ void gfs2_unlink_di(struct inode *inode) trace_gfs2_block_alloc(ip, rgd, blkno, 1, GFS2_BLKST_UNLINKED); gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data); + gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, rgd->rd_bits[0].bi_bh->b_data); + update_rgrp_lvb_unlinked(rgd, 1); } static void gfs2_free_uninit_di(struct gfs2_rgrpd *rgd, u64 blkno) @@ -1630,6 +1757,8 @@ static void gfs2_free_uninit_di(struct gfs2_rgrpd *rgd, u64 blkno) gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data); + gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, rgd->rd_bits[0].bi_bh->b_data); + update_rgrp_lvb_unlinked(rgd, -1); gfs2_statfs_change(sdp, 0, +1, -1); } diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 81fc76264ed4..788068758f3a 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -78,6 +78,8 @@ enum { Opt_quota_quantum, Opt_barrier, Opt_nobarrier, + Opt_rgrplvb, + Opt_norgrplvb, Opt_error, }; @@ -115,6 +117,8 @@ static const match_table_t tokens = { {Opt_quota_quantum, "quota_quantum=%d"}, {Opt_barrier, "barrier"}, {Opt_nobarrier, "nobarrier"}, + {Opt_rgrplvb, "rgrplvb"}, + {Opt_norgrplvb, "norgrplvb"}, {Opt_error, NULL} }; @@ -267,6 +271,12 @@ int gfs2_mount_args(struct gfs2_args *args, char *options) case Opt_nobarrier: args->ar_nobarrier = 1; break; + case Opt_rgrplvb: + args->ar_rgrplvb = 1; + break; + case Opt_norgrplvb: + args->ar_rgrplvb = 0; + break; case Opt_error: default: printk(KERN_WARNING "GFS2: invalid mount option: %s\n", o); @@ -1379,6 +1389,8 @@ static int gfs2_show_options(struct seq_file *s, struct dentry *root) seq_printf(s, ",nobarrier"); if (test_bit(SDF_DEMOTE, &sdp->sd_flags)) seq_printf(s, ",demote_interface_used"); + if (args->ar_rgrplvb) + seq_printf(s, ",rgrplvb"); return 0; } diff --git a/include/linux/gfs2_ondisk.h b/include/linux/gfs2_ondisk.h index e8ccf6ff3b4d..b2de1f9a88d6 100644 --- a/include/linux/gfs2_ondisk.h +++ b/include/linux/gfs2_ondisk.h @@ -170,6 +170,16 @@ struct gfs2_rindex { #define GFS2_RGF_NOALLOC 0x00000008 #define GFS2_RGF_TRIMMED 0x00000010 +struct gfs2_rgrp_lvb { + __be32 rl_magic; + __be32 rl_flags; + __be32 rl_free; + __be32 rl_dinodes; + __be64 rl_igeneration; + __be32 rl_unlinked; + __be32 __pad; +}; + struct gfs2_rgrp { struct gfs2_meta_header rg_header; -- cgit v1.2.3-59-g8ed1b From 2397849baa7c44c242e5d5142d5d16d1e7ed53d0 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 9 Jun 2012 14:56:12 -0700 Subject: [PATCH] tcp: Cache inetpeer in timewait socket, and only when necessary. Since it's guarenteed that we will access the inetpeer if we're trying to do timewait recycling and TCP options were enabled on the connection, just cache the peer in the timewait socket. In the future, inetpeer lookups will be context dependent (per routing realm), and this helps facilitate that as well. Signed-off-by: David S. Miller --- include/linux/tcp.h | 3 ++- include/net/tcp.h | 1 - include/net/timewait_sock.h | 8 -------- net/ipv4/tcp_ipv4.c | 10 ---------- net/ipv4/tcp_minisocks.c | 27 ++++++++++++++++++++------- net/ipv6/tcp_ipv6.c | 13 ------------- 6 files changed, 22 insertions(+), 40 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 4c5b63283377..23e8234f75a5 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -506,8 +506,9 @@ struct tcp_timewait_sock { u32 tw_rcv_wnd; u32 tw_ts_recent; long tw_ts_recent_stamp; + struct inet_peer *tw_peer; #ifdef CONFIG_TCP_MD5SIG - struct tcp_md5sig_key *tw_md5_key; + struct tcp_md5sig_key *tw_md5_key; #endif /* Few sockets in timewait have cookies; in that case, then this * object holds a reference to them (tw_cookie_values->kref). diff --git a/include/net/tcp.h b/include/net/tcp.h index 424591866037..9332f342259a 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -328,7 +328,6 @@ extern void tcp_shutdown (struct sock *sk, int how); extern int tcp_v4_rcv(struct sk_buff *skb); extern struct inet_peer *tcp_v4_get_peer(struct sock *sk); -extern void *tcp_v4_tw_get_peer(struct sock *sk); extern int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw); extern int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t size); diff --git a/include/net/timewait_sock.h b/include/net/timewait_sock.h index 8d6689cb2c66..68f0ecad6c6e 100644 --- a/include/net/timewait_sock.h +++ b/include/net/timewait_sock.h @@ -22,7 +22,6 @@ struct timewait_sock_ops { int (*twsk_unique)(struct sock *sk, struct sock *sktw, void *twp); void (*twsk_destructor)(struct sock *sk); - void *(*twsk_getpeer)(struct sock *sk); }; static inline int twsk_unique(struct sock *sk, struct sock *sktw, void *twp) @@ -41,11 +40,4 @@ static inline void twsk_destructor(struct sock *sk) sk->sk_prot->twsk_prot->twsk_destructor(sk); } -static inline void *twsk_getpeer(struct sock *sk) -{ - if (sk->sk_prot->twsk_prot->twsk_getpeer) - return sk->sk_prot->twsk_prot->twsk_getpeer(sk); - return NULL; -} - #endif /* _TIMEWAIT_SOCK_H */ diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 77f049d00dbb..fda2ca17135e 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1835,20 +1835,10 @@ struct inet_peer *tcp_v4_get_peer(struct sock *sk) } EXPORT_SYMBOL(tcp_v4_get_peer); -void *tcp_v4_tw_get_peer(struct sock *sk) -{ - const struct inet_timewait_sock *tw = inet_twsk(sk); - struct net *net = sock_net(sk); - - return inet_getpeer_v4(net, tw->tw_daddr, 1); -} -EXPORT_SYMBOL(tcp_v4_tw_get_peer); - static struct timewait_sock_ops tcp_timewait_sock_ops = { .twsk_obj_size = sizeof(struct tcp_timewait_sock), .twsk_unique = tcp_twsk_unique, .twsk_destructor= tcp_twsk_destructor, - .twsk_getpeer = tcp_v4_tw_get_peer, }; const struct inet_connection_sock_af_ops ipv4_specific = { diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index fef9dbf3af00..cb015317c9f7 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -77,20 +77,19 @@ static bool tcp_remember_stamp(struct sock *sk) static bool tcp_tw_remember_stamp(struct inet_timewait_sock *tw) { + const struct tcp_timewait_sock *tcptw; struct sock *sk = (struct sock *) tw; struct inet_peer *peer; - peer = twsk_getpeer(sk); + tcptw = tcp_twsk(sk); + peer = tcptw->tw_peer; if (peer) { - const struct tcp_timewait_sock *tcptw = tcp_twsk(sk); - if ((s32)(peer->tcp_ts - tcptw->tw_ts_recent) <= 0 || ((u32)get_seconds() - peer->tcp_ts_stamp > TCP_PAWS_MSL && peer->tcp_ts_stamp <= (u32)tcptw->tw_ts_recent_stamp)) { peer->tcp_ts_stamp = (u32)tcptw->tw_ts_recent_stamp; peer->tcp_ts = tcptw->tw_ts_recent; } - inet_putpeer(peer); return true; } return false; @@ -314,9 +313,12 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) const struct inet_connection_sock *icsk = inet_csk(sk); const struct tcp_sock *tp = tcp_sk(sk); bool recycle_ok = false; + bool recycle_on = false; - if (tcp_death_row.sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp) + if (tcp_death_row.sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp) { recycle_ok = tcp_remember_stamp(sk); + recycle_on = true; + } if (tcp_death_row.tw_count < tcp_death_row.sysctl_max_tw_buckets) tw = inet_twsk_alloc(sk, state); @@ -324,8 +326,10 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) if (tw != NULL) { struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw); const int rto = (icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1); + struct inet_sock *inet = inet_sk(sk); + struct inet_peer *peer = NULL; - tw->tw_transparent = inet_sk(sk)->transparent; + tw->tw_transparent = inet->transparent; tw->tw_rcv_wscale = tp->rx_opt.rcv_wscale; tcptw->tw_rcv_nxt = tp->rcv_nxt; tcptw->tw_snd_nxt = tp->snd_nxt; @@ -347,6 +351,12 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) } #endif + if (recycle_on) + peer = icsk->icsk_af_ops->get_peer(sk); + tcptw->tw_peer = peer; + if (peer) + atomic_inc(&peer->refcnt); + #ifdef CONFIG_TCP_MD5SIG /* * The timewait bucket does not have the key DB from the @@ -398,8 +408,11 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) void tcp_twsk_destructor(struct sock *sk) { -#ifdef CONFIG_TCP_MD5SIG struct tcp_timewait_sock *twsk = tcp_twsk(sk); + + if (twsk->tw_peer) + inet_putpeer(twsk->tw_peer); +#ifdef CONFIG_TCP_MD5SIG if (twsk->tw_md5_key) { tcp_free_md5sig_pool(); kfree_rcu(twsk->tw_md5_key, rcu); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index b5ecf37b61a6..f91b0bfd12d5 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1746,23 +1746,10 @@ static struct inet_peer *tcp_v6_get_peer(struct sock *sk) return rt6_get_peer_create(rt); } -static void *tcp_v6_tw_get_peer(struct sock *sk) -{ - const struct inet6_timewait_sock *tw6 = inet6_twsk(sk); - const struct inet_timewait_sock *tw = inet_twsk(sk); - struct net *net = sock_net(sk); - - if (tw->tw_family == AF_INET) - return tcp_v4_tw_get_peer(sk); - - return inet_getpeer_v6(net, &tw6->tw_v6_daddr, 1); -} - static struct timewait_sock_ops tcp6_timewait_sock_ops = { .twsk_obj_size = sizeof(struct tcp6_timewait_sock), .twsk_unique = tcp_twsk_unique, .twsk_destructor= tcp_twsk_destructor, - .twsk_getpeer = tcp_v6_tw_get_peer, }; static const struct inet_connection_sock_af_ops ipv6_specific = { -- cgit v1.2.3-59-g8ed1b From 4a31bd28e86ac50eb620f6b5b36464c45b5fa38f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 11 Jan 2012 13:52:34 +0100 Subject: ARM: nomadik: convert to generic clock Remove more custom stuff by simply converting the Nomadik machine to use generic clocks and move the driver to drivers/clk. Acked-by: Arnd Bergmann Cc: Mike Turquette Signed-off-by: Linus Walleij --- arch/arm/Kconfig | 2 +- arch/arm/mach-nomadik/Makefile | 2 - arch/arm/mach-nomadik/clock.c | 75 ------------------------------- arch/arm/mach-nomadik/clock.h | 15 ------- arch/arm/mach-nomadik/cpu-8815.c | 4 +- drivers/clk/Makefile | 1 + drivers/clk/clk-nomadik.c | 47 +++++++++++++++++++ include/linux/platform_data/clk-nomadik.h | 2 + 8 files changed, 53 insertions(+), 95 deletions(-) delete mode 100644 arch/arm/mach-nomadik/clock.c delete mode 100644 arch/arm/mach-nomadik/clock.h create mode 100644 drivers/clk/clk-nomadik.c create mode 100644 include/linux/platform_data/clk-nomadik.h (limited to 'include/linux') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index b649c5904a4f..e58bda6b6dde 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -912,7 +912,7 @@ config ARCH_NOMADIK select ARM_AMBA select ARM_VIC select CPU_ARM926T - select CLKDEV_LOOKUP + select COMMON_CLK select GENERIC_CLOCKEVENTS select PINCTRL select MIGHT_HAVE_CACHE_L2X0 diff --git a/arch/arm/mach-nomadik/Makefile b/arch/arm/mach-nomadik/Makefile index a6bbd1a7b4e7..a42c9a33d3bf 100644 --- a/arch/arm/mach-nomadik/Makefile +++ b/arch/arm/mach-nomadik/Makefile @@ -7,8 +7,6 @@ # Object file lists. -obj-y += clock.o - # Cpu revision obj-$(CONFIG_NOMADIK_8815) += cpu-8815.o diff --git a/arch/arm/mach-nomadik/clock.c b/arch/arm/mach-nomadik/clock.c deleted file mode 100644 index 48a59f24e10c..000000000000 --- a/arch/arm/mach-nomadik/clock.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * linux/arch/arm/mach-nomadik/clock.c - * - * Copyright (C) 2009 Alessandro Rubini - */ -#include -#include -#include -#include -#include -#include "clock.h" - -/* - * The nomadik board uses generic clocks, but the serial pl011 file - * calls clk_enable(), clk_disable(), clk_get_rate(), so we provide them - */ -unsigned long clk_get_rate(struct clk *clk) -{ - return clk->rate; -} -EXPORT_SYMBOL(clk_get_rate); - -/* enable and disable do nothing */ -int clk_enable(struct clk *clk) -{ - return 0; -} -EXPORT_SYMBOL(clk_enable); - -void clk_disable(struct clk *clk) -{ -} -EXPORT_SYMBOL(clk_disable); - -static struct clk clk_24 = { - .rate = 2400000, -}; - -static struct clk clk_48 = { - .rate = 48 * 1000 * 1000, -}; - -/* - * Catch-all default clock to satisfy drivers using the clk API. We don't - * model the actual hardware clocks yet. - */ -static struct clk clk_default; - -#define CLK(_clk, dev) \ - { \ - .clk = _clk, \ - .dev_id = dev, \ - } - -static struct clk_lookup lookups[] = { - { - .con_id = "apb_pclk", - .clk = &clk_default, - }, - CLK(&clk_24, "mtu0"), - CLK(&clk_24, "mtu1"), - CLK(&clk_48, "uart0"), - CLK(&clk_48, "uart1"), - CLK(&clk_default, "gpio.0"), - CLK(&clk_default, "gpio.1"), - CLK(&clk_default, "gpio.2"), - CLK(&clk_default, "gpio.3"), - CLK(&clk_default, "rng"), -}; - -int __init clk_init(void) -{ - clkdev_add_table(lookups, ARRAY_SIZE(lookups)); - return 0; -} diff --git a/arch/arm/mach-nomadik/clock.h b/arch/arm/mach-nomadik/clock.h deleted file mode 100644 index 78da2e7c3985..000000000000 --- a/arch/arm/mach-nomadik/clock.h +++ /dev/null @@ -1,15 +0,0 @@ - -/* - * linux/arch/arm/mach-nomadik/clock.h - * - * Copyright (C) 2009 Alessandro Rubini - * - * 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. - */ -struct clk { - unsigned long rate; -}; - -int __init clk_init(void); diff --git a/arch/arm/mach-nomadik/cpu-8815.c b/arch/arm/mach-nomadik/cpu-8815.c index 88511fef7987..6fd8e46567a4 100644 --- a/arch/arm/mach-nomadik/cpu-8815.c +++ b/arch/arm/mach-nomadik/cpu-8815.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -35,7 +36,6 @@ #include #include -#include "clock.h" #include "cpu-8815.h" /* The 8815 has 4 GPIO blocks, let's register them immediately */ @@ -123,7 +123,7 @@ void __init cpu8815_init_irq(void) * Init clocks here so that they are available for system timer * initialization. */ - clk_init(); + nomadik_clk_init(); } /* diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index b9a5158a30b1..26b6b92942e1 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \ clk-mux.o clk-divider.o clk-fixed-factor.o # SoCs specific +obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_PLAT_SPEAR) += spear/ diff --git a/drivers/clk/clk-nomadik.c b/drivers/clk/clk-nomadik.c new file mode 100644 index 000000000000..517a8ff7121e --- /dev/null +++ b/drivers/clk/clk-nomadik.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include + +/* + * The Nomadik clock tree is described in the STN8815A12 DB V4.2 + * reference manual for the chip, page 94 ff. + */ + +void __init nomadik_clk_init(void) +{ + struct clk *clk; + + clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0); + clk_register_clkdev(clk, "apb_pclk", NULL); + clk_register_clkdev(clk, NULL, "gpio.0"); + clk_register_clkdev(clk, NULL, "gpio.1"); + clk_register_clkdev(clk, NULL, "gpio.2"); + clk_register_clkdev(clk, NULL, "gpio.3"); + clk_register_clkdev(clk, NULL, "rng"); + + /* + * The 2.4 MHz TIMCLK reference clock is active at boot time, this is + * actually the MXTALCLK @19.2 MHz divided by 8. This clock is used + * by the timers and watchdog. See page 105 ff. + */ + clk = clk_register_fixed_rate(NULL, "TIMCLK", NULL, CLK_IS_ROOT, + 2400000); + clk_register_clkdev(clk, NULL, "mtu0"); + clk_register_clkdev(clk, NULL, "mtu1"); + + /* + * At boot time, PLL2 is set to generate a set of fixed clocks, + * one of them is CLK48, the 48 MHz clock, routed to the UART, MMC/SD + * I2C, IrDA, USB and SSP blocks. + */ + clk = clk_register_fixed_rate(NULL, "CLK48", NULL, CLK_IS_ROOT, + 48000000); + clk_register_clkdev(clk, NULL, "uart0"); + clk_register_clkdev(clk, NULL, "uart1"); + clk_register_clkdev(clk, NULL, "mmci"); + clk_register_clkdev(clk, NULL, "ssp"); + clk_register_clkdev(clk, NULL, "nmk-i2c.0"); + clk_register_clkdev(clk, NULL, "nmk-i2c.1"); +} diff --git a/include/linux/platform_data/clk-nomadik.h b/include/linux/platform_data/clk-nomadik.h new file mode 100644 index 000000000000..5713c87b2477 --- /dev/null +++ b/include/linux/platform_data/clk-nomadik.h @@ -0,0 +1,2 @@ +/* Minimal platform data header */ +void nomadik_clk_init(void); -- cgit v1.2.3-59-g8ed1b From 98a175b60f46a80dfa44fb0e0807f2e5a351f35f Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Sat, 9 Jun 2012 16:40:38 +0530 Subject: regulator: core: Add regulator_set_voltage_time_sel to calculate ramp delay. This patch adds regulator_set_voltage_time_sel(), to move into core, the commonly used code by drivers to provide the .set_voltage_time_sel callback. It will also allow us to configure different ramp delay for different regulators easily. Signed-off-by: Yadwinder Singh Brar Signed-off-by: Mark Brown --- drivers/regulator/core.c | 24 ++++++++++++++++++++++++ include/linux/regulator/driver.h | 5 +++++ 2 files changed, 29 insertions(+) (limited to 'include/linux') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 5c6aedaa934d..ff76abde3ab3 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2279,6 +2279,30 @@ int regulator_set_voltage_time(struct regulator *regulator, } EXPORT_SYMBOL_GPL(regulator_set_voltage_time); +/** + *regulator_set_voltage_time_sel - get raise/fall time + * @regulator: regulator source + * @old_selector: selector for starting voltage + * @new_selector: selector for target voltage + * + * Provided with the starting and target voltage selectors, this function + * returns time in microseconds required to rise or fall to this new voltage + * + * Drivers providing uV_step in their regulator_desc and ramp_delay in + * regulation_constraints can use this as their set_voltage_time_sel() + * operation. + */ +int regulator_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector) +{ + if (rdev->desc->ramp_delay && rdev->desc->uV_step) + return DIV_ROUND_UP(rdev->desc->uV_step * + abs(new_selector - old_selector), + rdev->desc->ramp_delay) * 1000; + return 0; +} + /** * regulator_sync_voltage - re-apply last regulator output voltage * @regulator: regulator source diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 80226383e561..ae5c25379237 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -170,6 +170,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) + * @ramp_delay: Time to settle down after voltage change (unit: mV/us) * @volt_table: Voltage mapping table (if table based mapping) * * @vsel_reg: Register for selector when using regulator_regmap_X_voltage_ @@ -189,6 +190,7 @@ struct regulator_desc { unsigned int min_uV; unsigned int uV_step; + unsigned int ramp_delay; const unsigned int *volt_table; @@ -285,6 +287,9 @@ int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel); int regulator_is_enabled_regmap(struct regulator_dev *rdev); int regulator_enable_regmap(struct regulator_dev *rdev); int regulator_disable_regmap(struct regulator_dev *rdev); +int regulator_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector); void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data); -- cgit v1.2.3-59-g8ed1b From a4f606ea73d56d15f28653d2242e54d58bb612e5 Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Mon, 11 Jun 2012 11:59:36 +0800 Subject: {nl,cfg,mac}80211: fix the coding style related to mesh parameters fix the coding style related to mesh parameters, especially the indentation, as pointed out by Johannes Berg. Signed-off-by: Chun-Yeow Yeoh Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 6 +-- include/linux/nl80211.h | 98 ++++++++++++++++++++++--------------------- include/net/cfg80211.h | 12 +++--- net/mac80211/cfg.c | 3 +- net/mac80211/debugfs_netdev.c | 34 +++++++-------- net/wireless/nl80211.c | 97 ++++++++++++++++++++++-------------------- 6 files changed, 130 insertions(+), 120 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index ce9af8918514..f831078182de 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1443,7 +1443,7 @@ enum ieee80211_tdls_actioncode { * * @IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET: the default synchronization method * @IEEE80211_SYNC_METHOD_VENDOR: a vendor specific synchronization method - * that will be specified in a vendor specific information element + * that will be specified in a vendor specific information element */ enum { IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET = 1, @@ -1455,7 +1455,7 @@ enum { * * @IEEE80211_PATH_PROTOCOL_HWMP: the default path selection protocol * @IEEE80211_PATH_PROTOCOL_VENDOR: a vendor specific protocol that will - * be specified in a vendor specific information element + * be specified in a vendor specific information element */ enum { IEEE80211_PATH_PROTOCOL_HWMP = 1, @@ -1467,7 +1467,7 @@ enum { * * @IEEE80211_PATH_METRIC_AIRTIME: the default path selection metric * @IEEE80211_PATH_METRIC_VENDOR: a vendor specific metric that will be - * specified in a vendor specific information element + * specified in a vendor specific information element */ enum { IEEE80211_PATH_METRIC_AIRTIME = 1, diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 970afdf5a605..c61e1621822c 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -2090,78 +2090,80 @@ enum nl80211_mntr_flags { * @__NL80211_MESHCONF_INVALID: internal use * * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in - * millisecond units, used by the Peer Link Open message + * millisecond units, used by the Peer Link Open message * * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in - * millisecond units, used by the peer link management to close a peer link + * millisecond units, used by the peer link management to close a peer link * * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in - * millisecond units + * millisecond units * * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed - * on this mesh interface + * on this mesh interface * * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link - * open retries that can be sent to establish a new peer link instance in a - * mesh + * open retries that can be sent to establish a new peer link instance in a + * mesh * * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh - * point. + * point. * * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically - * open peer links when we detect compatible mesh peers. + * open peer links when we detect compatible mesh peers. * * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames - * containing a PREQ that an MP can send to a particular destination (path - * target) + * containing a PREQ that an MP can send to a particular destination (path + * target) * * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths - * (in milliseconds) + * (in milliseconds) * * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait - * until giving up on a path discovery (in milliseconds) + * until giving up on a path discovery (in milliseconds) * * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh - * points receiving a PREQ shall consider the forwarding information from the - * root to be valid. (TU = time unit) + * points receiving a PREQ shall consider the forwarding information from + * the root to be valid. (TU = time unit) * * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in - * TUs) during which an MP can send only one action frame containing a PREQ - * reference element + * TUs) during which an MP can send only one action frame containing a PREQ + * reference element * * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs) - * that it takes for an HWMP information element to propagate across the mesh + * that it takes for an HWMP information element to propagate across the + * mesh * * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not * * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a - * source mesh point for path selection elements. + * source mesh point for path selection elements. * * @NL80211_MESHCONF_HWMP_RANN_INTERVAL: The interval of time (in TUs) between - * root announcements are transmitted. + * root announcements are transmitted. * * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has - * access to a broader network beyond the MBSS. This is done via Root - * Announcement frames. + * access to a broader network beyond the MBSS. This is done via Root + * Announcement frames. * * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in - * TUs) during which a mesh STA can send only one Action frame containing a - * PERR element. + * TUs) during which a mesh STA can send only one Action frame containing a + * PERR element. * * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding - * or forwarding entity (default is TRUE - forwarding entity) + * or forwarding entity (default is TRUE - forwarding entity) * * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the - * threshold for average signal strength of candidate station to establish - * a peer link. - * - * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute + * threshold for average signal strength of candidate station to establish + * a peer link. * * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors - * to synchronize to for 11s default synchronization method (see 11C.12.2.2) + * to synchronize to for 11s default synchronization method + * (see 11C.12.2.2) * * @NL80211_MESHCONF_HT_OPMODE: set mesh HT protection mode. * + * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute + * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ enum nl80211_meshconf_params { @@ -2203,34 +2205,36 @@ enum nl80211_meshconf_params { * @__NL80211_MESH_SETUP_INVALID: Internal use * * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a - * vendor specific path selection algorithm or disable it to use the default - * HWMP. + * vendor specific path selection algorithm or disable it to use the + * default HWMP. * * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a - * vendor specific path metric or disable it to use the default Airtime - * metric. + * vendor specific path metric or disable it to use the default Airtime + * metric. * * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a - * robust security network ie, or a vendor specific information element that - * vendors will use to identify the path selection methods and metrics in use. + * robust security network ie, or a vendor specific information element + * that vendors will use to identify the path selection methods and + * metrics in use. * * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication - * daemon will be authenticating mesh candidates. + * daemon will be authenticating mesh candidates. * * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication - * daemon will be securing peer link frames. AMPE is a secured version of Mesh - * Peering Management (MPM) and is implemented with the assistance of a - * userspace daemon. When this flag is set, the kernel will send peer - * management frames to a userspace daemon that will implement AMPE - * functionality (security capabilities selection, key confirmation, and key - * management). When the flag is unset (default), the kernel can autonomously - * complete (unsecured) mesh peering without the need of a userspace daemon. - * - * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number + * daemon will be securing peer link frames. AMPE is a secured version of + * Mesh Peering Management (MPM) and is implemented with the assistance of + * a userspace daemon. When this flag is set, the kernel will send peer + * management frames to a userspace daemon that will implement AMPE + * functionality (security capabilities selection, key confirmation, and + * key management). When the flag is unset (default), the kernel can + * autonomously complete (unsecured) mesh peering without the need of a + * userspace daemon. * * @NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: Enable this option to use a - * vendor specific synchronization method or disable it to use the default - * neighbor offset synchronization + * vendor specific synchronization method or disable it to use the default + * neighbor offset synchronization + * + * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number * * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a129ee2f5483..778e533a9734 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -848,21 +848,21 @@ struct mesh_config { u16 dot11MeshConfirmTimeout; u16 dot11MeshHoldingTimeout; u16 dot11MeshMaxPeerLinks; - u8 dot11MeshMaxRetries; - u8 dot11MeshTTL; - u8 element_ttl; + u8 dot11MeshMaxRetries; + u8 dot11MeshTTL; + u8 element_ttl; bool auto_open_plinks; u32 dot11MeshNbrOffsetMaxNeighbor; - u8 dot11MeshHWMPmaxPREQretries; + u8 dot11MeshHWMPmaxPREQretries; u32 path_refresh_time; u16 min_discovery_timeout; u32 dot11MeshHWMPactivePathTimeout; u16 dot11MeshHWMPpreqMinInterval; u16 dot11MeshHWMPperrMinInterval; u16 dot11MeshHWMPnetDiameterTraversalTime; - u8 dot11MeshHWMPRootMode; + u8 dot11MeshHWMPRootMode; u16 dot11MeshHWMPRannInterval; - bool dot11MeshGateAnnouncementProtocol; + bool dot11MeshGateAnnouncementProtocol; bool dot11MeshForwarding; s32 rssi_threshold; u16 ht_opmode; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 498c94e34427..f41f9bea242a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1571,10 +1571,9 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, conf->dot11MeshGateAnnouncementProtocol = nconf->dot11MeshGateAnnouncementProtocol; } - if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_RANN_INTERVAL, mask)) { + if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_RANN_INTERVAL, mask)) conf->dot11MeshHWMPRannInterval = nconf->dot11MeshHWMPRannInterval; - } if (_chg_mesh_attr(NL80211_MESHCONF_FORWARDING, mask)) conf->dot11MeshForwarding = nconf->dot11MeshForwarding; if (_chg_mesh_attr(NL80211_MESHCONF_RSSI_THRESHOLD, mask)) { diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index d4272ff43f71..c429417e1322 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -468,45 +468,45 @@ IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC); IEEE80211_IF_FILE(fwded_frames, u.mesh.mshstats.fwded_frames, DEC); IEEE80211_IF_FILE(dropped_frames_ttl, u.mesh.mshstats.dropped_frames_ttl, DEC); IEEE80211_IF_FILE(dropped_frames_congestion, - u.mesh.mshstats.dropped_frames_congestion, DEC); + u.mesh.mshstats.dropped_frames_congestion, DEC); IEEE80211_IF_FILE(dropped_frames_no_route, - u.mesh.mshstats.dropped_frames_no_route, DEC); + u.mesh.mshstats.dropped_frames_no_route, DEC); IEEE80211_IF_FILE(estab_plinks, u.mesh.mshstats.estab_plinks, ATOMIC); /* Mesh parameters */ IEEE80211_IF_FILE(dot11MeshMaxRetries, - u.mesh.mshcfg.dot11MeshMaxRetries, DEC); + u.mesh.mshcfg.dot11MeshMaxRetries, DEC); IEEE80211_IF_FILE(dot11MeshRetryTimeout, - u.mesh.mshcfg.dot11MeshRetryTimeout, DEC); + u.mesh.mshcfg.dot11MeshRetryTimeout, DEC); IEEE80211_IF_FILE(dot11MeshConfirmTimeout, - u.mesh.mshcfg.dot11MeshConfirmTimeout, DEC); + u.mesh.mshcfg.dot11MeshConfirmTimeout, DEC); IEEE80211_IF_FILE(dot11MeshHoldingTimeout, - u.mesh.mshcfg.dot11MeshHoldingTimeout, DEC); + u.mesh.mshcfg.dot11MeshHoldingTimeout, DEC); IEEE80211_IF_FILE(dot11MeshTTL, u.mesh.mshcfg.dot11MeshTTL, DEC); IEEE80211_IF_FILE(element_ttl, u.mesh.mshcfg.element_ttl, DEC); IEEE80211_IF_FILE(auto_open_plinks, u.mesh.mshcfg.auto_open_plinks, DEC); IEEE80211_IF_FILE(dot11MeshMaxPeerLinks, - u.mesh.mshcfg.dot11MeshMaxPeerLinks, DEC); + u.mesh.mshcfg.dot11MeshMaxPeerLinks, DEC); IEEE80211_IF_FILE(dot11MeshHWMPactivePathTimeout, - u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC); + u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC); IEEE80211_IF_FILE(dot11MeshHWMPpreqMinInterval, - u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC); + u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC); IEEE80211_IF_FILE(dot11MeshHWMPperrMinInterval, - u.mesh.mshcfg.dot11MeshHWMPperrMinInterval, DEC); + u.mesh.mshcfg.dot11MeshHWMPperrMinInterval, DEC); IEEE80211_IF_FILE(dot11MeshHWMPnetDiameterTraversalTime, - u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC); + u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC); IEEE80211_IF_FILE(dot11MeshHWMPmaxPREQretries, - u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries, DEC); + u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries, DEC); IEEE80211_IF_FILE(path_refresh_time, - u.mesh.mshcfg.path_refresh_time, DEC); + u.mesh.mshcfg.path_refresh_time, DEC); IEEE80211_IF_FILE(min_discovery_timeout, - u.mesh.mshcfg.min_discovery_timeout, DEC); + u.mesh.mshcfg.min_discovery_timeout, DEC); IEEE80211_IF_FILE(dot11MeshHWMPRootMode, - u.mesh.mshcfg.dot11MeshHWMPRootMode, DEC); + u.mesh.mshcfg.dot11MeshHWMPRootMode, DEC); IEEE80211_IF_FILE(dot11MeshGateAnnouncementProtocol, - u.mesh.mshcfg.dot11MeshGateAnnouncementProtocol, DEC); + u.mesh.mshcfg.dot11MeshGateAnnouncementProtocol, DEC); IEEE80211_IF_FILE(dot11MeshHWMPRannInterval, - u.mesh.mshcfg.dot11MeshHWMPRannInterval, DEC); + u.mesh.mshcfg.dot11MeshHWMPRannInterval, DEC); IEEE80211_IF_FILE(dot11MeshForwarding, u.mesh.mshcfg.dot11MeshForwarding, DEC); IEEE80211_IF_FILE(rssi_threshold, u.mesh.mshcfg.rssi_threshold, DEC); IEEE80211_IF_FILE(ht_opmode, u.mesh.mshcfg.ht_opmode, DEC); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7ae54b82291f..dd94ee5fb40a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -115,7 +115,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ }, [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_MESH_ID_LEN }, + .len = IEEE80211_MAX_MESH_ID_LEN }, [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 }, @@ -3492,7 +3492,6 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 }, [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 }, [NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR] = { .type = NLA_U32 }, - [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 }, [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 }, [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 }, @@ -3504,8 +3503,8 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 }, [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 }, [NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 }, - [NL80211_MESHCONF_RSSI_THRESHOLD] = { .type = NLA_U32}, - [NL80211_MESHCONF_HT_OPMODE] = { .type = NLA_U16}, + [NL80211_MESHCONF_RSSI_THRESHOLD] = { .type = NLA_U32 }, + [NL80211_MESHCONF_HT_OPMODE] = { .type = NLA_U16 }, }; static const struct nla_policy @@ -3515,7 +3514,7 @@ static const struct nla_policy [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 }, [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG }, [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, + .len = IEEE80211_MAX_DATA_LEN }, [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG }, }; @@ -3548,63 +3547,71 @@ do {\ /* Fill in the params struct */ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, - mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16); + mask, NL80211_MESHCONF_RETRY_TIMEOUT, + nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, - mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16); + mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, + nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, - mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16); + mask, NL80211_MESHCONF_HOLDING_TIMEOUT, + nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, - mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16); + mask, NL80211_MESHCONF_MAX_PEER_LINKS, + nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, - mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8); + mask, NL80211_MESHCONF_MAX_RETRIES, + nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, - mask, NL80211_MESHCONF_TTL, nla_get_u8); + mask, NL80211_MESHCONF_TTL, nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, - mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8); + mask, NL80211_MESHCONF_ELEMENT_TTL, + nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, - mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, - mask, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, - nla_get_u32); + mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, + nla_get_u8); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, mask, + NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, + nla_get_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, - mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, - nla_get_u8); + mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, + nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, - mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32); + mask, NL80211_MESHCONF_PATH_REFRESH_TIME, + nla_get_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, - mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, - nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, - mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, - nla_get_u32); + mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, + nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, mask, + NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, + nla_get_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, - mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, - nla_get_u16); + mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, + nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval, - mask, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, - nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshHWMPnetDiameterTraversalTime, - mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, - nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshHWMPRootMode, mask, - NL80211_MESHCONF_HWMP_ROOTMODE, - nla_get_u8); + mask, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, + nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshHWMPRannInterval, mask, - NL80211_MESHCONF_HWMP_RANN_INTERVAL, - nla_get_u16); + dot11MeshHWMPnetDiameterTraversalTime, mask, + NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, + nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, mask, + NL80211_MESHCONF_HWMP_ROOTMODE, nla_get_u8); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, mask, + NL80211_MESHCONF_HWMP_RANN_INTERVAL, + nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshGateAnnouncementProtocol, mask, - NL80211_MESHCONF_GATE_ANNOUNCEMENTS, - nla_get_u8); + dot11MeshGateAnnouncementProtocol, mask, + NL80211_MESHCONF_GATE_ANNOUNCEMENTS, + nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, - mask, NL80211_MESHCONF_FORWARDING, nla_get_u8); + mask, NL80211_MESHCONF_FORWARDING, + nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, - mask, NL80211_MESHCONF_RSSI_THRESHOLD, nla_get_u32); + mask, NL80211_MESHCONF_RSSI_THRESHOLD, + nla_get_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, - mask, NL80211_MESHCONF_HT_OPMODE, nla_get_u16); + mask, NL80211_MESHCONF_HT_OPMODE, + nla_get_u16); if (mask_out) *mask_out = mask; -- cgit v1.2.3-59-g8ed1b From a4808147dcf1ecf2f76212a78fd9692b3c112f47 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 11 Jun 2012 13:16:35 +0100 Subject: seq_file: Add seq_vprintf function and export it The existing seq_printf function is rewritten in terms of the new seq_vprintf which is also exported to modules. This allows GFS2 (and potentially other seq_file users) to have a vprintf based interface and to avoid an extra copy into a temporary buffer in some cases. Signed-off-by: Steven Whitehouse Reported-by: Eric Dumazet Acked-by: Al Viro --- fs/seq_file.c | 18 ++++++++++++++---- include/linux/seq_file.h | 1 + 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/fs/seq_file.c b/fs/seq_file.c index 0cbd0494b79e..14cf9de1dbe1 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -385,15 +385,12 @@ int seq_escape(struct seq_file *m, const char *s, const char *esc) } EXPORT_SYMBOL(seq_escape); -int seq_printf(struct seq_file *m, const char *f, ...) +int seq_vprintf(struct seq_file *m, const char *f, va_list args) { - va_list args; int len; if (m->count < m->size) { - va_start(args, f); len = vsnprintf(m->buf + m->count, m->size - m->count, f, args); - va_end(args); if (m->count + len < m->size) { m->count += len; return 0; @@ -402,6 +399,19 @@ int seq_printf(struct seq_file *m, const char *f, ...) seq_set_overflow(m); return -1; } +EXPORT_SYMBOL(seq_vprintf); + +int seq_printf(struct seq_file *m, const char *f, ...) +{ + int ret; + va_list args; + + va_start(args, f); + ret = seq_vprintf(m, f, args); + va_end(args); + + return ret; +} EXPORT_SYMBOL(seq_printf); /** diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index fc61854f6224..83c44eefe698 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -86,6 +86,7 @@ int seq_puts(struct seq_file *m, const char *s); int seq_write(struct seq_file *seq, const void *data, size_t len); __printf(2, 3) int seq_printf(struct seq_file *, const char *, ...); +__printf(2, 0) int seq_vprintf(struct seq_file *, const char *, va_list args); int seq_path(struct seq_file *, const struct path *, const char *); int seq_dentry(struct seq_file *, struct dentry *, const char *); -- cgit v1.2.3-59-g8ed1b From 109cdbc223f6e2d6c80f8371f22415b50c18a366 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 18 May 2012 16:52:19 -0600 Subject: PCI: remove pci_bus_find_ext_capability() (unused) pci_bus_find_ext_capability() is unused, and this patch removes it. Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 43 ------------------------------------------- include/linux/pci.h | 2 -- 2 files changed, 45 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..de9386da2eb2 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -329,49 +329,6 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap) } EXPORT_SYMBOL_GPL(pci_find_ext_capability); -/** - * pci_bus_find_ext_capability - find an extended capability - * @bus: the PCI bus to query - * @devfn: PCI device to query - * @cap: capability code - * - * Like pci_find_ext_capability() but works for pci devices that do not have a - * pci_dev structure set up yet. - * - * Returns the address of the requested capability structure within the - * device's PCI configuration space or 0 in case the device does not - * support it. - */ -int pci_bus_find_ext_capability(struct pci_bus *bus, unsigned int devfn, - int cap) -{ - u32 header; - int ttl; - int pos = PCI_CFG_SPACE_SIZE; - - /* minimum 8 bytes per capability */ - ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; - - if (!pci_bus_read_config_dword(bus, devfn, pos, &header)) - return 0; - if (header == 0xffffffff || header == 0) - return 0; - - while (ttl-- > 0) { - if (PCI_EXT_CAP_ID(header) == cap) - return pos; - - pos = PCI_EXT_CAP_NEXT(header); - if (pos < PCI_CFG_SPACE_SIZE) - break; - - if (!pci_bus_read_config_dword(bus, devfn, pos, &header)) - break; - } - - return 0; -} - static int __pci_find_next_ht_cap(struct pci_dev *dev, int pos, int ht_cap) { int rc, ttl = PCI_FIND_CAP_TTL; diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..2618ad996535 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -714,8 +714,6 @@ enum pci_lost_interrupt_reason pci_lost_interrupt(struct pci_dev *dev); int pci_find_capability(struct pci_dev *dev, int cap); int pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap); int pci_find_ext_capability(struct pci_dev *dev, int cap); -int pci_bus_find_ext_capability(struct pci_bus *bus, unsigned int devfn, - int cap); int pci_find_ht_capability(struct pci_dev *dev, int ht_cap); int pci_find_next_ht_capability(struct pci_dev *dev, int pos, int ht_cap); struct pci_bus *pci_find_next_bus(const struct pci_bus *from); -- cgit v1.2.3-59-g8ed1b From f5d411c91ede162240f34e05a233f2759412988e Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sun, 31 Jul 2011 17:44:12 +0200 Subject: nohz: Rename ts->idle_tick to ts->last_tick Now that idle and nohz logics are going to be independant each others, ts->idle_tick becomes too much a biased name to describe the field that saves the last scheduled tick on top of which we re-calculate the next tick to schedule when the timer is restarted. We want to reuse this even to stop the tick outside idle cases. So let's rename it to some more generic name: ts->last_tick. This changes a bit the timer list stat export so we need to increase its version. Signed-off-by: Frederic Weisbecker Cc: Alessio Igor Bogani Cc: Andrew Morton Cc: Avi Kivity Cc: Chris Metcalf Cc: Christoph Lameter Cc: Daniel Lezcano Cc: Geoff Levand Cc: Gilad Ben Yossef Cc: Hakan Akkan Cc: Ingo Molnar Cc: Kevin Hilman Cc: Max Krasnyansky Cc: Paul E. McKenney Cc: Peter Zijlstra Cc: Stephen Hemminger Cc: Steven Rostedt Cc: Sven-Thorsten Dietrich Cc: Thomas Gleixner --- include/linux/tick.h | 8 ++++---- kernel/time/tick-sched.c | 4 ++-- kernel/time/timer_list.c | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tick.h b/include/linux/tick.h index ab8be90b5cc9..f37fceb69b73 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -31,10 +31,10 @@ enum tick_nohz_mode { * struct tick_sched - sched tick emulation and no idle tick control/stats * @sched_timer: hrtimer to schedule the periodic tick in high * resolution mode - * @idle_tick: Store the last idle tick expiry time when the tick - * timer is modified for idle sleeps. This is necessary + * @last_tick: Store the last tick expiry time when the tick + * timer is modified for nohz sleeps. This is necessary * to resume the tick timer operation in the timeline - * when the CPU returns from idle + * when the CPU returns from nohz sleep. * @tick_stopped: Indicator that the idle tick has been stopped * @idle_jiffies: jiffies at the entry to idle for idle time accounting * @idle_calls: Total number of idle calls @@ -51,7 +51,7 @@ struct tick_sched { struct hrtimer sched_timer; unsigned long check_clocks; enum tick_nohz_mode nohz_mode; - ktime_t idle_tick; + ktime_t last_tick; int inidle; int tick_stopped; unsigned long idle_jiffies; diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 911834b33b8a..73cc4901336d 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -400,7 +400,7 @@ static void tick_nohz_stop_sched_tick(struct tick_sched *ts, ktime_t now) if (!ts->tick_stopped) { select_nohz_load_balancer(1); - ts->idle_tick = hrtimer_get_expires(&ts->sched_timer); + ts->last_tick = hrtimer_get_expires(&ts->sched_timer); ts->tick_stopped = 1; } @@ -526,7 +526,7 @@ ktime_t tick_nohz_get_sleep_length(void) static void tick_nohz_restart(struct tick_sched *ts, ktime_t now) { hrtimer_cancel(&ts->sched_timer); - hrtimer_set_expires(&ts->sched_timer, ts->idle_tick); + hrtimer_set_expires(&ts->sched_timer, ts->last_tick); while (1) { /* Forward the time to expire in the future */ diff --git a/kernel/time/timer_list.c b/kernel/time/timer_list.c index 3258455549f4..af5a7e9f164b 100644 --- a/kernel/time/timer_list.c +++ b/kernel/time/timer_list.c @@ -167,7 +167,7 @@ static void print_cpu(struct seq_file *m, int cpu, u64 now) { struct tick_sched *ts = tick_get_tick_sched(cpu); P(nohz_mode); - P_ns(idle_tick); + P_ns(last_tick); P(tick_stopped); P(idle_jiffies); P(idle_calls); @@ -259,7 +259,7 @@ static int timer_list_show(struct seq_file *m, void *v) u64 now = ktime_to_ns(ktime_get()); int cpu; - SEQ_printf(m, "Timer List Version: v0.6\n"); + SEQ_printf(m, "Timer List Version: v0.7\n"); SEQ_printf(m, "HRTIMER_MAX_CLOCK_BASES: %d\n", HRTIMER_MAX_CLOCK_BASES); SEQ_printf(m, "now at %Ld nsecs\n", (unsigned long long)now); -- cgit v1.2.3-59-g8ed1b From 9136461ab921fd5066ba6a0de08399e2172f4d71 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 11 Jun 2012 06:36:13 +0000 Subject: net: keep name_hlist close to name __dev_get_by_name() is slow because pm_qos_req has been inserted between name[] and name_hlist, adding cache misses. pm_qos_req has nothing to do at the beginning of struct net_device Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d94cb1431519..a0b84e3b087c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1046,10 +1046,9 @@ struct net_device { */ char name[IFNAMSIZ]; - struct pm_qos_request pm_qos_req; - - /* device name hash chain */ + /* device name hash chain, please keep it close to name[] */ struct hlist_node name_hlist; + /* snmp alias */ char *ifalias; @@ -1322,6 +1321,8 @@ struct net_device { /* group the device belongs to */ int group; + + struct pm_qos_request pm_qos_req; }; #define to_net_dev(d) container_of(d, struct net_device, dev) -- cgit v1.2.3-59-g8ed1b From 12ea6cad1c7d046e21decc18b0e2170c6794dc51 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 11 Jun 2012 05:26:55 +0000 Subject: PCI: add PCI DMA source ID quirk DMA transactions are tagged with the source ID of the device making the request. Occasionally hardware screws this up and uses the source ID of a different device (often the wrong function number of a multifunction device). A specific Ricoh multifunction device is a prime example of this problem and included in this patch. Given a pci_dev, this function returns the pci_dev to use as the source ID for DMA. When hardware works correctly, this returns the input device. For the components of the Ricoh multifunction device, it returns the pci_dev for function 0. This will be used by IOMMU drivers for determining the boundaries of IOMMU groups as multiple devices using the same source ID must be contained within the same group. This can also be used by existing streaming DMA paths for the same purpose. [bhelgaas: fold in pci_dev_get() for !CONFIG_PCI] Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 8 ++++++++ 2 files changed, 59 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 2a7521677541..acd3956b44bd 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3179,3 +3179,54 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe) return -ENOTTY; } + +static struct pci_dev *pci_func_0_dma_source(struct pci_dev *dev) +{ + if (!PCI_FUNC(dev->devfn)) + return pci_dev_get(dev); + + return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); +} + +static const struct pci_dev_dma_source { + u16 vendor; + u16 device; + struct pci_dev *(*dma_source)(struct pci_dev *dev); +} pci_dev_dma_source[] = { + /* + * https://bugzilla.redhat.com/show_bug.cgi?id=605888 + * + * Some Ricoh devices use the function 0 source ID for DMA on + * other functions of a multifunction device. The DMA devices + * is therefore function 0, which will have implications of the + * iommu grouping of these devices. + */ + { PCI_VENDOR_ID_RICOH, 0xe822, pci_func_0_dma_source }, + { PCI_VENDOR_ID_RICOH, 0xe230, pci_func_0_dma_source }, + { PCI_VENDOR_ID_RICOH, 0xe832, pci_func_0_dma_source }, + { PCI_VENDOR_ID_RICOH, 0xe476, pci_func_0_dma_source }, + { 0 } +}; + +/* + * IOMMUs with isolation capabilities need to be programmed with the + * correct source ID of a device. In most cases, the source ID matches + * the device doing the DMA, but sometimes hardware is broken and will + * tag the DMA as being sourced from a different device. This function + * allows that translation. Note that the reference count of the + * returned device is incremented on all paths. + */ +struct pci_dev *pci_get_dma_source(struct pci_dev *dev) +{ + const struct pci_dev_dma_source *i; + + for (i = pci_dev_dma_source; i->dma_source; i++) { + if ((i->vendor == dev->vendor || + i->vendor == (u16)PCI_ANY_ID) && + (i->device == dev->device || + i->device == (u16)PCI_ANY_ID)) + return i->dma_source(dev); + } + + return pci_dev_get(dev); +} diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..39983be7b25b 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1332,6 +1332,9 @@ static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus, static inline int pci_domain_nr(struct pci_bus *bus) { return 0; } +static inline struct pci_dev *pci_dev_get(struct pci_dev *dev) +{ return NULL; } + #define dev_is_pci(d) (false) #define dev_is_pf(d) (false) #define dev_num_vf(d) (0) @@ -1486,9 +1489,14 @@ enum pci_fixup_pass { #ifdef CONFIG_PCI_QUIRKS void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); +struct pci_dev *pci_get_dma_source(struct pci_dev *dev); #else static inline void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) {} +static inline struct pci_dev *pci_get_dma_source(struct pci_dev *dev) +{ + return pci_dev_get(dev); +} #endif void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen); -- cgit v1.2.3-59-g8ed1b From c32823f82b42abc1f08b365085862fd1d57c0b61 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Fri, 1 Jun 2012 15:16:25 -0600 Subject: PCI: make pci_ltr_supported() static The PCI Express Latency Tolerance Reporting (LTR) feature's pci_ltr_supported() routine is currently only used within drivers/pci/pci.c so make it static. Acked-by: Donald Dutile Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 3 +-- include/linux/pci.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..847e0c35cdb7 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2169,7 +2169,7 @@ EXPORT_SYMBOL(pci_disable_obff); * RETURNS: * True if @dev supports latency tolerance reporting, false otherwise. */ -bool pci_ltr_supported(struct pci_dev *dev) +static bool pci_ltr_supported(struct pci_dev *dev) { int pos; u32 cap; @@ -2185,7 +2185,6 @@ bool pci_ltr_supported(struct pci_dev *dev) return cap & PCI_EXP_DEVCAP2_LTR; } -EXPORT_SYMBOL(pci_ltr_supported); /** * pci_enable_ltr - enable latency tolerance reporting diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..b2bec26b7f0a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -875,7 +875,6 @@ enum pci_obff_signal_type { int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type); void pci_disable_obff(struct pci_dev *dev); -bool pci_ltr_supported(struct pci_dev *dev); int pci_enable_ltr(struct pci_dev *dev); void pci_disable_ltr(struct pci_dev *dev); int pci_set_ltr(struct pci_dev *dev, int snoop_lat_ns, int nosnoop_lat_ns); -- cgit v1.2.3-59-g8ed1b From c463b8cb9350cf1230cefe467a1cf279140a5437 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Fri, 1 Jun 2012 15:16:37 -0600 Subject: PCI: add pci_pcie_cap2() check for PCIe feature capabilities >= v2 This patch resolves potential issues when accessing PCI Express Capability structures. The makeup of the capability varies substantially between v1 and v2: Version 1 of the PCI Express Capability (defined by PCI Express 1.0 and 1.1 base) neither requires the endpoint to implement the entire PCIe capability structure nor specifies default values of registers that are not implemented by the device. Version 2 of the PCI Express Capability (defined by PCIe 1.1 Capability Structure Expansion ECN, PCIe 2.0, 2.1, and 3.0) added additional registers to the structure and requires all registers to be either implemented or hardwired to 0. Due to the differences in the capability structures, code dealing with capability features must be careful not to access the additional registers introduced with v2 unless the device is specifically known to be a v2 capable device. Otherwise, attempts to access non-existant registers will occur. This is a subtle issue that is hard to track down when it occurs (and it has - see commit 864d296cf94). To try and help mitigate such occurrences, this patch introduces pci_pcie_cap2() which is similar to pci_pcie_cap() but also checks that the PCIe capability version is >= 2. pci_pcie_cap2() should be used for qualifying PCIe capability features introduced after v1. Suggested by Don Dutile. Acked-by: Donald Dutile Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 65 +++++++++++++++++++++++++++++++++++++----------- include/linux/pci_regs.h | 6 +++++ 2 files changed, 56 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 766bb13bb0a3..985df63aa59f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -277,6 +277,38 @@ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap) return pos; } +/** + * pci_pcie_cap2 - query for devices' PCI_CAP_ID_EXP v2 capability structure + * @dev: PCI device to check + * + * Like pci_pcie_cap() but also checks that the PCIe capability version is + * >= 2. Note that v1 capability structures could be sparse in that not + * all register fields were required. v2 requires the entire structure to + * be present size wise, while still allowing for non-implemented registers + * to exist but they must be hardwired to 0. + * + * Due to the differences in the versions of capability structures, one + * must be careful not to try and access non-existant registers that may + * exist in early versions - v1 - of Express devices. + * + * Returns the offset of the PCIe capability structure as long as the + * capability version is >= 2; otherwise 0 is returned. + */ +static int pci_pcie_cap2(struct pci_dev *dev) +{ + u16 flags; + int pos; + + pos = pci_pcie_cap(dev); + if (pos) { + pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &flags); + if ((flags & PCI_EXP_FLAGS_VERS) < 2) + pos = 0; + } + + return pos; +} + /** * pci_find_ext_capability - Find an extended capability * @dev: PCI device to query @@ -1983,7 +2015,7 @@ void pci_enable_ari(struct pci_dev *dev) { int pos; u32 cap; - u16 flags, ctrl; + u16 ctrl; struct pci_dev *bridge; if (pcie_ari_disabled || !pci_is_pcie(dev) || dev->devfn) @@ -1997,15 +2029,11 @@ void pci_enable_ari(struct pci_dev *dev) if (!bridge) return; - pos = pci_pcie_cap(bridge); + /* ARI is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(bridge); if (!pos) return; - /* ARI is a PCIe v2 feature */ - pci_read_config_word(bridge, pos + PCI_EXP_FLAGS, &flags); - if ((flags & PCI_EXP_FLAGS_VERS) < 2) - return; - pci_read_config_dword(bridge, pos + PCI_EXP_DEVCAP2, &cap); if (!(cap & PCI_EXP_DEVCAP2_ARI)) return; @@ -2018,7 +2046,7 @@ void pci_enable_ari(struct pci_dev *dev) } /** - * pci_enable_ido - enable ID-based ordering on a device + * pci_enable_ido - enable ID-based Ordering on a device * @dev: the PCI device * @type: which types of IDO to enable * @@ -2031,7 +2059,8 @@ void pci_enable_ido(struct pci_dev *dev, unsigned long type) int pos; u16 ctrl; - pos = pci_pcie_cap(dev); + /* ID-based Ordering is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; @@ -2054,7 +2083,8 @@ void pci_disable_ido(struct pci_dev *dev, unsigned long type) int pos; u16 ctrl; - pos = pci_pcie_cap(dev); + /* ID-based Ordering is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; @@ -2093,7 +2123,8 @@ int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type) u16 ctrl; int ret; - pos = pci_pcie_cap(dev); + /* OBFF is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return -ENOTSUPP; @@ -2143,7 +2174,8 @@ void pci_disable_obff(struct pci_dev *dev) int pos; u16 ctrl; - pos = pci_pcie_cap(dev); + /* OBFF is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; @@ -2165,7 +2197,8 @@ static bool pci_ltr_supported(struct pci_dev *dev) int pos; u32 cap; - pos = pci_pcie_cap(dev); + /* LTR is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return false; @@ -2193,7 +2226,8 @@ int pci_enable_ltr(struct pci_dev *dev) if (!pci_ltr_supported(dev)) return -ENOTSUPP; - pos = pci_pcie_cap(dev); + /* LTR is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return -ENOTSUPP; @@ -2228,7 +2262,8 @@ void pci_disable_ltr(struct pci_dev *dev) if (!pci_ltr_supported(dev)) return; - pos = pci_pcie_cap(dev); + /* LTR is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 4b608f543412..e7642f5bb12a 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -507,6 +507,12 @@ #define PCI_EXP_RTSTA 32 /* Root Status */ #define PCI_EXP_RTSTA_PME 0x10000 /* PME status */ #define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */ +/* + * Note that the following PCI Express 'Capability Structure' registers + * were introduced with 'Capability Version' 0x2 (v2). These registers + * do not exist on devices with Capability Version 1. Use pci_pcie_cap2() + * to use these fields safely. + */ #define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ #define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ #define PCI_EXP_DEVCAP2_LTR 0x800 /* Latency tolerance reporting */ -- cgit v1.2.3-59-g8ed1b From f242e50eee1ec7692c4854d94e8cd543991cce71 Mon Sep 17 00:00:00 2001 From: Ola Lilja Date: Thu, 7 Jun 2012 14:00:46 +0200 Subject: mfd/ab8500: Move platform-data for ab8500-codec into mfd-driver The platform-data used by the Ux500 ASoC-driver is moved from the machine-driver context into the codec-driver context. This means adding the platform-data for 'ab8500-codec' into the main AB8500 platform-data. Signed-off-by: Ola Lilja Signed-off-by: Mark Brown --- arch/arm/mach-ux500/board-mop500.c | 14 +++++++++ include/linux/mfd/abx500/ab8500-codec.h | 52 +++++++++++++++++++++++++++++++++ include/linux/mfd/abx500/ab8500.h | 2 ++ 3 files changed, 68 insertions(+) create mode 100644 include/linux/mfd/abx500/ab8500-codec.h (limited to 'include/linux') diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 9c74ac545849..c8a8fde777bb 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -97,6 +98,18 @@ static struct ab8500_gpio_platform_data ab8500_gpio_pdata = { 0x7A, 0x00, 0x00}, }; +/* ab8500-codec */ +static struct ab8500_codec_platform_data ab8500_codec_pdata = { + .amics = { + .mic1_type = AMIC_TYPE_DIFFERENTIAL, + .mic2_type = AMIC_TYPE_DIFFERENTIAL, + .mic1a_micbias = AMIC_MICBIAS_VAMIC1, + .mic1b_micbias = AMIC_MICBIAS_VAMIC1, + .mic2_micbias = AMIC_MICBIAS_VAMIC2 + }, + .ear_cmv = EAR_CMV_0_95V +}; + static struct gpio_keys_button snowball_key_array[] = { { .gpio = 32, @@ -195,6 +208,7 @@ static struct ab8500_platform_data ab8500_platdata = { .regulator = ab8500_regulators, .num_regulator = ARRAY_SIZE(ab8500_regulators), .gpio = &ab8500_gpio_pdata, + .codec = &ab8500_codec_pdata, }; static struct resource ab8500_resources[] = { diff --git a/include/linux/mfd/abx500/ab8500-codec.h b/include/linux/mfd/abx500/ab8500-codec.h new file mode 100644 index 000000000000..dc6529202cdd --- /dev/null +++ b/include/linux/mfd/abx500/ab8500-codec.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) ST-Ericsson SA 2012 + * + * Author: Ola Lilja + * for ST-Ericsson. + * + * License terms: + * + * 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 AB8500_CORE_CODEC_H +#define AB8500_CORE_CODEC_H + +/* Mic-types */ +enum amic_type { + AMIC_TYPE_SINGLE_ENDED, + AMIC_TYPE_DIFFERENTIAL +}; + +/* Mic-biases */ +enum amic_micbias { + AMIC_MICBIAS_VAMIC1, + AMIC_MICBIAS_VAMIC2 +}; + +/* Bias-voltage */ +enum ear_cm_voltage { + EAR_CMV_0_95V, + EAR_CMV_1_10V, + EAR_CMV_1_27V, + EAR_CMV_1_58V +}; + +/* Analog microphone settings */ +struct amic_settings { + enum amic_type mic1_type; + enum amic_type mic2_type; + enum amic_micbias mic1a_micbias; + enum amic_micbias mic1b_micbias; + enum amic_micbias mic2_micbias; +}; + +/* Platform data structure for the audio-parts of the AB8500 */ +struct ab8500_codec_platform_data { + struct amic_settings amics; + enum ear_cm_voltage ear_cmv; +}; + +#endif diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 91dd3ef63e99..bc9b84b60ec6 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -266,6 +266,7 @@ struct ab8500 { struct regulator_reg_init; struct regulator_init_data; struct ab8500_gpio_platform_data; +struct ab8500_codec_platform_data; /** * struct ab8500_platform_data - AB8500 platform data @@ -284,6 +285,7 @@ struct ab8500_platform_data { int num_regulator; struct regulator_init_data *regulator; struct ab8500_gpio_platform_data *gpio; + struct ab8500_codec_platform_data *codec; }; extern int __devinit ab8500_init(struct ab8500 *ab8500, -- cgit v1.2.3-59-g8ed1b From 535588e61a007416f46cf08b4ccb6cc73b3f6fb0 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Mon, 11 Jun 2012 18:14:16 -0700 Subject: ieee80211: definitions for Microsoft Vendor OUI and WPA OUI type Reference: http://standards.ieee.org/develop/regauth/oui/oui.txt Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index f831078182de..98c86ff657bb 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1589,6 +1589,8 @@ enum ieee80211_sa_query_action { #define WLAN_OUI_WFA 0x506f9a #define WLAN_OUI_TYPE_WFA_P2P 9 +#define WLAN_OUI_MICROSOFT 0x0050f2 +#define WLAN_OUI_TYPE_MICROSOFT_WPA 1 /* * WMM/802.11e Tspec Element -- cgit v1.2.3-59-g8ed1b From ad805758c0eb25bce7b2e3b298d63dc62a1bc71c Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 11 Jun 2012 05:27:07 +0000 Subject: PCI: add ACS validation utility In a PCI environment, transactions aren't always required to reach the root bus before being re-routed. Intermediate switches between an endpoint and the root bus can redirect DMA back downstream before things like IOMMUs have a chance to intervene. Legacy PCI is always susceptible to this as it operates on a shared bus. PCIe added a new capability to describe and control this behavior, Access Control Services, or ACS. The utility function pci_acs_enabled() allows us to test the ACS capabilities of an individual devices against a set of flags while pci_acs_path_enabled() tests a complete path from a given downstream device up to the specified upstream device. We also include the ability to add device specific tests as it's likely we'll see devices that do not implement ACS, but want to indicate support for various capabilities in this space. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/quirks.c | 33 +++++++++++++++++++++++++ include/linux/pci.h | 10 +++++++- 3 files changed, 111 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..1ccf7d49f522 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2359,6 +2359,75 @@ void pci_enable_acs(struct pci_dev *dev) pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); } +/** + * pci_acs_enabled - test ACS against required flags for a given device + * @pdev: device to test + * @acs_flags: required PCI ACS flags + * + * Return true if the device supports the provided flags. Automatically + * filters out flags that are not implemented on multifunction devices. + */ +bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags) +{ + int pos, ret; + u16 ctrl; + + ret = pci_dev_specific_acs_enabled(pdev, acs_flags); + if (ret >= 0) + return ret > 0; + + if (!pci_is_pcie(pdev)) + return false; + + /* Filter out flags not applicable to multifunction */ + if (pdev->multifunction) + acs_flags &= (PCI_ACS_RR | PCI_ACS_CR | + PCI_ACS_EC | PCI_ACS_DT); + + if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM || + pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT || + pdev->multifunction) { + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS); + if (!pos) + return false; + + pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl); + if ((ctrl & acs_flags) != acs_flags) + return false; + } + + return true; +} + +/** + * pci_acs_path_enable - test ACS flags from start to end in a hierarchy + * @start: starting downstream device + * @end: ending upstream device or NULL to search to the root bus + * @acs_flags: required flags + * + * Walk up a device tree from start to end testing PCI ACS support. If + * any step along the way does not support the required flags, return false. + */ +bool pci_acs_path_enabled(struct pci_dev *start, + struct pci_dev *end, u16 acs_flags) +{ + struct pci_dev *pdev, *parent = start; + + do { + pdev = parent; + + if (!pci_acs_enabled(pdev, acs_flags)) + return false; + + if (pci_is_root_bus(pdev->bus)) + return (end == NULL); + + parent = pdev->bus->self; + } while (pdev != end); + + return true; +} + /** * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge * @dev: the PCI device diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index acd3956b44bd..27e2c8f4ec73 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3230,3 +3230,36 @@ struct pci_dev *pci_get_dma_source(struct pci_dev *dev) return pci_dev_get(dev); } + +static const struct pci_dev_acs_enabled { + u16 vendor; + u16 device; + int (*acs_enabled)(struct pci_dev *dev, u16 acs_flags); +} pci_dev_acs_enabled[] = { + { 0 } +}; + +int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags) +{ + const struct pci_dev_acs_enabled *i; + int ret; + + /* + * Allow devices that do not expose standard PCIe ACS capabilities + * or control to indicate their support here. Multi-function express + * devices which do not allow internal peer-to-peer between functions, + * but do not implement PCIe ACS may wish to return true here. + */ + for (i = pci_dev_acs_enabled; i->acs_enabled; i++) { + if ((i->vendor == dev->vendor || + i->vendor == (u16)PCI_ANY_ID) && + (i->device == dev->device || + i->device == (u16)PCI_ANY_ID)) { + ret = i->acs_enabled(dev, acs_flags); + if (ret >= 0) + return ret; + } + } + + return -ENOTTY; +} diff --git a/include/linux/pci.h b/include/linux/pci.h index 39983be7b25b..dd7af0f37b3a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1490,6 +1490,7 @@ enum pci_fixup_pass { #ifdef CONFIG_PCI_QUIRKS void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); struct pci_dev *pci_get_dma_source(struct pci_dev *dev); +int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags); #else static inline void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) {} @@ -1497,6 +1498,11 @@ static inline struct pci_dev *pci_get_dma_source(struct pci_dev *dev) { return pci_dev_get(dev); } +static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev, + u16 acs_flags) +{ + return -ENOTTY; +} #endif void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen); @@ -1599,7 +1605,9 @@ static inline bool pci_is_pcie(struct pci_dev *dev) } void pci_request_acs(void); - +bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags); +bool pci_acs_path_enabled(struct pci_dev *start, + struct pci_dev *end, u16 acs_flags); #define PCI_VPD_LRDT 0x80 /* Large Resource Data Type */ #define PCI_VPD_LRDT_ID(x) (x | PCI_VPD_LRDT) -- cgit v1.2.3-59-g8ed1b From c63587d7f5b9db84e71daf5962dc0394eb657da2 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 11 Jun 2012 05:27:19 +0000 Subject: PCI: export pci_user functions for use by other drivers VFIO PCI support will make use of these for user-initiated PCI config accesses. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/access.c | 6 ++++-- drivers/pci/pci.h | 7 ------- include/linux/pci.h | 8 ++++++++ 3 files changed, 12 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 2a581642c237..ba91a7e17519 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -162,7 +162,8 @@ int pci_user_read_config_##size \ if (ret > 0) \ ret = -EINVAL; \ return ret; \ -} +} \ +EXPORT_SYMBOL_GPL(pci_user_read_config_##size); /* Returns 0 on success, negative values indicate error. */ #define PCI_USER_WRITE_CONFIG(size,type) \ @@ -181,7 +182,8 @@ int pci_user_write_config_##size \ if (ret > 0) \ ret = -EINVAL; \ return ret; \ -} +} \ +EXPORT_SYMBOL_GPL(pci_user_write_config_##size); PCI_USER_READ_CONFIG(byte, u8) PCI_USER_READ_CONFIG(word, u16) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e4943479b234..f2dcc46bdece 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -86,13 +86,6 @@ static inline bool pci_is_bridge(struct pci_dev *pci_dev) return !!(pci_dev->subordinate); } -extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); -extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); -extern int pci_user_read_config_dword(struct pci_dev *dev, int where, u32 *val); -extern int pci_user_write_config_byte(struct pci_dev *dev, int where, u8 val); -extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val); -extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); - struct pci_vpd_ops { ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); diff --git a/include/linux/pci.h b/include/linux/pci.h index dd7af0f37b3a..9d04599c6bd9 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -777,6 +777,14 @@ static inline int pci_write_config_dword(const struct pci_dev *dev, int where, return pci_bus_write_config_dword(dev->bus, dev->devfn, where, val); } +/* user-space driven config access */ +int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); +int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); +int pci_user_read_config_dword(struct pci_dev *dev, int where, u32 *val); +int pci_user_write_config_byte(struct pci_dev *dev, int where, u8 val); +int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val); +int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); + int __must_check pci_enable_device(struct pci_dev *dev); int __must_check pci_enable_device_io(struct pci_dev *dev); int __must_check pci_enable_device_mem(struct pci_dev *dev); -- cgit v1.2.3-59-g8ed1b From a6961651408afa9387d6df43c4a1dc4fd35dcb1b Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 11 Jun 2012 05:27:33 +0000 Subject: PCI: create common pcibios_err_to_errno For returning errors out to non-PCI code. Re-name xen's version. Acked-by: Konrad Rzeszutek Wilk Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/xen/xen-pciback/conf_space.c | 6 +++--- include/linux/pci.h | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/xen/xen-pciback/conf_space.c b/drivers/xen/xen-pciback/conf_space.c index 30d7be026c18..46ae0f9f02ad 100644 --- a/drivers/xen/xen-pciback/conf_space.c +++ b/drivers/xen/xen-pciback/conf_space.c @@ -124,7 +124,7 @@ static inline u32 merge_value(u32 val, u32 new_val, u32 new_val_mask, return val; } -static int pcibios_err_to_errno(int err) +static int xen_pcibios_err_to_errno(int err) { switch (err) { case PCIBIOS_SUCCESSFUL: @@ -202,7 +202,7 @@ out: pci_name(dev), size, offset, value); *ret_val = value; - return pcibios_err_to_errno(err); + return xen_pcibios_err_to_errno(err); } int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value) @@ -290,7 +290,7 @@ int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value) } } - return pcibios_err_to_errno(err); + return xen_pcibios_err_to_errno(err); } void xen_pcibk_config_free_dyn_fields(struct pci_dev *dev) diff --git a/include/linux/pci.h b/include/linux/pci.h index 9d04599c6bd9..a691f62bcf89 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -474,6 +474,32 @@ static inline bool pci_dev_msi_enabled(struct pci_dev *pci_dev) { return false; #define PCIBIOS_SET_FAILED 0x88 #define PCIBIOS_BUFFER_TOO_SMALL 0x89 +/* + * Translate above to generic errno for passing back through non-pci. + */ +static inline int pcibios_err_to_errno(int err) +{ + if (err <= PCIBIOS_SUCCESSFUL) + return err; /* Assume already errno */ + + switch (err) { + case PCIBIOS_FUNC_NOT_SUPPORTED: + return -ENOENT; + case PCIBIOS_BAD_VENDOR_ID: + return -EINVAL; + case PCIBIOS_DEVICE_NOT_FOUND: + return -ENODEV; + case PCIBIOS_BAD_REGISTER_NUMBER: + return -EFAULT; + case PCIBIOS_SET_FAILED: + return -EIO; + case PCIBIOS_BUFFER_TOO_SMALL: + return -ENOSPC; + } + + return -ENOTTY; +} + /* Low-level architecture-dependent routines */ struct pci_ops { -- cgit v1.2.3-59-g8ed1b From a0dee2ed0cdc666b5622f1fc74979355a6b36850 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 11 Jun 2012 05:27:45 +0000 Subject: PCI: misc pci_reg additions Fill in many missing definitions and add sizeof fields for many sections allowing for more extensive config parsing. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- include/linux/pci_regs.h | 113 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 4b608f543412..526d2c4bc3a6 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -26,6 +26,7 @@ * Under PCI, each device has 256 bytes of configuration address space, * of which the first 64 bytes are standardized as follows: */ +#define PCI_STD_HEADER_SIZEOF 64 #define PCI_VENDOR_ID 0x00 /* 16 bits */ #define PCI_DEVICE_ID 0x02 /* 16 bits */ #define PCI_COMMAND 0x04 /* 16 bits */ @@ -209,9 +210,12 @@ #define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ #define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */ #define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ +#define PCI_CAP_ID_SECDEV 0x0F /* Secure Device */ #define PCI_CAP_ID_EXP 0x10 /* PCI Express */ #define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ +#define PCI_CAP_ID_SATA 0x12 /* SATA Data/Index Conf. */ #define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */ +#define PCI_CAP_ID_MAX PCI_CAP_ID_AF #define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ #define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ #define PCI_CAP_SIZEOF 4 @@ -276,6 +280,7 @@ #define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ #define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ #define PCI_VPD_DATA 4 /* 32-bits of data returned here */ +#define PCI_CAP_VPD_SIZEOF 8 /* Slot Identification */ @@ -297,8 +302,10 @@ #define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ #define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ #define PCI_MSI_MASK_32 12 /* Mask bits register for 32-bit devices */ +#define PCI_MSI_PENDING_32 16 /* Pending intrs for 32-bit devices */ #define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ #define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */ +#define PCI_MSI_PENDING_64 20 /* Pending intrs for 64-bit devices */ /* MSI-X registers */ #define PCI_MSIX_FLAGS 2 @@ -308,6 +315,7 @@ #define PCI_MSIX_TABLE 4 #define PCI_MSIX_PBA 8 #define PCI_MSIX_FLAGS_BIRMASK (7 << 0) +#define PCI_CAP_MSIX_SIZEOF 12 /* size of MSIX registers */ /* MSI-X entry's format */ #define PCI_MSIX_ENTRY_SIZE 16 @@ -338,6 +346,7 @@ #define PCI_AF_CTRL_FLR 0x01 #define PCI_AF_STATUS 5 #define PCI_AF_STATUS_TP 0x01 +#define PCI_CAP_AF_SIZEOF 6 /* size of AF registers */ /* PCI-X registers */ @@ -374,6 +383,10 @@ #define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */ #define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ #define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ +#define PCI_X_ECC_CSR 8 /* ECC control and status */ +#define PCI_CAP_PCIX_SIZEOF_V0 8 /* size of registers for Version 0 */ +#define PCI_CAP_PCIX_SIZEOF_V1 24 /* size for Version 1 */ +#define PCI_CAP_PCIX_SIZEOF_V2 PCI_CAP_PCIX_SIZEOF_V1 /* Same for v2 */ /* PCI Bridge Subsystem ID registers */ @@ -462,6 +475,7 @@ #define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */ #define PCI_EXP_LNKSTA_LBMS 0x4000 /* Link Bandwidth Management Status */ #define PCI_EXP_LNKSTA_LABS 0x8000 /* Link Autonomous Bandwidth Status */ +#define PCI_CAP_EXP_ENDPOINT_SIZEOF_V1 20 /* v1 endpoints end here */ #define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ #define PCI_EXP_SLTCAP_ABP 0x00000001 /* Attention Button Present */ #define PCI_EXP_SLTCAP_PCP 0x00000002 /* Power Controller Present */ @@ -521,6 +535,7 @@ #define PCI_EXP_OBFF_MSGA_EN 0x2000 /* OBFF enable with Message type A */ #define PCI_EXP_OBFF_MSGB_EN 0x4000 /* OBFF enable with Message type B */ #define PCI_EXP_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */ +#define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 44 /* v2 endpoints end here */ #define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ #define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */ @@ -529,23 +544,43 @@ #define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf) #define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc) -#define PCI_EXT_CAP_ID_ERR 1 -#define PCI_EXT_CAP_ID_VC 2 -#define PCI_EXT_CAP_ID_DSN 3 -#define PCI_EXT_CAP_ID_PWR 4 -#define PCI_EXT_CAP_ID_VNDR 11 -#define PCI_EXT_CAP_ID_ACS 13 -#define PCI_EXT_CAP_ID_ARI 14 -#define PCI_EXT_CAP_ID_ATS 15 -#define PCI_EXT_CAP_ID_SRIOV 16 -#define PCI_EXT_CAP_ID_PRI 19 -#define PCI_EXT_CAP_ID_LTR 24 -#define PCI_EXT_CAP_ID_PASID 27 +#define PCI_EXT_CAP_ID_ERR 0x01 /* Advanced Error Reporting */ +#define PCI_EXT_CAP_ID_VC 0x02 /* Virtual Channel Capability */ +#define PCI_EXT_CAP_ID_DSN 0x03 /* Device Serial Number */ +#define PCI_EXT_CAP_ID_PWR 0x04 /* Power Budgeting */ +#define PCI_EXT_CAP_ID_RCLD 0x05 /* Root Complex Link Declaration */ +#define PCI_EXT_CAP_ID_RCILC 0x06 /* Root Complex Internal Link Control */ +#define PCI_EXT_CAP_ID_RCEC 0x07 /* Root Complex Event Collector */ +#define PCI_EXT_CAP_ID_MFVC 0x08 /* Multi-Function VC Capability */ +#define PCI_EXT_CAP_ID_VC9 0x09 /* same as _VC */ +#define PCI_EXT_CAP_ID_RCRB 0x0A /* Root Complex RB? */ +#define PCI_EXT_CAP_ID_VNDR 0x0B /* Vendor Specific */ +#define PCI_EXT_CAP_ID_CAC 0x0C /* Config Access - obsolete */ +#define PCI_EXT_CAP_ID_ACS 0x0D /* Access Control Services */ +#define PCI_EXT_CAP_ID_ARI 0x0E /* Alternate Routing ID */ +#define PCI_EXT_CAP_ID_ATS 0x0F /* Address Translation Services */ +#define PCI_EXT_CAP_ID_SRIOV 0x10 /* Single Root I/O Virtualization */ +#define PCI_EXT_CAP_ID_MRIOV 0x11 /* Multi Root I/O Virtualization */ +#define PCI_EXT_CAP_ID_MCAST 0x12 /* Multicast */ +#define PCI_EXT_CAP_ID_PRI 0x13 /* Page Request Interface */ +#define PCI_EXT_CAP_ID_AMD_XXX 0x14 /* reserved for AMD */ +#define PCI_EXT_CAP_ID_REBAR 0x15 /* resizable BAR */ +#define PCI_EXT_CAP_ID_DPA 0x16 /* dynamic power alloc */ +#define PCI_EXT_CAP_ID_TPH 0x17 /* TPH request */ +#define PCI_EXT_CAP_ID_LTR 0x18 /* latency tolerance reporting */ +#define PCI_EXT_CAP_ID_SECPCI 0x19 /* Secondary PCIe */ +#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */ +#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */ +#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PASID + +#define PCI_EXT_CAP_DSN_SIZEOF 12 +#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40 /* Advanced Error Reporting */ #define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ #define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */ #define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ +#define PCI_ERR_UNC_SURPDN 0x00000020 /* Surprise Down */ #define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ #define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ #define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ @@ -555,6 +590,11 @@ #define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ #define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ #define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ +#define PCI_ERR_UNC_ACSV 0x00200000 /* ACS Violation */ +#define PCI_ERR_UNC_INTN 0x00400000 /* internal error */ +#define PCI_ERR_UNC_MCBTLP 0x00800000 /* MC blocked TLP */ +#define PCI_ERR_UNC_ATOMEG 0x01000000 /* Atomic egress blocked */ +#define PCI_ERR_UNC_TLPPRE 0x02000000 /* TLP prefix blocked */ #define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ /* Same bits as above */ #define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ @@ -565,6 +605,9 @@ #define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ #define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ #define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ +#define PCI_ERR_COR_ADV_NFAT 0x00002000 /* Advisory Non-Fatal */ +#define PCI_ERR_COR_INTERNAL 0x00004000 /* Corrected Internal */ +#define PCI_ERR_COR_LOG_OVER 0x00008000 /* Header Log Overflow */ #define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ /* Same bits as above */ #define PCI_ERR_CAP 24 /* Advanced Error Capabilities */ @@ -596,12 +639,18 @@ /* Virtual Channel */ #define PCI_VC_PORT_REG1 4 +#define PCI_VC_REG1_EVCC 0x7 /* extended vc count */ #define PCI_VC_PORT_REG2 8 +#define PCI_VC_REG2_32_PHASE 0x2 +#define PCI_VC_REG2_64_PHASE 0x4 +#define PCI_VC_REG2_128_PHASE 0x8 #define PCI_VC_PORT_CTRL 12 #define PCI_VC_PORT_STATUS 14 #define PCI_VC_RES_CAP 16 #define PCI_VC_RES_CTRL 20 #define PCI_VC_RES_STATUS 26 +#define PCI_CAP_VC_BASE_SIZEOF 0x10 +#define PCI_CAP_VC_PER_VC_SIZEOF 0x0C /* Power Budgeting */ #define PCI_PWR_DSR 4 /* Data Select Register */ @@ -614,6 +663,7 @@ #define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */ #define PCI_PWR_CAP 12 /* Capability */ #define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ +#define PCI_EXT_CAP_PWR_SIZEOF 16 /* * Hypertransport sub capability types @@ -646,6 +696,8 @@ #define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */ #define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */ #define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */ +#define HT_CAP_SIZEOF_LONG 28 /* slave & primary */ +#define HT_CAP_SIZEOF_SHORT 24 /* host & secondary */ /* Alternative Routing-ID Interpretation */ #define PCI_ARI_CAP 0x04 /* ARI Capability Register */ @@ -656,6 +708,7 @@ #define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */ #define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */ #define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */ +#define PCI_EXT_CAP_ARI_SIZEOF 8 /* Address Translation Service */ #define PCI_ATS_CAP 0x04 /* ATS Capability Register */ @@ -665,6 +718,7 @@ #define PCI_ATS_CTRL_ENABLE 0x8000 /* ATS Enable */ #define PCI_ATS_CTRL_STU(x) ((x) & 0x1f) /* Smallest Translation Unit */ #define PCI_ATS_MIN_STU 12 /* shift of minimum STU block */ +#define PCI_EXT_CAP_ATS_SIZEOF 8 /* Page Request Interface */ #define PCI_PRI_CTRL 0x04 /* PRI control register */ @@ -676,6 +730,7 @@ #define PCI_PRI_STATUS_STOPPED 0x100 /* PRI Stopped */ #define PCI_PRI_MAX_REQ 0x08 /* PRI max reqs supported */ #define PCI_PRI_ALLOC_REQ 0x0c /* PRI max reqs allowed */ +#define PCI_EXT_CAP_PRI_SIZEOF 16 /* PASID capability */ #define PCI_PASID_CAP 0x04 /* PASID feature register */ @@ -685,6 +740,7 @@ #define PCI_PASID_CTRL_ENABLE 0x01 /* Enable bit */ #define PCI_PASID_CTRL_EXEC 0x02 /* Exec permissions Enable */ #define PCI_PASID_CTRL_PRIV 0x04 /* Priviledge Mode Enable */ +#define PCI_EXT_CAP_PASID_SIZEOF 8 /* Single Root I/O Virtualization */ #define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */ @@ -716,12 +772,14 @@ #define PCI_SRIOV_VFM_MI 0x1 /* Dormant.MigrateIn */ #define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */ #define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */ +#define PCI_EXT_CAP_SRIOV_SIZEOF 64 #define PCI_LTR_MAX_SNOOP_LAT 0x4 #define PCI_LTR_MAX_NOSNOOP_LAT 0x6 #define PCI_LTR_VALUE_MASK 0x000003ff #define PCI_LTR_SCALE_MASK 0x00001c00 #define PCI_LTR_SCALE_SHIFT 10 +#define PCI_EXT_CAP_LTR_SIZEOF 8 /* Access Control Service */ #define PCI_ACS_CAP 0x04 /* ACS Capability Register */ @@ -732,7 +790,38 @@ #define PCI_ACS_UF 0x10 /* Upstream Forwarding */ #define PCI_ACS_EC 0x20 /* P2P Egress Control */ #define PCI_ACS_DT 0x40 /* Direct Translated P2P */ +#define PCI_ACS_EGRESS_BITS 0x05 /* ACS Egress Control Vector Size */ #define PCI_ACS_CTRL 0x06 /* ACS Control Register */ #define PCI_ACS_EGRESS_CTL_V 0x08 /* ACS Egress Control Vector */ +#define PCI_VSEC_HDR 4 /* extended cap - vendor specific */ +#define PCI_VSEC_HDR_LEN_SHIFT 20 /* shift for length field */ + +/* sata capability */ +#define PCI_SATA_REGS 4 /* SATA REGs specifier */ +#define PCI_SATA_REGS_MASK 0xF /* location - BAR#/inline */ +#define PCI_SATA_REGS_INLINE 0xF /* REGS in config space */ +#define PCI_SATA_SIZEOF_SHORT 8 +#define PCI_SATA_SIZEOF_LONG 16 + +/* resizable BARs */ +#define PCI_REBAR_CTRL 8 /* control register */ +#define PCI_REBAR_CTRL_NBAR_MASK (7 << 5) /* mask for # bars */ +#define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # bars */ + +/* dynamic power allocation */ +#define PCI_DPA_CAP 4 /* capability register */ +#define PCI_DPA_CAP_SUBSTATE_MASK 0x1F /* # substates - 1 */ +#define PCI_DPA_BASE_SIZEOF 16 /* size with 0 substates */ + +/* TPH Requester */ +#define PCI_TPH_CAP 4 /* capability register */ +#define PCI_TPH_CAP_LOC_MASK 0x600 /* location mask */ +#define PCI_TPH_LOC_NONE 0x000 /* no location */ +#define PCI_TPH_LOC_CAP 0x200 /* in capability */ +#define PCI_TPH_LOC_MSIX 0x400 /* in MSI-X */ +#define PCI_TPH_CAP_ST_MASK 0x07FF0000 /* st table mask */ +#define PCI_TPH_CAP_ST_SHIFT 16 /* st table shift */ +#define PCI_TPH_BASE_SIZEOF 12 /* size with no st table */ + #endif /* LINUX_PCI_REGS_H */ -- cgit v1.2.3-59-g8ed1b From d0daebc3d622f95db181601cb0c4a0781f74f758 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 12 Jun 2012 00:44:01 +0000 Subject: ipv4: Add interface option to enable routing of 127.0.0.0/8 Routing of 127/8 is tradtionally forbidden, we consider packets from that address block martian when routing and do not process corresponding ARP requests. This is a sane default but renders a huge address space practically unuseable. The RFC states that no address within the 127/8 block should ever appear on any network anywhere but it does not forbid the use of such addresses outside of the loopback device in particular. For example to address a pool of virtual guests behind a load balancer. This patch adds a new interface option 'route_localnet' enabling routing of the 127/8 address block and processing of ARP requests on a specific interface. Note that for the feature to work, the default local route covering 127/8 dev lo needs to be removed. Example: $ sysctl -w net.ipv4.conf.eth0.route_localnet=1 $ ip route del 127.0.0.0/8 dev lo table local $ ip addr add 127.1.0.1/16 dev eth0 $ ip route flush cache V2: Fix invalid check to auto flush cache (thanks davem) Signed-off-by: Thomas Graf Acked-by: Neil Horman Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 5 +++++ include/linux/inetdevice.h | 2 ++ net/ipv4/arp.c | 3 ++- net/ipv4/devinet.c | 5 ++++- net/ipv4/route.c | 30 +++++++++++++++++++++--------- 5 files changed, 34 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 6f896b94abdc..99d0e0504d6e 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -862,6 +862,11 @@ accept_local - BOOLEAN local interfaces over the wire and have them accepted properly. default FALSE +route_localnet - BOOLEAN + Do not consider loopback addresses as martian source or destination + while routing. This enables the use of 127/8 for local routing purposes. + default FALSE + rp_filter - INTEGER 0 - No source validation. 1 - Strict mode as defined in RFC3704 Strict Reverse Path diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 597f4a9f3240..67f9ddacb70c 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -38,6 +38,7 @@ enum IPV4_DEVCONF_ACCEPT_LOCAL, IPV4_DEVCONF_SRC_VMARK, IPV4_DEVCONF_PROXY_ARP_PVLAN, + IPV4_DEVCONF_ROUTE_LOCALNET, __IPV4_DEVCONF_MAX }; @@ -131,6 +132,7 @@ static inline void ipv4_devconf_setall(struct in_device *in_dev) #define IN_DEV_PROMOTE_SECONDARIES(in_dev) \ IN_DEV_ORCONF((in_dev), \ PROMOTE_SECONDARIES) +#define IN_DEV_ROUTE_LOCALNET(in_dev) IN_DEV_ORCONF(in_dev, ROUTE_LOCALNET) #define IN_DEV_RX_REDIRECTS(in_dev) \ ((IN_DEV_FORWARD(in_dev) && \ diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index cda37be02f8d..2e560f0c757d 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -790,7 +790,8 @@ static int arp_process(struct sk_buff *skb) * Check for bad requests for 127.x.x.x and requests for multicast * addresses. If this is one such, delete it. */ - if (ipv4_is_loopback(tip) || ipv4_is_multicast(tip)) + if (ipv4_is_multicast(tip) || + (!IN_DEV_ROUTE_LOCALNET(in_dev) && ipv4_is_loopback(tip))) goto out; /* diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 10e15a144e95..44bf82e3aef7 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1500,7 +1500,8 @@ static int devinet_conf_proc(ctl_table *ctl, int write, if (cnf == net->ipv4.devconf_dflt) devinet_copy_dflt_conf(net, i); - if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1) + if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || + i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) if ((new_value == 0) && (old_value != 0)) rt_cache_flush(net, 0); } @@ -1617,6 +1618,8 @@ static struct devinet_sysctl_table { "force_igmp_version"), DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, "promote_secondaries"), + DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, + "route_localnet"), }, }; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 842510d50453..655506af47ca 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1960,9 +1960,13 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, return -EINVAL; if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr) || - ipv4_is_loopback(saddr) || skb->protocol != htons(ETH_P_IP)) + skb->protocol != htons(ETH_P_IP)) goto e_inval; + if (likely(!IN_DEV_ROUTE_LOCALNET(in_dev))) + if (ipv4_is_loopback(saddr)) + goto e_inval; + if (ipv4_is_zeronet(saddr)) { if (!ipv4_is_local_multicast(daddr)) goto e_inval; @@ -2203,8 +2207,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, by fib_lookup. */ - if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr) || - ipv4_is_loopback(saddr)) + if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr)) goto martian_source; if (ipv4_is_lbcast(daddr) || (saddr == 0 && daddr == 0)) @@ -2216,9 +2219,17 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, if (ipv4_is_zeronet(saddr)) goto martian_source; - if (ipv4_is_zeronet(daddr) || ipv4_is_loopback(daddr)) + if (ipv4_is_zeronet(daddr)) goto martian_destination; + if (likely(!IN_DEV_ROUTE_LOCALNET(in_dev))) { + if (ipv4_is_loopback(daddr)) + goto martian_destination; + + if (ipv4_is_loopback(saddr)) + goto martian_source; + } + /* * Now we are ready to route packet. */ @@ -2457,9 +2468,14 @@ static struct rtable *__mkroute_output(const struct fib_result *res, u16 type = res->type; struct rtable *rth; - if (ipv4_is_loopback(fl4->saddr) && !(dev_out->flags & IFF_LOOPBACK)) + in_dev = __in_dev_get_rcu(dev_out); + if (!in_dev) return ERR_PTR(-EINVAL); + if (likely(!IN_DEV_ROUTE_LOCALNET(in_dev))) + if (ipv4_is_loopback(fl4->saddr) && !(dev_out->flags & IFF_LOOPBACK)) + return ERR_PTR(-EINVAL); + if (ipv4_is_lbcast(fl4->daddr)) type = RTN_BROADCAST; else if (ipv4_is_multicast(fl4->daddr)) @@ -2470,10 +2486,6 @@ static struct rtable *__mkroute_output(const struct fib_result *res, if (dev_out->flags & IFF_LOOPBACK) flags |= RTCF_LOCAL; - in_dev = __in_dev_get_rcu(dev_out); - if (!in_dev) - return ERR_PTR(-EINVAL); - if (type == RTN_BROADCAST) { flags |= RTCF_BROADCAST | RTCF_LOCAL; fi = NULL; -- cgit v1.2.3-59-g8ed1b From 4a5a14d39e8164b5c77f1bf42851a58a69a6c7b2 Mon Sep 17 00:00:00 2001 From: "tom.leiming@gmail.com" Date: Mon, 11 Jun 2012 15:19:43 +0000 Subject: usbnet: remove flag of EVENT_DEV_WAKING The flag of EVENT_DEV_WAKING is not used any more, so just remove it. Signed-off-by: Ming Lei Signed-off-by: David S. Miller --- include/linux/usb/usbnet.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index 76f439647c4b..f87cf622317f 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -66,9 +66,8 @@ struct usbnet { # define EVENT_STS_SPLIT 3 # define EVENT_LINK_RESET 4 # define EVENT_RX_PAUSED 5 -# define EVENT_DEV_WAKING 6 -# define EVENT_DEV_ASLEEP 7 -# define EVENT_DEV_OPEN 8 +# define EVENT_DEV_ASLEEP 6 +# define EVENT_DEV_OPEN 7 }; static inline struct usb_driver *driver_of(struct usb_interface *intf) -- cgit v1.2.3-59-g8ed1b From 95603e2293de556de7e82221649bfd7fd98b64a3 Mon Sep 17 00:00:00 2001 From: Michel Machado Date: Tue, 12 Jun 2012 10:16:35 +0000 Subject: net-next: add dev_loopback_xmit() to avoid duplicate code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add dev_loopback_xmit() in order to deduplicate functions ip_dev_loopback_xmit() (in net/ipv4/ip_output.c) and ip6_dev_loopback_xmit() (in net/ipv6/ip6_output.c). I was about to reinvent the wheel when I noticed that ip_dev_loopback_xmit() and ip6_dev_loopback_xmit() do exactly what I need and are not IP-only functions, but they were not available to reuse elsewhere. ip6_dev_loopback_xmit() does not have line "skb_dst_force(skb);", but I understand that this is harmless, and should be in dev_loopback_xmit(). Signed-off-by: Michel Machado CC: "David S. Miller" CC: Alexey Kuznetsov CC: James Morris CC: Hideaki YOSHIFUJI CC: Patrick McHardy CC: Eric Dumazet CC: Jiri Pirko CC: "Michał Mirosław" CC: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/core/dev.c | 17 +++++++++++++++++ net/ipv4/ip_output.c | 17 ++--------------- net/ipv6/ip6_output.c | 15 +-------------- 4 files changed, 21 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a0b84e3b087c..2c2ecea28a1b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1627,6 +1627,7 @@ extern int dev_alloc_name(struct net_device *dev, const char *name); extern int dev_open(struct net_device *dev); extern int dev_close(struct net_device *dev); extern void dev_disable_lro(struct net_device *dev); +extern int dev_loopback_xmit(struct sk_buff *newskb); extern int dev_queue_xmit(struct sk_buff *skb); extern int register_netdevice(struct net_device *dev); extern void unregister_netdevice_queue(struct net_device *dev, diff --git a/net/core/dev.c b/net/core/dev.c index cd0981977f5c..c6e29ea65bd9 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2475,6 +2475,23 @@ static void skb_update_prio(struct sk_buff *skb) static DEFINE_PER_CPU(int, xmit_recursion); #define RECURSION_LIMIT 10 +/** + * dev_loopback_xmit - loop back @skb + * @skb: buffer to transmit + */ +int dev_loopback_xmit(struct sk_buff *skb) +{ + skb_reset_mac_header(skb); + __skb_pull(skb, skb_network_offset(skb)); + skb->pkt_type = PACKET_LOOPBACK; + skb->ip_summed = CHECKSUM_UNNECESSARY; + WARN_ON(!skb_dst(skb)); + skb_dst_force(skb); + netif_rx_ni(skb); + return 0; +} +EXPORT_SYMBOL(dev_loopback_xmit); + /** * dev_queue_xmit - transmit a buffer * @skb: buffer to transmit diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index b99ca4e154b9..0f3185a662c3 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -113,19 +113,6 @@ int ip_local_out(struct sk_buff *skb) } EXPORT_SYMBOL_GPL(ip_local_out); -/* dev_loopback_xmit for use with netfilter. */ -static int ip_dev_loopback_xmit(struct sk_buff *newskb) -{ - skb_reset_mac_header(newskb); - __skb_pull(newskb, skb_network_offset(newskb)); - newskb->pkt_type = PACKET_LOOPBACK; - newskb->ip_summed = CHECKSUM_UNNECESSARY; - WARN_ON(!skb_dst(newskb)); - skb_dst_force(newskb); - netif_rx_ni(newskb); - return 0; -} - static inline int ip_select_ttl(struct inet_sock *inet, struct dst_entry *dst) { int ttl = inet->uc_ttl; @@ -281,7 +268,7 @@ int ip_mc_output(struct sk_buff *skb) if (newskb) NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, newskb, NULL, newskb->dev, - ip_dev_loopback_xmit); + dev_loopback_xmit); } /* Multicasts with ttl 0 must not go beyond the host */ @@ -296,7 +283,7 @@ int ip_mc_output(struct sk_buff *skb) struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); if (newskb) NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, newskb, - NULL, newskb->dev, ip_dev_loopback_xmit); + NULL, newskb->dev, dev_loopback_xmit); } return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 62fcf3e48aca..ee1bb450bfe4 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -83,19 +83,6 @@ int ip6_local_out(struct sk_buff *skb) } EXPORT_SYMBOL_GPL(ip6_local_out); -/* dev_loopback_xmit for use with netfilter. */ -static int ip6_dev_loopback_xmit(struct sk_buff *newskb) -{ - skb_reset_mac_header(newskb); - __skb_pull(newskb, skb_network_offset(newskb)); - newskb->pkt_type = PACKET_LOOPBACK; - newskb->ip_summed = CHECKSUM_UNNECESSARY; - WARN_ON(!skb_dst(newskb)); - - netif_rx_ni(newskb); - return 0; -} - static int ip6_finish_output2(struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); @@ -121,7 +108,7 @@ static int ip6_finish_output2(struct sk_buff *skb) if (newskb) NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, newskb, NULL, newskb->dev, - ip6_dev_loopback_xmit); + dev_loopback_xmit); if (ipv6_hdr(skb)->hop_limit == 0) { IP6_INC_STATS(dev_net(dev), idev, -- cgit v1.2.3-59-g8ed1b From 4a4ab0d7c9abe4e403bcea6ae2fc5d3f28a64a29 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Jun 2012 11:17:11 +0200 Subject: nl80211: fix sched scan match attribute name It should be NL80211_SCHED_SCAN_MATCH_ATTR_SSID as documented, not NL80211_ATTR_SCHED_SCAN_MATCH_SSID. Signed-off-by: Johannes Berg --- include/linux/nl80211.h | 5 ++++- net/wireless/nl80211.c | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index c61e1621822c..e7b1fc1fe26b 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1963,7 +1963,7 @@ enum nl80211_reg_rule_attr { enum nl80211_sched_scan_match_attr { __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID, - NL80211_ATTR_SCHED_SCAN_MATCH_SSID, + NL80211_SCHED_SCAN_MATCH_ATTR_SSID, /* keep last */ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, @@ -1971,6 +1971,9 @@ enum nl80211_sched_scan_match_attr { __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1 }; +/* only for backward compatibility */ +#define NL80211_ATTR_SCHED_SCAN_MATCH_SSID NL80211_SCHED_SCAN_MATCH_ATTR_SSID + /** * enum nl80211_reg_rule_flags - regulatory rule flags * diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index dd94ee5fb40a..7db0aee8cd5b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -250,7 +250,7 @@ nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { static const struct nla_policy nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = { - [NL80211_ATTR_SCHED_SCAN_MATCH_SSID] = { .type = NLA_BINARY, + [NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY, .len = IEEE80211_MAX_SSID_LEN }, }; @@ -4253,7 +4253,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, nla_data(attr), nla_len(attr), nl80211_match_policy); - ssid = tb[NL80211_ATTR_SCHED_SCAN_MATCH_SSID]; + ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]; if (ssid) { if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) { err = -EINVAL; -- cgit v1.2.3-59-g8ed1b From 73c3df3ba3f2d7fe3ea47f944282f3cda31c5505 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Jun 2012 11:17:14 +0200 Subject: cfg80211/nl80211: fix kernel-doc Add missing entries to nl80211.h and fix the kernel-doc notation in cfg80211.h. Signed-off-by: Johannes Berg --- include/linux/nl80211.h | 33 ++++++++++++++++++++++++++++----- include/net/cfg80211.h | 8 ++++---- 2 files changed, 32 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index e7b1fc1fe26b..e4f41bdebc07 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -277,6 +277,12 @@ * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) * + * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC + * (for the BSSID) and %NL80211_ATTR_PMKID. + * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC + * (for the BSSID) and %NL80211_ATTR_PMKID. + * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries. + * * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain * has been changed and provides details of the request information * that caused the change such as who initiated the regulatory request @@ -456,6 +462,10 @@ * the frame. * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for * backward compatibility. + * + * @NL80211_CMD_SET_POWER_SAVE: Set powersave, using %NL80211_ATTR_PS_STATE + * @NL80211_CMD_GET_POWER_SAVE: Get powersave status in %NL80211_ATTR_PS_STATE + * * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command * is used to configure connection quality monitoring notification trigger * levels. @@ -771,6 +781,13 @@ enum nl80211_commands { * section 7.3.2.25.1, e.g. 0x000FAC04) * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and * CCMP keys, each six bytes in little endian + * @NL80211_ATTR_KEY_DEFAULT: Flag attribute indicating the key is default key + * @NL80211_ATTR_KEY_DEFAULT_MGMT: Flag attribute indicating the key is the + * default management key + * @NL80211_ATTR_CIPHER_SUITES_PAIRWISE: For crypto settings for connect or + * other commands, indicates which pairwise cipher suites are used + * @NL80211_ATTR_CIPHER_SUITE_GROUP: For crypto settings for connect or + * other commands, indicates which group cipher suite is used * * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing @@ -1006,6 +1023,8 @@ enum nl80211_commands { * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was * acknowledged by the recipient. * + * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values. + * * @NL80211_ATTR_CQM: connection quality monitor configuration in a * nested attribute with %NL80211_ATTR_CQM_* sub-attributes. * @@ -1063,7 +1082,7 @@ enum nl80211_commands { * flag isn't set, the frame will be rejected. This is also used as an * nl80211 capability flag. * - * @NL80211_ATTR_BSS_HTOPMODE: HT operation mode (u16) + * @NL80211_ATTR_BSS_HT_OPMODE: HT operation mode (u16) * * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags * attributes, specifying what a key should be set as default as. @@ -1087,10 +1106,10 @@ enum nl80211_commands { * indicate which WoW triggers should be enabled. This is also * used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN * triggers. - + * * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan * cycles, in msecs. - + * * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more * sets of attributes to match during scheduled scans. Only BSSs * that match any of the sets will be reported. These are @@ -1117,7 +1136,7 @@ enum nl80211_commands { * are managed in software: interfaces of these types aren't subject to * any restrictions in their number or combinations. * - * @%NL80211_ATTR_REKEY_DATA: nested attribute containing the information + * @NL80211_ATTR_REKEY_DATA: nested attribute containing the information * necessary for GTK rekeying in the device, see &enum nl80211_rekey_data. * * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan, @@ -1184,7 +1203,6 @@ enum nl80211_commands { * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from * &enum nl80211_feature_flags and is advertised in wiphy information. * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe - * * requests while operating in AP-mode. * This attribute holds a bitmap of the supported protocols for * offloading (see &enum nl80211_probe_resp_offload_support_attr). @@ -2507,6 +2525,11 @@ enum nl80211_band { NL80211_BAND_5GHZ, }; +/** + * enum nl80211_ps_state - powersave state + * @NL80211_PS_DISABLED: powersave is disabled + * @NL80211_PS_ENABLED: powersave is enabled + */ enum nl80211_ps_state { NL80211_PS_DISABLED, NL80211_PS_ENABLED, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 778e533a9734..76d54725ea31 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -627,10 +627,10 @@ struct sta_bss_parameters { * @llid: mesh local link id * @plid: mesh peer link id * @plink_state: mesh peer link state - * @signal: the signal strength, type depends on the wiphy's signal_type - NOTE: For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_. - * @signal_avg: avg signal strength, type depends on the wiphy's signal_type - NOTE: For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_. + * @signal: The signal strength, type depends on the wiphy's signal_type. + * For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_. + * @signal_avg: Average signal strength, type depends on the wiphy's signal_type. + * For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_. * @txrate: current unicast bitrate from this station * @rxrate: current unicast bitrate to this station * @rx_packets: packets received from this station -- cgit v1.2.3-59-g8ed1b From 7a824e214e25a49442fe868dac0af8a904b24f58 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Mon, 11 Jun 2012 18:04:38 +0800 Subject: ASoC: mmp: add audio dma support mmp-pcm handle audio dma based on soc-dmaengine Support mmp and pxa910 Signed-off-by: Zhangfei Gao Signed-off-by: Leo Yan Signed-off-by: Qiao Zhou Signed-off-by: Mark Brown --- include/linux/platform_data/mmp_audio.h | 22 +++ sound/soc/pxa/Kconfig | 9 + sound/soc/pxa/Makefile | 2 + sound/soc/pxa/mmp-pcm.c | 297 ++++++++++++++++++++++++++++++++ 4 files changed, 330 insertions(+) create mode 100644 include/linux/platform_data/mmp_audio.h create mode 100644 sound/soc/pxa/mmp-pcm.c (limited to 'include/linux') diff --git a/include/linux/platform_data/mmp_audio.h b/include/linux/platform_data/mmp_audio.h new file mode 100644 index 000000000000..0f25d165abd6 --- /dev/null +++ b/include/linux/platform_data/mmp_audio.h @@ -0,0 +1,22 @@ +/* + * MMP Platform AUDIO Management + * + * Copyright (c) 2011 Marvell Semiconductors 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 MMP_AUDIO_H +#define MMP_AUDIO_H + +struct mmp_audio_platdata { + u32 period_max_capture; + u32 buffer_max_capture; + u32 period_max_playback; + u32 buffer_max_playback; +}; + +#endif /* MMP_AUDIO_H */ diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index a0f7d3cfa470..5d76e2971fbe 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -8,6 +8,15 @@ config SND_PXA2XX_SOC the PXA2xx AC97, I2S or SSP interface. You will also need to select the audio interfaces to support below. +config SND_MMP_SOC + bool "Soc Audio for Marvell MMP chips" + depends on ARCH_MMP + select SND_SOC_DMAENGINE_PCM + select SND_ARM + help + Say Y if you want to add support for codecs attached to + the MMP SSPA interface. + config SND_PXA2XX_AC97 tristate select SND_AC97_CODEC diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index af357623be9d..f913e9bfce4f 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile @@ -3,11 +3,13 @@ snd-soc-pxa2xx-objs := pxa2xx-pcm.o snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o snd-soc-pxa-ssp-objs := pxa-ssp.o +snd-soc-mmp-objs := mmp-pcm.o obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o obj-$(CONFIG_SND_PXA_SOC_SSP) += snd-soc-pxa-ssp.o +obj-$(CONFIG_SND_MMP_SOC) += snd-soc-mmp.o # PXA Machine Support snd-soc-corgi-objs := corgi.o diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c new file mode 100644 index 000000000000..73ac5463c9e4 --- /dev/null +++ b/sound/soc/pxa/mmp-pcm.c @@ -0,0 +1,297 @@ +/* + * linux/sound/soc/pxa/mmp-pcm.c + * + * Copyright (C) 2011 Marvell International 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 +#include + +struct mmp_dma_data { + int ssp_id; + struct resource *dma_res; +}; + +#define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP | \ + SNDRV_PCM_INFO_MMAP_VALID | \ + SNDRV_PCM_INFO_INTERLEAVED | \ + SNDRV_PCM_INFO_PAUSE | \ + SNDRV_PCM_INFO_RESUME) + +#define MMP_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_pcm_hardware mmp_pcm_hardware[] = { + { + .info = MMP_PCM_INFO, + .formats = MMP_PCM_FORMATS, + .period_bytes_min = 1024, + .period_bytes_max = 2048, + .periods_min = 2, + .periods_max = 32, + .buffer_bytes_max = 4096, + .fifo_size = 32, + }, + { + .info = MMP_PCM_INFO, + .formats = MMP_PCM_FORMATS, + .period_bytes_min = 1024, + .period_bytes_max = 2048, + .periods_min = 2, + .periods_max = 32, + .buffer_bytes_max = 4096, + .fifo_size = 32, + }, +}; + +static int mmp_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct pxa2xx_pcm_dma_params *dma_params; + struct dma_slave_config slave_config; + int ret; + + dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + if (!dma_params) + return 0; + + ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config); + if (ret) + return ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + slave_config.dst_addr = dma_params->dev_addr; + slave_config.dst_maxburst = 4; + } else { + slave_config.src_addr = dma_params->dev_addr; + slave_config.src_maxburst = 4; + } + + ret = dmaengine_slave_config(chan, &slave_config); + if (ret) + return ret; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +static bool filter(struct dma_chan *chan, void *param) +{ + struct mmp_dma_data *dma_data = param; + bool found = false; + char *devname; + + devname = kasprintf(GFP_KERNEL, "%s.%d", dma_data->dma_res->name, + dma_data->ssp_id); + if ((strcmp(dev_name(chan->device->dev), devname) == 0) && + (chan->chan_id == dma_data->dma_res->start)) { + found = true; + } + + kfree(devname); + return found; +} + +static int mmp_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct platform_device *pdev = to_platform_device(rtd->platform->dev); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct mmp_dma_data *dma_data; + struct resource *r; + int ret; + + r = platform_get_resource(pdev, IORESOURCE_DMA, substream->stream); + if (!r) + return -EBUSY; + + snd_soc_set_runtime_hwparams(substream, + &mmp_pcm_hardware[substream->stream]); + dma_data = devm_kzalloc(&pdev->dev, + sizeof(struct mmp_dma_data), GFP_KERNEL); + if (dma_data == NULL) + return -ENOMEM; + + dma_data->dma_res = r; + dma_data->ssp_id = cpu_dai->id; + + ret = snd_dmaengine_pcm_open(substream, filter, dma_data); + if (ret) { + devm_kfree(&pdev->dev, dma_data); + return ret; + } + + snd_dmaengine_pcm_set_data(substream, dma_data); + return 0; +} + +static int mmp_pcm_close(struct snd_pcm_substream *substream) +{ + struct mmp_dma_data *dma_data = snd_dmaengine_pcm_get_data(substream); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct platform_device *pdev = to_platform_device(rtd->platform->dev); + + snd_dmaengine_pcm_close(substream); + devm_kfree(&pdev->dev, dma_data); + return 0; +} + +static int mmp_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned long off = vma->vm_pgoff; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + return remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(runtime->dma_addr) + off, + vma->vm_end - vma->vm_start, vma->vm_page_prot); +} + +struct snd_pcm_ops mmp_pcm_ops = { + .open = mmp_pcm_open, + .close = mmp_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = mmp_pcm_hw_params, + .trigger = snd_dmaengine_pcm_trigger, + .pointer = snd_dmaengine_pcm_pointer, + .mmap = mmp_pcm_mmap, +}; + +static void mmp_pcm_free_dma_buffers(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + struct gen_pool *gpool; + + gpool = sram_get_gpool("asram"); + if (!gpool) + return; + + for (stream = 0; stream < 2; stream++) { + size_t size = mmp_pcm_hardware[stream].buffer_bytes_max; + + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + gen_pool_free(gpool, (unsigned long)buf->area, size); + buf->area = NULL; + } + + return; +} + +static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream, + int stream) +{ + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = mmp_pcm_hardware[stream].buffer_bytes_max; + struct gen_pool *gpool; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = substream->pcm->card->dev; + buf->private_data = NULL; + + gpool = sram_get_gpool("asram"); + if (!gpool) + return -ENOMEM; + + buf->area = (unsigned char *)gen_pool_alloc(gpool, size); + if (!buf->area) + return -ENOMEM; + buf->addr = gen_pool_virt_to_phys(gpool, (unsigned long)buf->area); + buf->bytes = size; + return 0; +} + +int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm_substream *substream; + struct snd_pcm *pcm = rtd->pcm; + int ret = 0, stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + + ret = mmp_pcm_preallocate_dma_buffer(substream, stream); + if (ret) + goto err; + } + + return 0; + +err: + mmp_pcm_free_dma_buffers(pcm); + return ret; +} + +struct snd_soc_platform_driver mmp_soc_platform = { + .ops = &mmp_pcm_ops, + .pcm_new = mmp_pcm_new, + .pcm_free = mmp_pcm_free_dma_buffers, +}; + +static __devinit int mmp_pcm_probe(struct platform_device *pdev) +{ + struct mmp_audio_platdata *pdata = pdev->dev.platform_data; + + if (pdata) { + mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].buffer_bytes_max = + pdata->buffer_max_playback; + mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].period_bytes_max = + pdata->period_max_playback; + mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].buffer_bytes_max = + pdata->buffer_max_capture; + mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].period_bytes_max = + pdata->period_max_capture; + } + return snd_soc_register_platform(&pdev->dev, &mmp_soc_platform); +} + +static int __devexit mmp_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static struct platform_driver mmp_pcm_driver = { + .driver = { + .name = "mmp-pcm-audio", + .owner = THIS_MODULE, + }, + + .probe = mmp_pcm_probe, + .remove = __devexit_p(mmp_pcm_remove), +}; + +module_platform_driver(mmp_pcm_driver); + +MODULE_AUTHOR("Leo Yan "); +MODULE_DESCRIPTION("MMP Soc Audio DMA module"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From bd3810a58ba9c16702450c643ea3c557668185db Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 13 Jun 2012 14:29:16 +0100 Subject: regmap: Remove warning on stubbed dev_get_regmap() It's perfectly sensible to ask if there's a regmap for a device which doesn't have one so the stubbed version shouldn't complain, the caller should be prepared for this. Signed-off-by: Mark Brown --- include/linux/regmap.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/regmap.h b/include/linux/regmap.h index f9b624c83464..4191a4281587 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -386,7 +386,6 @@ static inline int regmap_register_patch(struct regmap *map, static inline struct regmap *dev_get_regmap(struct device *dev, const char *name) { - WARN_ONCE(1, "regmap API is disabled"); return NULL; } -- cgit v1.2.3-59-g8ed1b From b4bc9ef6253578ecc71eec79a7dd423d0a282a4b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 13 Jun 2012 11:27:11 +0800 Subject: regulator: tps65217: Convert to regulator_[is_enabled|get_voltage_sel]_regmap This patch converts .is_enabled and .get_voltage_sel to regulator_is_enabled_regmap and regulator_get_voltage_sel_regmap. For .enable, .disable, and .set_voltage_sel, the write protect level is either 1 or 2. So we cannot use regulator_[enable|disable|set_voltage_sel]_regmap. Now we store the enable reg/mask and vsel reg/mask in regulator_desc, so we can remove enable_mask, set_vout_reg, and set_vout_mask from struct tps_info. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps65217-regulator.c | 114 ++++++++++++--------------------- include/linux/mfd/tps65217.h | 6 -- 2 files changed, 42 insertions(+), 78 deletions(-) (limited to 'include/linux') diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 9d371d2cbcae..f5fa05b5bea4 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -26,7 +26,7 @@ #include #include -#define TPS65217_REGULATOR(_name, _id, _ops, _n) \ +#define TPS65217_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _em) \ { \ .name = _name, \ .id = _id, \ @@ -34,9 +34,13 @@ .n_voltages = _n, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ + .vsel_reg = _vr, \ + .vsel_mask = _vm, \ + .enable_reg = TPS65217_REG_ENABLE, \ + .enable_mask = _em, \ } \ -#define TPS65217_INFO(_nm, _min, _max, _f1, _f2, _t, _n, _em, _vr, _vm) \ +#define TPS65217_INFO(_nm, _min, _max, _f1, _f2, _t, _n)\ { \ .name = _nm, \ .min_uV = _min, \ @@ -45,9 +49,6 @@ .uv_to_vsel = _f2, \ .table = _t, \ .table_len = _n, \ - .enable_mask = _em, \ - .set_vout_reg = _vr, \ - .set_vout_mask = _vm, \ } static const int LDO1_VSEL_table[] = { @@ -127,46 +128,21 @@ static int tps65217_uv_to_vsel2(int uV, unsigned int *vsel) static struct tps_info tps65217_pmic_regs[] = { TPS65217_INFO("DCDC1", 900000, 1800000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_DC1_EN, - TPS65217_REG_DEFDCDC1, TPS65217_DEFDCDCX_DCDC_MASK), + tps65217_uv_to_vsel1, NULL, 64), TPS65217_INFO("DCDC2", 900000, 3300000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_DC2_EN, - TPS65217_REG_DEFDCDC2, TPS65217_DEFDCDCX_DCDC_MASK), + tps65217_uv_to_vsel1, NULL, 64), TPS65217_INFO("DCDC3", 900000, 1500000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_DC3_EN, - TPS65217_REG_DEFDCDC3, TPS65217_DEFDCDCX_DCDC_MASK), + tps65217_uv_to_vsel1, NULL, 64), TPS65217_INFO("LDO1", 1000000, 3300000, NULL, NULL, LDO1_VSEL_table, - 16, TPS65217_ENABLE_LDO1_EN, TPS65217_REG_DEFLDO1, - TPS65217_DEFLDO1_LDO1_MASK), + 16), TPS65217_INFO("LDO2", 900000, 3300000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_LDO2_EN, - TPS65217_REG_DEFLDO2, TPS65217_DEFLDO2_LDO2_MASK), + tps65217_uv_to_vsel1, NULL, 64), TPS65217_INFO("LDO3", 1800000, 3300000, tps65217_vsel_to_uv2, - tps65217_uv_to_vsel2, NULL, 32, - TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN, - TPS65217_REG_DEFLS1, TPS65217_DEFLDO3_LDO3_MASK), + tps65217_uv_to_vsel2, NULL, 32), TPS65217_INFO("LDO4", 1800000, 3300000, tps65217_vsel_to_uv2, - tps65217_uv_to_vsel2, NULL, 32, - TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN, - TPS65217_REG_DEFLS2, TPS65217_DEFLDO4_LDO4_MASK), + tps65217_uv_to_vsel2, NULL, 32), }; -static int tps65217_pmic_is_enabled(struct regulator_dev *dev) -{ - int ret; - struct tps65217 *tps = rdev_get_drvdata(dev); - unsigned int data, rid = rdev_get_id(dev); - - if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) - return -EINVAL; - - ret = tps65217_reg_read(tps, TPS65217_REG_ENABLE, &data); - if (ret) - return ret; - - return (data & tps->info[rid]->enable_mask) ? 1 : 0; -} - static int tps65217_pmic_enable(struct regulator_dev *dev) { struct tps65217 *tps = rdev_get_drvdata(dev); @@ -177,9 +153,8 @@ static int tps65217_pmic_enable(struct regulator_dev *dev) /* Enable the regulator and password protection is level 1 */ return tps65217_set_bits(tps, TPS65217_REG_ENABLE, - tps->info[rid]->enable_mask, - tps->info[rid]->enable_mask, - TPS65217_PROTECT_L1); + dev->desc->enable_mask, dev->desc->enable_mask, + TPS65217_PROTECT_L1); } static int tps65217_pmic_disable(struct regulator_dev *dev) @@ -192,25 +167,7 @@ static int tps65217_pmic_disable(struct regulator_dev *dev) /* Disable the regulator and password protection is level 1 */ return tps65217_clear_bits(tps, TPS65217_REG_ENABLE, - tps->info[rid]->enable_mask, TPS65217_PROTECT_L1); -} - -static int tps65217_pmic_get_voltage_sel(struct regulator_dev *dev) -{ - int ret; - struct tps65217 *tps = rdev_get_drvdata(dev); - unsigned int selector, rid = rdev_get_id(dev); - - if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) - return -EINVAL; - - ret = tps65217_reg_read(tps, tps->info[rid]->set_vout_reg, &selector); - if (ret) - return ret; - - selector &= tps->info[rid]->set_vout_mask; - - return selector; + dev->desc->enable_mask, TPS65217_PROTECT_L1); } static int tps65217_pmic_set_voltage_sel(struct regulator_dev *dev, @@ -221,8 +178,7 @@ static int tps65217_pmic_set_voltage_sel(struct regulator_dev *dev, unsigned int rid = rdev_get_id(dev); /* Set the voltage based on vsel value and write protect level is 2 */ - ret = tps65217_set_bits(tps, tps->info[rid]->set_vout_reg, - tps->info[rid]->set_vout_mask, + ret = tps65217_set_bits(tps, dev->desc->vsel_reg, dev->desc->vsel_mask, selector, TPS65217_PROTECT_L2); /* Set GO bit for DCDCx to initiate voltage transistion */ @@ -285,10 +241,10 @@ static int tps65217_pmic_list_voltage(struct regulator_dev *dev, /* Operations permitted on DCDCx, LDO2, LDO3 and LDO4 */ static struct regulator_ops tps65217_pmic_ops = { - .is_enabled = tps65217_pmic_is_enabled, + .is_enabled = regulator_is_enabled_regmap, .enable = tps65217_pmic_enable, .disable = tps65217_pmic_disable, - .get_voltage_sel = tps65217_pmic_get_voltage_sel, + .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = tps65217_pmic_set_voltage_sel, .list_voltage = tps65217_pmic_list_voltage, .map_voltage = tps65217_pmic_map_voltage, @@ -296,22 +252,36 @@ static struct regulator_ops tps65217_pmic_ops = { /* Operations permitted on LDO1 */ static struct regulator_ops tps65217_pmic_ldo1_ops = { - .is_enabled = tps65217_pmic_is_enabled, + .is_enabled = regulator_is_enabled_regmap, .enable = tps65217_pmic_enable, .disable = tps65217_pmic_disable, - .get_voltage_sel = tps65217_pmic_get_voltage_sel, + .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = tps65217_pmic_set_voltage_sel, .list_voltage = tps65217_pmic_list_voltage, }; static const struct regulator_desc regulators[] = { - TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, tps65217_pmic_ops, 64), - TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, tps65217_pmic_ops, 64), - TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, tps65217_pmic_ops, 64), - TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, tps65217_pmic_ldo1_ops, 16), - TPS65217_REGULATOR("LDO2", TPS65217_LDO_2, tps65217_pmic_ops, 64), - TPS65217_REGULATOR("LDO3", TPS65217_LDO_3, tps65217_pmic_ops, 32), - TPS65217_REGULATOR("LDO4", TPS65217_LDO_4, tps65217_pmic_ops, 32), + TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, tps65217_pmic_ops, 64, + TPS65217_REG_DEFDCDC1, TPS65217_DEFDCDCX_DCDC_MASK, + TPS65217_ENABLE_DC1_EN), + TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, tps65217_pmic_ops, 64, + TPS65217_REG_DEFDCDC2, TPS65217_DEFDCDCX_DCDC_MASK, + TPS65217_ENABLE_DC2_EN), + TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, tps65217_pmic_ops, 64, + TPS65217_REG_DEFDCDC3, TPS65217_DEFDCDCX_DCDC_MASK, + TPS65217_ENABLE_DC3_EN), + TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, tps65217_pmic_ldo1_ops, 16, + TPS65217_REG_DEFLDO1, TPS65217_DEFLDO1_LDO1_MASK, + TPS65217_ENABLE_LDO1_EN), + TPS65217_REGULATOR("LDO2", TPS65217_LDO_2, tps65217_pmic_ops, 64, + TPS65217_REG_DEFLDO2, TPS65217_DEFLDO2_LDO2_MASK, + TPS65217_ENABLE_LDO2_EN), + TPS65217_REGULATOR("LDO3", TPS65217_LDO_3, tps65217_pmic_ops, 32, + TPS65217_REG_DEFLS1, TPS65217_DEFLDO3_LDO3_MASK, + TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN), + TPS65217_REGULATOR("LDO4", TPS65217_LDO_4, tps65217_pmic_ops, 32, + TPS65217_REG_DEFLS2, TPS65217_DEFLDO4_LDO4_MASK, + TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN), }; static int __devinit tps65217_regulator_probe(struct platform_device *pdev) diff --git a/include/linux/mfd/tps65217.h b/include/linux/mfd/tps65217.h index e030ef9a64ee..4e035a41a9b0 100644 --- a/include/linux/mfd/tps65217.h +++ b/include/linux/mfd/tps65217.h @@ -229,9 +229,6 @@ struct tps65217_board { * @uv_to_vsel: Function pointer to get selector from voltage * @table: Table for non-uniform voltage step-size * @table_len: Length of the voltage table - * @enable_mask: Regulator enable mask bits - * @set_vout_reg: Regulator output voltage set register - * @set_vout_mask: Regulator output voltage set mask * * This data is used to check the regualtor voltage limits while setting. */ @@ -243,9 +240,6 @@ struct tps_info { int (*uv_to_vsel)(int uV, unsigned int *vsel); const int *table; unsigned int table_len; - unsigned int enable_mask; - unsigned int set_vout_reg; - unsigned int set_vout_mask; }; /** -- cgit v1.2.3-59-g8ed1b From 92f02430934ca1c1e991a1ab3541880575042697 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:11 -0700 Subject: PCI: add busn_res in struct pci_bus This adds a busn_res resource in struct pci_bus. This will replace the secondary/subordinate members and will be used to build a bus number resource tree to help with bus number allocation. [bhelgaas: changelog] CC: Andrew Morton Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..ba7c5cd314b7 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -419,6 +419,7 @@ struct pci_bus { struct list_head slots; /* list of slots on this bus */ struct resource *resource[PCI_BRIDGE_RESOURCE_NUM]; struct list_head resources; /* address space routed to this bus */ + struct resource busn_res; /* bus numbers routed to this bus */ struct pci_ops *ops; /* configuration access functions */ void *sysdata; /* hook for sys-specific extension */ -- cgit v1.2.3-59-g8ed1b From 3527ed81ca01bbaf09df952e68528377a9cd092f Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:11 -0700 Subject: PCI: remove secondary/subordinate in struct pci_bus The pci_bus secondary/subordinate members are now unused, so remove them. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- include/linux/pci.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pci.h b/include/linux/pci.h index ba7c5cd314b7..6b10966cd1b9 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -427,8 +427,6 @@ struct pci_bus { unsigned char number; /* bus number */ unsigned char primary; /* number of primary bridge */ - unsigned char secondary; /* number of secondary bridge */ - unsigned char subordinate; /* max number of subordinate buses */ unsigned char max_bus_speed; /* enum pci_bus_speed */ unsigned char cur_bus_speed; /* enum pci_bus_speed */ -- cgit v1.2.3-59-g8ed1b From 98a3583107ed587ed3cfe2a1d8e5347421de5a80 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 18 May 2012 11:35:50 -0600 Subject: PCI: add busn_res operation functions Will use them insert/update busn res in pci_bus struct. [bhelgaas: print conflicting entry if insertion fails] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 3 +++ 2 files changed, 71 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 674a477a6486..7662ab7b2640 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1714,6 +1714,74 @@ err_out: return NULL; } +int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max) +{ + struct resource *res = &b->busn_res; + struct resource *parent_res, *conflict; + + res->start = bus; + res->end = bus_max; + res->flags = IORESOURCE_BUS; + + if (!pci_is_root_bus(b)) + parent_res = &b->parent->busn_res; + else { + parent_res = get_pci_domain_busn_res(pci_domain_nr(b)); + res->flags |= IORESOURCE_PCI_FIXED; + } + + conflict = insert_resource_conflict(parent_res, res); + + if (conflict) + dev_printk(KERN_DEBUG, &b->dev, + "busn_res: can not insert %pR under %s%pR (conflicts with %s %pR)\n", + res, pci_is_root_bus(b) ? "domain " : "", + parent_res, conflict->name, conflict); + else + dev_printk(KERN_DEBUG, &b->dev, + "busn_res: %pR is inserted under %s%pR\n", + res, pci_is_root_bus(b) ? "domain " : "", + parent_res); + + return conflict == NULL; +} + +int pci_bus_update_busn_res_end(struct pci_bus *b, int bus_max) +{ + struct resource *res = &b->busn_res; + struct resource old_res = *res; + resource_size_t size; + int ret; + + if (res->start > bus_max) + return -EINVAL; + + size = bus_max - res->start + 1; + ret = adjust_resource(res, res->start, size); + dev_printk(KERN_DEBUG, &b->dev, + "busn_res: %pR end %s updated to %02x\n", + &old_res, ret ? "can not be" : "is", bus_max); + + if (!ret && !res->parent) + pci_bus_insert_busn_res(b, res->start, res->end); + + return ret; +} + +void pci_bus_release_busn_res(struct pci_bus *b) +{ + struct resource *res = &b->busn_res; + int ret; + + if (!res->flags || !res->parent) + return; + + ret = release_resource(res); + dev_printk(KERN_DEBUG, &b->dev, + "busn_res: %pR %s released\n", + res, ret ? "can not be" : "is"); +} + struct pci_bus * __devinit pci_scan_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { diff --git a/include/linux/pci.h b/include/linux/pci.h index 6b10966cd1b9..c4df570f3bbb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -667,6 +667,9 @@ struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops, void *sysdata); struct pci_bus *pci_create_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources); +int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int busmax); +int pci_bus_update_busn_res_end(struct pci_bus *b, int busmax); +void pci_bus_release_busn_res(struct pci_bus *b); struct pci_bus * __devinit pci_scan_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources); -- cgit v1.2.3-59-g8ed1b From 67cdc827286366acb6c60c821013c1185ee00b36 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:12 -0700 Subject: PCI: add default busn_resource We need to put into the resources list for legacy system. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 7 +++++++ include/linux/pci.h | 2 ++ 2 files changed, 9 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6258f6f24983..68e75cb0831b 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -16,6 +16,13 @@ #define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ #define CARDBUS_RESERVE_BUSNR 3 +struct resource busn_resource = { + .name = "PCI busn", + .start = 0, + .end = 255, + .flags = IORESOURCE_BUS, +}; + /* Ugh. Need to stop exporting this to modules. */ LIST_HEAD(pci_root_buses); EXPORT_SYMBOL(pci_root_buses); diff --git a/include/linux/pci.h b/include/linux/pci.h index c4df570f3bbb..8c8b44d62105 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -368,6 +368,8 @@ static inline int pci_channel_offline(struct pci_dev *pdev) return (pdev->error_state != pci_channel_io_normal); } +extern struct resource busn_resource; + struct pci_host_bridge_window { struct list_head list; struct resource *res; /* host bridge aperture (CPU address) */ -- cgit v1.2.3-59-g8ed1b From ac1073a61d73b6277794d2efc872eb7e1b706b5c Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Thu, 14 Jun 2012 02:06:06 +0800 Subject: {nl,cfg,mac}80211: implement dot11MeshHWMProotInterval and dot11MeshHWMPactivePathToRootTimeout Add the mesh configuration parameters dot11MeshHWMProotInterval and dot11MeshHWMPactivePathToRootTimeout to be used by proactive PREQ mechanism. Signed-off-by: Chun-Yeow Yeoh [line-break commit log] Signed-off-by: Johannes Berg --- include/linux/nl80211.h | 9 +++++++++ include/net/cfg80211.h | 9 +++++++++ net/mac80211/cfg.c | 6 ++++++ net/mac80211/debugfs_netdev.c | 6 ++++++ net/wireless/mesh.c | 4 ++++ net/wireless/nl80211.c | 15 ++++++++++++++- 6 files changed, 48 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index e4f41bdebc07..6936fabe8797 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -2185,6 +2185,13 @@ enum nl80211_mntr_flags { * * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute * + * @NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT: The time (in TUs) for + * which mesh STAs receiving a proactive PREQ shall consider the forwarding + * information to the root mesh STA to be valid. + * + * @NL80211_MESHCONF_HWMP_ROOT_INTERVAL: The interval of time (in TUs) between + * proactive PREQs are transmitted. + * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ enum nl80211_meshconf_params { @@ -2211,6 +2218,8 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_RSSI_THRESHOLD, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, NL80211_MESHCONF_HT_OPMODE, + NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, + NL80211_MESHCONF_HWMP_ROOT_INTERVAL, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 76d54725ea31..e52b38d7b1b6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -842,6 +842,13 @@ struct bss_parameters { * @rssi_threshold: the threshold for average signal strength of candidate * station to establish a peer link * @ht_opmode: mesh HT protection mode + * + * @dot11MeshHWMPactivePathToRootTimeout: The time (in TUs) for which mesh STAs + * receiving a proactive PREQ shall consider the forwarding information to + * the root mesh STA to be valid. + * + * @dot11MeshHWMProotInterval: The interval of time (in TUs) between proactive + * PREQs are transmitted. */ struct mesh_config { u16 dot11MeshRetryTimeout; @@ -866,6 +873,8 @@ struct mesh_config { bool dot11MeshForwarding; s32 rssi_threshold; u16 ht_opmode; + u32 dot11MeshHWMPactivePathToRootTimeout; + u16 dot11MeshHWMProotInterval; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index cd8b1fb05d42..d93cda1c4215 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1590,6 +1590,12 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, sdata->vif.bss_conf.ht_operation_mode = nconf->ht_opmode; ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT); } + if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, mask)) + conf->dot11MeshHWMPactivePathToRootTimeout = + nconf->dot11MeshHWMPactivePathToRootTimeout; + if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ROOT_INTERVAL, mask)) + conf->dot11MeshHWMProotInterval = + nconf->dot11MeshHWMProotInterval; return 0; } diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index c429417e1322..a8cea70902e4 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -510,6 +510,10 @@ IEEE80211_IF_FILE(dot11MeshHWMPRannInterval, IEEE80211_IF_FILE(dot11MeshForwarding, u.mesh.mshcfg.dot11MeshForwarding, DEC); IEEE80211_IF_FILE(rssi_threshold, u.mesh.mshcfg.rssi_threshold, DEC); IEEE80211_IF_FILE(ht_opmode, u.mesh.mshcfg.ht_opmode, DEC); +IEEE80211_IF_FILE(dot11MeshHWMPactivePathToRootTimeout, + u.mesh.mshcfg.dot11MeshHWMPactivePathToRootTimeout, DEC); +IEEE80211_IF_FILE(dot11MeshHWMProotInterval, + u.mesh.mshcfg.dot11MeshHWMProotInterval, DEC); #endif #define DEBUGFS_ADD_MODE(name, mode) \ @@ -611,6 +615,8 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata) MESHPARAMS_ADD(dot11MeshGateAnnouncementProtocol); MESHPARAMS_ADD(rssi_threshold); MESHPARAMS_ADD(ht_opmode); + MESHPARAMS_ADD(dot11MeshHWMPactivePathToRootTimeout); + MESHPARAMS_ADD(dot11MeshHWMProotInterval); #undef MESHPARAMS_ADD } #endif diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index b44c736bf9cf..2f141cfd581e 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -14,6 +14,8 @@ #define MESH_PATH_TIMEOUT 5000 #define MESH_RANN_INTERVAL 5000 +#define MESH_PATH_TO_ROOT_TIMEOUT 6000 +#define MESH_ROOT_INTERVAL 5000 /* * Minimum interval between two consecutive PREQs originated by the same @@ -62,6 +64,8 @@ const struct mesh_config default_mesh_config = { .dot11MeshForwarding = true, .rssi_threshold = MESH_RSSI_THRESHOLD, .ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED, + .dot11MeshHWMPactivePathToRootTimeout = MESH_PATH_TO_ROOT_TIMEOUT, + .dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL, }; const struct mesh_setup default_mesh_setup = { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7db0aee8cd5b..f8930db613df 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3469,7 +3469,11 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD, cur_params.rssi_threshold) || nla_put_u32(msg, NL80211_MESHCONF_HT_OPMODE, - cur_params.ht_opmode)) + cur_params.ht_opmode) || + nla_put_u32(msg, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, + cur_params.dot11MeshHWMPactivePathToRootTimeout) || + nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, + cur_params.dot11MeshHWMProotInterval)) goto nla_put_failure; nla_nest_end(msg, pinfoattr); genlmsg_end(msg, hdr); @@ -3505,6 +3509,8 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A [NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 }, [NL80211_MESHCONF_RSSI_THRESHOLD] = { .type = NLA_U32 }, [NL80211_MESHCONF_HT_OPMODE] = { .type = NLA_U16 }, + [NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 }, + [NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 }, }; static const struct nla_policy @@ -3612,6 +3618,13 @@ do {\ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, mask, NL80211_MESHCONF_HT_OPMODE, nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout, + mask, + NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, + nla_get_u32); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, + mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, + nla_get_u16); if (mask_out) *mask_out = mask; -- cgit v1.2.3-59-g8ed1b From a69cc44fe9ebb806c5f3f8bd83fb4a50ca63647b Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Thu, 14 Jun 2012 02:06:07 +0800 Subject: mac80211: implement the proactive PREQ generation Generate the proactive PREQ element as defined in Sec. 13.10.9.3 (Case C) of IEEE Std. 802.11-2012 based on the selection of dot11MeshHWMPRootMode as follow: dot11MeshHWMPRootMode (2) is proactivePREQnoPREP dot11MeshHWMPRootMode (3) is proactivePREQwithPREP The proactive PREQ is generated based on the interval defined by dot11MeshHWMProotInterval. With this change, proactive RANN element is now generated if the dot11MeshHWMPRootMode is set to (4) instead of (1). Signed-off-by: Chun-Yeow Yeoh [line-break commit log] Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 42 ++++++++++++++++++++++++++++++++++++++++++ net/mac80211/mesh.c | 10 ++++++++-- net/mac80211/mesh_hwmp.c | 27 ++++++++++++++++++++++++--- 3 files changed, 74 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 98c86ff657bb..6e0601189db9 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -567,6 +567,26 @@ struct ieee80211s_hdr { #define MESH_FLAGS_AE 0x3 #define MESH_FLAGS_PS_DEEP 0x4 +/** + * enum ieee80211_preq_flags - mesh PREQ element flags + * + * @IEEE80211_PREQ_PROACTIVE_PREP_FLAG: proactive PREP subfield + */ +enum ieee80211_preq_flags { + IEEE80211_PREQ_PROACTIVE_PREP_FLAG = 1<<2, +}; + +/** + * enum ieee80211_preq_target_flags - mesh PREQ element per target flags + * + * @IEEE80211_PREQ_TO_FLAG: target only subfield + * @IEEE80211_PREQ_USN_FLAG: unknown target HWMP sequence number subfield + */ +enum ieee80211_preq_target_flags { + IEEE80211_PREQ_TO_FLAG = 1<<0, + IEEE80211_PREQ_USN_FLAG = 1<<2, +}; + /** * struct ieee80211_quiet_ie * @@ -1474,6 +1494,28 @@ enum { IEEE80211_PATH_METRIC_VENDOR = 255, }; +/** + * enum ieee80211_root_mode_identifier - root mesh STA mode identifier + * + * These attribute are used by dot11MeshHWMPRootMode to set root mesh STA mode + * + * @IEEE80211_ROOTMODE_NO_ROOT: the mesh STA is not a root mesh STA (default) + * @IEEE80211_ROOTMODE_ROOT: the mesh STA is a root mesh STA if greater than + * this value + * @IEEE80211_PROACTIVE_PREQ_NO_PREP: the mesh STA is a root mesh STA supports + * the proactive PREQ with proactive PREP subfield set to 0 + * @IEEE80211_PROACTIVE_PREQ_WITH_PREP: the mesh STA is a root mesh STA + * supports the proactive PREQ with proactive PREP subfield set to 1 + * @IEEE80211_PROACTIVE_RANN: the mesh STA is a root mesh STA supports + * the proactive RANN + */ +enum ieee80211_root_mode_identifier { + IEEE80211_ROOTMODE_NO_ROOT = 0, + IEEE80211_ROOTMODE_ROOT = 1, + IEEE80211_PROACTIVE_PREQ_NO_PREP = 2, + IEEE80211_PROACTIVE_PREQ_WITH_PREP = 3, + IEEE80211_PROACTIVE_RANN = 4, +}; /* * IEEE 802.11-2007 7.3.2.9 Country information element diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 7cf19509fb68..6bff3c4d17dd 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -541,11 +541,17 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata, static void ieee80211_mesh_rootpath(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + u32 interval; mesh_path_tx_root_frame(sdata); + + if (ifmsh->mshcfg.dot11MeshHWMPRootMode == IEEE80211_PROACTIVE_RANN) + interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval; + else + interval = ifmsh->mshcfg.dot11MeshHWMProotInterval; + mod_timer(&ifmsh->mesh_path_root_timer, - round_jiffies(TU_TO_EXP_TIME( - ifmsh->mshcfg.dot11MeshHWMPRannInterval))); + round_jiffies(TU_TO_EXP_TIME(interval))); } #ifdef CONFIG_PM diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 9b6da2de660d..a6b08f5c4612 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -1154,13 +1154,34 @@ mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u32 interval = ifmsh->mshcfg.dot11MeshHWMPRannInterval; - u8 flags; + u8 flags, target_flags = 0; flags = (ifmsh->mshcfg.dot11MeshGateAnnouncementProtocol) ? RANN_FLAG_IS_GATE : 0; - mesh_path_sel_frame_tx(MPATH_RANN, flags, sdata->vif.addr, + + switch (ifmsh->mshcfg.dot11MeshHWMPRootMode) { + case IEEE80211_PROACTIVE_RANN: + mesh_path_sel_frame_tx(MPATH_RANN, flags, sdata->vif.addr, cpu_to_le32(++ifmsh->sn), 0, NULL, 0, broadcast_addr, - 0, sdata->u.mesh.mshcfg.element_ttl, + 0, ifmsh->mshcfg.element_ttl, cpu_to_le32(interval), 0, 0, sdata); + break; + case IEEE80211_PROACTIVE_PREQ_WITH_PREP: + flags |= IEEE80211_PREQ_PROACTIVE_PREP_FLAG; + case IEEE80211_PROACTIVE_PREQ_NO_PREP: + interval = ifmsh->mshcfg.dot11MeshHWMPactivePathToRootTimeout; + target_flags |= IEEE80211_PREQ_TO_FLAG | + IEEE80211_PREQ_USN_FLAG; + mesh_path_sel_frame_tx(MPATH_PREQ, flags, sdata->vif.addr, + cpu_to_le32(++ifmsh->sn), target_flags, + (u8 *) broadcast_addr, 0, broadcast_addr, + 0, ifmsh->mshcfg.element_ttl, + cpu_to_le32(interval), + 0, cpu_to_le32(ifmsh->preq_id++), sdata); + break; + default: + mhwmp_dbg("Proactive mechanism not supported"); + return; + } } -- cgit v1.2.3-59-g8ed1b From 5da43bed800770906fca24deef7ae3d456823b86 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 31 May 2012 21:28:58 -0400 Subject: tracing: Add comments for the other bits of ftrace_event_call.flags TRACE_EVENT_FL_ENABLED_BIT, TRACE_EVENT_FL_FILTERED_BIT, TRACE_EVENT_FL_RECORDED_CMD_BIT, Have comments about what they are, but: TRACE_EVENT_FL_CAP_ANY_BIT, TRACE_EVENT_FL_NO_SET_FILTER_BIT, TRACE_EVENT_FL_IGNORE_ENABLE_BIT, do not, making them second class citizens. To prevent another class warfare, these bits have protested for their right to be commented. And By Golly! I'll give them what they want! Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 176a939d1547..1aff18346c71 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -207,6 +207,9 @@ struct ftrace_event_call { * bit 1: enabled * bit 2: filter_active * bit 3: enabled cmd record + * bit 4: allow trace by non root (cap any) + * bit 5: failed to apply filter + * bit 6: ftrace internal event (do not enable) * * Changes to flags must hold the event_mutex. * -- cgit v1.2.3-59-g8ed1b From 7eb9ae0799b1e9f0b77733b432bc5f6f055b020b Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 14 Jun 2012 18:28:49 -0700 Subject: irq/apic: Use config_enabled(CONFIG_SMP) checks to clean up irq_set_affinity() for UP Move the ->irq_set_affinity() routines out of the #ifdef CONFIG_SMP sections and use config_enabled(CONFIG_SMP) checks inside those routines. Thus making those routines simple null stubs for !CONFIG_SMP and retaining those routines with no additional runtime overhead for CONFIG_SMP kernels. Cleans up the ifdef CONFIG_SMP in and around routines related to irq_set_affinity in io_apic and irq_remapping subsystems. Signed-off-by: Suresh Siddha Cc: torvalds@linux-foundation.org Cc: joerg.roedel@amd.com Cc: Sam Ravnborg Cc: Paul Gortmaker Link: http://lkml.kernel.org/r/1339723729.3475.63.camel@sbsiddha-desk.sc.intel.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/apic/io_apic.c | 180 ++++++++++++++++-------------------- drivers/iommu/intel_irq_remapping.c | 7 +- drivers/iommu/irq_remapping.c | 5 +- drivers/iommu/irq_remapping.h | 2 - include/linux/irq.h | 2 - 5 files changed, 86 insertions(+), 110 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index 7cbd397884f5..a951ef7decb1 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c @@ -2224,81 +2224,6 @@ void send_cleanup_vector(struct irq_cfg *cfg) cfg->move_in_progress = 0; } -static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, struct irq_cfg *cfg) -{ - int apic, pin; - struct irq_pin_list *entry; - u8 vector = cfg->vector; - - for_each_irq_pin(entry, cfg->irq_2_pin) { - unsigned int reg; - - apic = entry->apic; - pin = entry->pin; - /* - * With interrupt-remapping, destination information comes - * from interrupt-remapping table entry. - */ - if (!irq_remapped(cfg)) - io_apic_write(apic, 0x11 + pin*2, dest); - reg = io_apic_read(apic, 0x10 + pin*2); - reg &= ~IO_APIC_REDIR_VECTOR_MASK; - reg |= vector; - io_apic_modify(apic, 0x10 + pin*2, reg); - } -} - -/* - * Either sets data->affinity to a valid value, and returns - * ->cpu_mask_to_apicid of that in dest_id, or returns -1 and - * leaves data->affinity untouched. - */ -int __ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask, - unsigned int *dest_id) -{ - struct irq_cfg *cfg = data->chip_data; - unsigned int irq = data->irq; - int err; - - if (!cpumask_intersects(mask, cpu_online_mask)) - return -EINVAL; - - err = assign_irq_vector(irq, cfg, mask); - if (err) - return err; - - err = apic->cpu_mask_to_apicid_and(mask, cfg->domain, dest_id); - if (err) { - if (assign_irq_vector(irq, cfg, data->affinity)) - pr_err("Failed to recover vector for irq %d\n", irq); - return err; - } - - cpumask_copy(data->affinity, mask); - - return 0; -} - -static int -ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask, - bool force) -{ - unsigned int dest, irq = data->irq; - unsigned long flags; - int ret; - - raw_spin_lock_irqsave(&ioapic_lock, flags); - ret = __ioapic_set_affinity(data, mask, &dest); - if (!ret) { - /* Only the high 8 bits are valid. */ - dest = SET_APIC_LOGICAL_ID(dest); - __target_IO_APIC_irq(irq, dest, data->chip_data); - ret = IRQ_SET_MASK_OK_NOCOPY; - } - raw_spin_unlock_irqrestore(&ioapic_lock, flags); - return ret; -} - asmlinkage void smp_irq_move_cleanup_interrupt(void) { unsigned vector, me; @@ -2386,6 +2311,87 @@ void irq_force_complete_move(int irq) static inline void irq_complete_move(struct irq_cfg *cfg) { } #endif +static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, struct irq_cfg *cfg) +{ + int apic, pin; + struct irq_pin_list *entry; + u8 vector = cfg->vector; + + for_each_irq_pin(entry, cfg->irq_2_pin) { + unsigned int reg; + + apic = entry->apic; + pin = entry->pin; + /* + * With interrupt-remapping, destination information comes + * from interrupt-remapping table entry. + */ + if (!irq_remapped(cfg)) + io_apic_write(apic, 0x11 + pin*2, dest); + reg = io_apic_read(apic, 0x10 + pin*2); + reg &= ~IO_APIC_REDIR_VECTOR_MASK; + reg |= vector; + io_apic_modify(apic, 0x10 + pin*2, reg); + } +} + +/* + * Either sets data->affinity to a valid value, and returns + * ->cpu_mask_to_apicid of that in dest_id, or returns -1 and + * leaves data->affinity untouched. + */ +int __ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask, + unsigned int *dest_id) +{ + struct irq_cfg *cfg = data->chip_data; + unsigned int irq = data->irq; + int err; + + if (!config_enabled(CONFIG_SMP)) + return -1; + + if (!cpumask_intersects(mask, cpu_online_mask)) + return -EINVAL; + + err = assign_irq_vector(irq, cfg, mask); + if (err) + return err; + + err = apic->cpu_mask_to_apicid_and(mask, cfg->domain, dest_id); + if (err) { + if (assign_irq_vector(irq, cfg, data->affinity)) + pr_err("Failed to recover vector for irq %d\n", irq); + return err; + } + + cpumask_copy(data->affinity, mask); + + return 0; +} + +static int +ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask, + bool force) +{ + unsigned int dest, irq = data->irq; + unsigned long flags; + int ret; + + if (!config_enabled(CONFIG_SMP)) + return -1; + + raw_spin_lock_irqsave(&ioapic_lock, flags); + ret = __ioapic_set_affinity(data, mask, &dest); + if (!ret) { + /* Only the high 8 bits are valid. */ + dest = SET_APIC_LOGICAL_ID(dest); + __target_IO_APIC_irq(irq, dest, data->chip_data); + ret = IRQ_SET_MASK_OK_NOCOPY; + } + raw_spin_unlock_irqrestore(&ioapic_lock, flags); + return ret; +} + static void ack_apic_edge(struct irq_data *data) { irq_complete_move(data->chip_data); @@ -2565,9 +2571,7 @@ static void irq_remap_modify_chip_defaults(struct irq_chip *chip) chip->irq_ack = ir_ack_apic_edge; chip->irq_eoi = ir_ack_apic_level; -#ifdef CONFIG_SMP chip->irq_set_affinity = set_remapped_irq_affinity; -#endif } #endif /* CONFIG_IRQ_REMAP */ @@ -2578,9 +2582,7 @@ static struct irq_chip ioapic_chip __read_mostly = { .irq_unmask = unmask_ioapic_irq, .irq_ack = ack_apic_edge, .irq_eoi = ack_apic_level, -#ifdef CONFIG_SMP .irq_set_affinity = ioapic_set_affinity, -#endif .irq_retrigger = ioapic_retrigger_irq, }; @@ -3099,7 +3101,6 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, return err; } -#ifdef CONFIG_SMP static int msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force) { @@ -3121,7 +3122,6 @@ msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force) return IRQ_SET_MASK_OK_NOCOPY; } -#endif /* CONFIG_SMP */ /* * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices, @@ -3132,9 +3132,7 @@ static struct irq_chip msi_chip = { .irq_unmask = unmask_msi_irq, .irq_mask = mask_msi_irq, .irq_ack = ack_apic_edge, -#ifdef CONFIG_SMP .irq_set_affinity = msi_set_affinity, -#endif .irq_retrigger = ioapic_retrigger_irq, }; @@ -3219,7 +3217,6 @@ void native_teardown_msi_irq(unsigned int irq) } #ifdef CONFIG_DMAR_TABLE -#ifdef CONFIG_SMP static int dmar_msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force) @@ -3244,16 +3241,12 @@ dmar_msi_set_affinity(struct irq_data *data, const struct cpumask *mask, return IRQ_SET_MASK_OK_NOCOPY; } -#endif /* CONFIG_SMP */ - static struct irq_chip dmar_msi_type = { .name = "DMAR_MSI", .irq_unmask = dmar_msi_unmask, .irq_mask = dmar_msi_mask, .irq_ack = ack_apic_edge, -#ifdef CONFIG_SMP .irq_set_affinity = dmar_msi_set_affinity, -#endif .irq_retrigger = ioapic_retrigger_irq, }; @@ -3274,7 +3267,6 @@ int arch_setup_dmar_msi(unsigned int irq) #ifdef CONFIG_HPET_TIMER -#ifdef CONFIG_SMP static int hpet_msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force) { @@ -3297,16 +3289,12 @@ static int hpet_msi_set_affinity(struct irq_data *data, return IRQ_SET_MASK_OK_NOCOPY; } -#endif /* CONFIG_SMP */ - static struct irq_chip hpet_msi_type = { .name = "HPET_MSI", .irq_unmask = hpet_msi_unmask, .irq_mask = hpet_msi_mask, .irq_ack = ack_apic_edge, -#ifdef CONFIG_SMP .irq_set_affinity = hpet_msi_set_affinity, -#endif .irq_retrigger = ioapic_retrigger_irq, }; @@ -3341,8 +3329,6 @@ int arch_setup_hpet_msi(unsigned int irq, unsigned int id) */ #ifdef CONFIG_HT_IRQ -#ifdef CONFIG_SMP - static void target_ht_irq(unsigned int irq, unsigned int dest, u8 vector) { struct ht_irq_msg msg; @@ -3370,16 +3356,12 @@ ht_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force) return IRQ_SET_MASK_OK_NOCOPY; } -#endif - static struct irq_chip ht_irq_chip = { .name = "PCI-HT", .irq_mask = mask_ht_irq, .irq_unmask = unmask_ht_irq, .irq_ack = ack_apic_edge, -#ifdef CONFIG_SMP .irq_set_affinity = ht_set_affinity, -#endif .irq_retrigger = ioapic_retrigger_irq, }; diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c index 853902a1b7db..e0b18f3ae9a8 100644 --- a/drivers/iommu/intel_irq_remapping.c +++ b/drivers/iommu/intel_irq_remapping.c @@ -902,7 +902,6 @@ static int intel_setup_ioapic_entry(int irq, return 0; } -#ifdef CONFIG_SMP /* * Migrate the IO-APIC irq in the presence of intr-remapping. * @@ -926,6 +925,9 @@ intel_ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask, struct irte irte; int err; + if (!config_enabled(CONFIG_SMP)) + return -EINVAL; + if (!cpumask_intersects(mask, cpu_online_mask)) return -EINVAL; @@ -963,7 +965,6 @@ intel_ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask, cpumask_copy(data->affinity, mask); return 0; } -#endif static void intel_compose_msi_msg(struct pci_dev *pdev, unsigned int irq, unsigned int dest, @@ -1065,9 +1066,7 @@ struct irq_remap_ops intel_irq_remap_ops = { .reenable = reenable_irq_remapping, .enable_faulting = enable_drhd_fault_handling, .setup_ioapic_entry = intel_setup_ioapic_entry, -#ifdef CONFIG_SMP .set_affinity = intel_ioapic_set_affinity, -#endif .free_irq = free_irte, .compose_msi_msg = intel_compose_msi_msg, .msi_alloc_irq = intel_msi_alloc_irq, diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c index 40cda8e98d87..1d29b1c66e72 100644 --- a/drivers/iommu/irq_remapping.c +++ b/drivers/iommu/irq_remapping.c @@ -111,16 +111,15 @@ int setup_ioapic_remapped_entry(int irq, vector, attr); } -#ifdef CONFIG_SMP int set_remapped_irq_affinity(struct irq_data *data, const struct cpumask *mask, bool force) { - if (!remap_ops || !remap_ops->set_affinity) + if (!config_enabled(CONFIG_SMP) || !remap_ops || + !remap_ops->set_affinity) return 0; return remap_ops->set_affinity(data, mask, force); } -#endif void free_remapped_irq(int irq) { diff --git a/drivers/iommu/irq_remapping.h b/drivers/iommu/irq_remapping.h index be9d72950c51..b12974cc1dfe 100644 --- a/drivers/iommu/irq_remapping.h +++ b/drivers/iommu/irq_remapping.h @@ -59,11 +59,9 @@ struct irq_remap_ops { unsigned int, int, struct io_apic_irq_attr *); -#ifdef CONFIG_SMP /* Set the CPU affinity of a remapped interrupt */ int (*set_affinity)(struct irq_data *data, const struct cpumask *mask, bool force); -#endif /* Free an IRQ */ int (*free_irq)(int); diff --git a/include/linux/irq.h b/include/linux/irq.h index 61f5cec031e0..47a937cd84af 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -150,9 +150,7 @@ struct irq_data { void *handler_data; void *chip_data; struct msi_desc *msi_desc; -#ifdef CONFIG_SMP cpumask_var_t affinity; -#endif }; /* -- cgit v1.2.3-59-g8ed1b From 1afc56794e03229fa53cfa3c5012704d226e1dec Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 7 Jun 2012 12:11:50 +0200 Subject: netfilter: nf_ct_helper: implement variable length helper private data This patch uses the new variable length conntrack extensions. Instead of using union nf_conntrack_help that contain all the helper private data information, we allocate variable length area to store the private helper data. This patch includes the modification of all existing helpers. It also includes a couple of include header to avoid compilation warnings. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_conntrack_sip.h | 2 ++ include/net/netfilter/nf_conntrack.h | 35 ++--------------------- include/net/netfilter/nf_conntrack_helper.h | 15 +++++++++- net/ipv4/netfilter/nf_nat_amanda.c | 4 +-- net/ipv4/netfilter/nf_nat_h323.c | 8 +++--- net/ipv4/netfilter/nf_nat_pptp.c | 6 ++-- net/ipv4/netfilter/nf_nat_tftp.c | 4 +-- net/netfilter/nf_conntrack_core.c | 3 +- net/netfilter/nf_conntrack_ftp.c | 3 +- net/netfilter/nf_conntrack_h323_main.c | 16 +++++++---- net/netfilter/nf_conntrack_helper.c | 11 +++++--- net/netfilter/nf_conntrack_netlink.c | 4 +-- net/netfilter/nf_conntrack_pptp.c | 17 +++++------ net/netfilter/nf_conntrack_proto_gre.c | 16 +++++------ net/netfilter/nf_conntrack_sane.c | 4 +-- net/netfilter/nf_conntrack_sip.c | 25 ++++++++-------- net/netfilter/xt_CT.c | 44 +++++++++++++++++------------ 17 files changed, 111 insertions(+), 106 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h index 0ce91d56a5f2..0dfc8b7210a3 100644 --- a/include/linux/netfilter/nf_conntrack_sip.h +++ b/include/linux/netfilter/nf_conntrack_sip.h @@ -2,6 +2,8 @@ #define __NF_CONNTRACK_SIP_H__ #ifdef __KERNEL__ +#include + #define SIP_PORT 5060 #define SIP_TIMEOUT 3600 diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index cce7f6a798bf..f1494feba79f 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -39,36 +39,6 @@ union nf_conntrack_expect_proto { /* insert expect proto private data here */ }; -/* Add protocol helper include file here */ -#include -#include -#include -#include -#include - -/* per conntrack: application helper private data */ -union nf_conntrack_help { - /* insert conntrack helper private data (master) here */ -#if defined(CONFIG_NF_CONNTRACK_FTP) || defined(CONFIG_NF_CONNTRACK_FTP_MODULE) - struct nf_ct_ftp_master ct_ftp_info; -#endif -#if defined(CONFIG_NF_CONNTRACK_PPTP) || \ - defined(CONFIG_NF_CONNTRACK_PPTP_MODULE) - struct nf_ct_pptp_master ct_pptp_info; -#endif -#if defined(CONFIG_NF_CONNTRACK_H323) || \ - defined(CONFIG_NF_CONNTRACK_H323_MODULE) - struct nf_ct_h323_master ct_h323_info; -#endif -#if defined(CONFIG_NF_CONNTRACK_SANE) || \ - defined(CONFIG_NF_CONNTRACK_SANE_MODULE) - struct nf_ct_sane_master ct_sane_info; -#endif -#if defined(CONFIG_NF_CONNTRACK_SIP) || defined(CONFIG_NF_CONNTRACK_SIP_MODULE) - struct nf_ct_sip_master ct_sip_info; -#endif -}; - #include #include #include @@ -89,12 +59,13 @@ struct nf_conn_help { /* Helper. if any */ struct nf_conntrack_helper __rcu *helper; - union nf_conntrack_help help; - struct hlist_head expectations; /* Current number of expected connections */ u8 expecting[NF_CT_MAX_EXPECT_CLASSES]; + + /* private helper information. */ + char data[]; }; #include diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 5f5a4d9d4df5..061352f71a84 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -11,6 +11,7 @@ #define _NF_CONNTRACK_HELPER_H #include #include +#include struct module; @@ -23,6 +24,9 @@ struct nf_conntrack_helper { struct module *me; /* pointer to self */ const struct nf_conntrack_expect_policy *expect_policy; + /* length of internal data, ie. sizeof(struct nf_ct_*_master) */ + size_t data_len; + /* Tuple of things we will help (compared against server response) */ struct nf_conntrack_tuple tuple; @@ -48,7 +52,7 @@ nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum); extern int nf_conntrack_helper_register(struct nf_conntrack_helper *); extern void nf_conntrack_helper_unregister(struct nf_conntrack_helper *); -extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp); +extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, struct nf_conntrack_helper *helper, gfp_t gfp); extern int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, gfp_t flags); @@ -60,6 +64,15 @@ static inline struct nf_conn_help *nfct_help(const struct nf_conn *ct) return nf_ct_ext_find(ct, NF_CT_EXT_HELPER); } +static inline void *nfct_help_data(const struct nf_conn *ct) +{ + struct nf_conn_help *help; + + help = nf_ct_ext_find(ct, NF_CT_EXT_HELPER); + + return (void *)help->data; +} + extern int nf_conntrack_helper_init(struct net *net); extern void nf_conntrack_helper_fini(struct net *net); diff --git a/net/ipv4/netfilter/nf_nat_amanda.c b/net/ipv4/netfilter/nf_nat_amanda.c index 7b22382ff0e9..3c04d24e2976 100644 --- a/net/ipv4/netfilter/nf_nat_amanda.c +++ b/net/ipv4/netfilter/nf_nat_amanda.c @@ -13,10 +13,10 @@ #include #include -#include -#include #include #include +#include +#include #include MODULE_AUTHOR("Brian J. Murrell "); diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c index cad29c121318..c6784a18c1c4 100644 --- a/net/ipv4/netfilter/nf_nat_h323.c +++ b/net/ipv4/netfilter/nf_nat_h323.c @@ -95,7 +95,7 @@ static int set_sig_addr(struct sk_buff *skb, struct nf_conn *ct, unsigned char **data, TransportAddress *taddr, int count) { - const struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info; + const struct nf_ct_h323_master *info = nfct_help_data(ct); int dir = CTINFO2DIR(ctinfo); int i; __be16 port; @@ -178,7 +178,7 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, struct nf_conntrack_expect *rtp_exp, struct nf_conntrack_expect *rtcp_exp) { - struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info; + struct nf_ct_h323_master *info = nfct_help_data(ct); int dir = CTINFO2DIR(ctinfo); int i; u_int16_t nated_port; @@ -330,7 +330,7 @@ static int nat_h245(struct sk_buff *skb, struct nf_conn *ct, TransportAddress *taddr, __be16 port, struct nf_conntrack_expect *exp) { - struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info; + struct nf_ct_h323_master *info = nfct_help_data(ct); int dir = CTINFO2DIR(ctinfo); u_int16_t nated_port = ntohs(port); @@ -419,7 +419,7 @@ static int nat_q931(struct sk_buff *skb, struct nf_conn *ct, unsigned char **data, TransportAddress *taddr, int idx, __be16 port, struct nf_conntrack_expect *exp) { - struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info; + struct nf_ct_h323_master *info = nfct_help_data(ct); int dir = CTINFO2DIR(ctinfo); u_int16_t nated_port = ntohs(port); union nf_inet_addr addr; diff --git a/net/ipv4/netfilter/nf_nat_pptp.c b/net/ipv4/netfilter/nf_nat_pptp.c index c273d58980ae..388140881ebe 100644 --- a/net/ipv4/netfilter/nf_nat_pptp.c +++ b/net/ipv4/netfilter/nf_nat_pptp.c @@ -49,7 +49,7 @@ static void pptp_nat_expected(struct nf_conn *ct, const struct nf_nat_pptp *nat_pptp_info; struct nf_nat_ipv4_range range; - ct_pptp_info = &nfct_help(master)->help.ct_pptp_info; + ct_pptp_info = nfct_help_data(master); nat_pptp_info = &nfct_nat(master)->help.nat_pptp_info; /* And here goes the grand finale of corrosion... */ @@ -123,7 +123,7 @@ pptp_outbound_pkt(struct sk_buff *skb, __be16 new_callid; unsigned int cid_off; - ct_pptp_info = &nfct_help(ct)->help.ct_pptp_info; + ct_pptp_info = nfct_help_data(ct); nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info; new_callid = ct_pptp_info->pns_call_id; @@ -192,7 +192,7 @@ pptp_exp_gre(struct nf_conntrack_expect *expect_orig, struct nf_ct_pptp_master *ct_pptp_info; struct nf_nat_pptp *nat_pptp_info; - ct_pptp_info = &nfct_help(ct)->help.ct_pptp_info; + ct_pptp_info = nfct_help_data(ct); nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info; /* save original PAC call ID in nat_info */ diff --git a/net/ipv4/netfilter/nf_nat_tftp.c b/net/ipv4/netfilter/nf_nat_tftp.c index a2901bf829c0..9dbb8d284f99 100644 --- a/net/ipv4/netfilter/nf_nat_tftp.c +++ b/net/ipv4/netfilter/nf_nat_tftp.c @@ -8,10 +8,10 @@ #include #include -#include -#include #include #include +#include +#include #include MODULE_AUTHOR("Magnus Boden "); diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 1ee2082b81b5..cf4875565d67 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -819,7 +819,8 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, __set_bit(IPS_EXPECTED_BIT, &ct->status); ct->master = exp->master; if (exp->helper) { - help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); + help = nf_ct_helper_ext_add(ct, exp->helper, + GFP_ATOMIC); if (help) rcu_assign_pointer(help->helper, exp->helper); } diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index 44e47c9e14fb..4bb771d1f57a 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -358,7 +358,7 @@ static int help(struct sk_buff *skb, u32 seq; int dir = CTINFO2DIR(ctinfo); unsigned int uninitialized_var(matchlen), uninitialized_var(matchoff); - struct nf_ct_ftp_master *ct_ftp_info = &nfct_help(ct)->help.ct_ftp_info; + struct nf_ct_ftp_master *ct_ftp_info = nfct_help_data(ct); struct nf_conntrack_expect *exp; union nf_inet_addr *daddr; struct nf_conntrack_man cmd = {}; @@ -554,6 +554,7 @@ static int __init nf_conntrack_ftp_init(void) ftp[i][0].tuple.src.l3num = PF_INET; ftp[i][1].tuple.src.l3num = PF_INET6; for (j = 0; j < 2; j++) { + ftp[i][j].data_len = sizeof(struct nf_ct_ftp_master); ftp[i][j].tuple.src.u.tcp.port = htons(ports[i]); ftp[i][j].tuple.dst.protonum = IPPROTO_TCP; ftp[i][j].expect_policy = &ftp_exp_policy; diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index 46d69d7f1bb4..ed2199280527 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -114,7 +114,7 @@ static int get_tpkt_data(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned char **data, int *datalen, int *dataoff) { - struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info; + struct nf_ct_h323_master *info = nfct_help_data(ct); int dir = CTINFO2DIR(ctinfo); const struct tcphdr *th; struct tcphdr _tcph; @@ -618,6 +618,7 @@ static const struct nf_conntrack_expect_policy h245_exp_policy = { static struct nf_conntrack_helper nf_conntrack_helper_h245 __read_mostly = { .name = "H.245", .me = THIS_MODULE, + .data_len = sizeof(struct nf_ct_h323_master), .tuple.src.l3num = AF_UNSPEC, .tuple.dst.protonum = IPPROTO_UDP, .help = h245_help, @@ -1170,6 +1171,7 @@ static struct nf_conntrack_helper nf_conntrack_helper_q931[] __read_mostly = { { .name = "Q.931", .me = THIS_MODULE, + .data_len = sizeof(struct nf_ct_h323_master), .tuple.src.l3num = AF_INET, .tuple.src.u.tcp.port = cpu_to_be16(Q931_PORT), .tuple.dst.protonum = IPPROTO_TCP, @@ -1245,7 +1247,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct, unsigned char **data, TransportAddress *taddr, int count) { - struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info; + struct nf_ct_h323_master *info = nfct_help_data(ct); int dir = CTINFO2DIR(ctinfo); int ret = 0; int i; @@ -1360,7 +1362,7 @@ static int process_rrq(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned char **data, RegistrationRequest *rrq) { - struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info; + struct nf_ct_h323_master *info = nfct_help_data(ct); int ret; typeof(set_ras_addr_hook) set_ras_addr; @@ -1395,7 +1397,7 @@ static int process_rcf(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned char **data, RegistrationConfirm *rcf) { - struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info; + struct nf_ct_h323_master *info = nfct_help_data(ct); int dir = CTINFO2DIR(ctinfo); int ret; struct nf_conntrack_expect *exp; @@ -1444,7 +1446,7 @@ static int process_urq(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned char **data, UnregistrationRequest *urq) { - struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info; + struct nf_ct_h323_master *info = nfct_help_data(ct); int dir = CTINFO2DIR(ctinfo); int ret; typeof(set_sig_addr_hook) set_sig_addr; @@ -1476,7 +1478,7 @@ static int process_arq(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned char **data, AdmissionRequest *arq) { - const struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info; + const struct nf_ct_h323_master *info = nfct_help_data(ct); int dir = CTINFO2DIR(ctinfo); __be16 port; union nf_inet_addr addr; @@ -1743,6 +1745,7 @@ static struct nf_conntrack_helper nf_conntrack_helper_ras[] __read_mostly = { { .name = "RAS", .me = THIS_MODULE, + .data_len = sizeof(struct nf_ct_h323_master), .tuple.src.l3num = AF_INET, .tuple.src.u.udp.port = cpu_to_be16(RAS_PORT), .tuple.dst.protonum = IPPROTO_UDP, @@ -1752,6 +1755,7 @@ static struct nf_conntrack_helper nf_conntrack_helper_ras[] __read_mostly = { { .name = "RAS", .me = THIS_MODULE, + .data_len = sizeof(struct nf_ct_h323_master), .tuple.src.l3num = AF_INET6, .tuple.src.u.udp.port = cpu_to_be16(RAS_PORT), .tuple.dst.protonum = IPPROTO_UDP, diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 4fa2ff961f5a..9c18ecb0ab81 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -161,11 +161,14 @@ nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum) } EXPORT_SYMBOL_GPL(nf_conntrack_helper_try_module_get); -struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp) +struct nf_conn_help * +nf_ct_helper_ext_add(struct nf_conn *ct, + struct nf_conntrack_helper *helper, gfp_t gfp) { struct nf_conn_help *help; - help = nf_ct_ext_add(ct, NF_CT_EXT_HELPER, gfp); + help = nf_ct_ext_add_length(ct, NF_CT_EXT_HELPER, + helper->data_len, gfp); if (help) INIT_HLIST_HEAD(&help->expectations); else @@ -218,13 +221,13 @@ int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, } if (help == NULL) { - help = nf_ct_helper_ext_add(ct, flags); + help = nf_ct_helper_ext_add(ct, helper, flags); if (help == NULL) { ret = -ENOMEM; goto out; } } else { - memset(&help->help, 0, sizeof(help->help)); + memset(help->data, 0, helper->data_len); } rcu_assign_pointer(help->helper, helper); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 6f4b00a8fc73..a08892048b46 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1218,7 +1218,7 @@ ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[]) if (help->helper) return -EBUSY; /* need to zero data of old helper */ - memset(&help->help, 0, sizeof(help->help)); + memset(help->data, 0, help->helper->data_len); } else { /* we cannot set a helper for an existing conntrack */ return -EOPNOTSUPP; @@ -1440,7 +1440,7 @@ ctnetlink_create_conntrack(struct net *net, u16 zone, } else { struct nf_conn_help *help; - help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); + help = nf_ct_helper_ext_add(ct, helper, GFP_ATOMIC); if (help == NULL) { err = -ENOMEM; goto err2; diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index 31d56b23b9e9..6fed9ec35248 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -174,7 +174,7 @@ static int destroy_sibling_or_exp(struct net *net, struct nf_conn *ct, static void pptp_destroy_siblings(struct nf_conn *ct) { struct net *net = nf_ct_net(ct); - const struct nf_conn_help *help = nfct_help(ct); + const struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct); struct nf_conntrack_tuple t; nf_ct_gre_keymap_destroy(ct); @@ -182,16 +182,16 @@ static void pptp_destroy_siblings(struct nf_conn *ct) /* try original (pns->pac) tuple */ memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t)); t.dst.protonum = IPPROTO_GRE; - t.src.u.gre.key = help->help.ct_pptp_info.pns_call_id; - t.dst.u.gre.key = help->help.ct_pptp_info.pac_call_id; + t.src.u.gre.key = ct_pptp_info->pns_call_id; + t.dst.u.gre.key = ct_pptp_info->pac_call_id; if (!destroy_sibling_or_exp(net, ct, &t)) pr_debug("failed to timeout original pns->pac ct/exp\n"); /* try reply (pac->pns) tuple */ memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t)); t.dst.protonum = IPPROTO_GRE; - t.src.u.gre.key = help->help.ct_pptp_info.pac_call_id; - t.dst.u.gre.key = help->help.ct_pptp_info.pns_call_id; + t.src.u.gre.key = ct_pptp_info->pac_call_id; + t.dst.u.gre.key = ct_pptp_info->pns_call_id; if (!destroy_sibling_or_exp(net, ct, &t)) pr_debug("failed to timeout reply pac->pns ct/exp\n"); } @@ -269,7 +269,7 @@ pptp_inbound_pkt(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { - struct nf_ct_pptp_master *info = &nfct_help(ct)->help.ct_pptp_info; + struct nf_ct_pptp_master *info = nfct_help_data(ct); u_int16_t msg; __be16 cid = 0, pcid = 0; typeof(nf_nat_pptp_hook_inbound) nf_nat_pptp_inbound; @@ -396,7 +396,7 @@ pptp_outbound_pkt(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { - struct nf_ct_pptp_master *info = &nfct_help(ct)->help.ct_pptp_info; + struct nf_ct_pptp_master *info = nfct_help_data(ct); u_int16_t msg; __be16 cid = 0, pcid = 0; typeof(nf_nat_pptp_hook_outbound) nf_nat_pptp_outbound; @@ -506,7 +506,7 @@ conntrack_pptp_help(struct sk_buff *skb, unsigned int protoff, { int dir = CTINFO2DIR(ctinfo); - const struct nf_ct_pptp_master *info = &nfct_help(ct)->help.ct_pptp_info; + const struct nf_ct_pptp_master *info = nfct_help_data(ct); const struct tcphdr *tcph; struct tcphdr _tcph; const struct pptp_pkt_hdr *pptph; @@ -592,6 +592,7 @@ static const struct nf_conntrack_expect_policy pptp_exp_policy = { static struct nf_conntrack_helper pptp __read_mostly = { .name = "pptp", .me = THIS_MODULE, + .data_len = sizeof(struct nf_ct_pptp_master), .tuple.src.l3num = AF_INET, .tuple.src.u.tcp.port = cpu_to_be16(PPTP_CONTROL_PORT), .tuple.dst.protonum = IPPROTO_TCP, diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 25ba5a2f5edc..5cac41c2fa09 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -117,10 +117,10 @@ int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir, { struct net *net = nf_ct_net(ct); struct netns_proto_gre *net_gre = gre_pernet(net); - struct nf_conn_help *help = nfct_help(ct); + struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct); struct nf_ct_gre_keymap **kmp, *km; - kmp = &help->help.ct_pptp_info.keymap[dir]; + kmp = &ct_pptp_info->keymap[dir]; if (*kmp) { /* check whether it's a retransmission */ read_lock_bh(&net_gre->keymap_lock); @@ -158,19 +158,19 @@ void nf_ct_gre_keymap_destroy(struct nf_conn *ct) { struct net *net = nf_ct_net(ct); struct netns_proto_gre *net_gre = gre_pernet(net); - struct nf_conn_help *help = nfct_help(ct); + struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct); enum ip_conntrack_dir dir; pr_debug("entering for ct %p\n", ct); write_lock_bh(&net_gre->keymap_lock); for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) { - if (help->help.ct_pptp_info.keymap[dir]) { + if (ct_pptp_info->keymap[dir]) { pr_debug("removing %p from list\n", - help->help.ct_pptp_info.keymap[dir]); - list_del(&help->help.ct_pptp_info.keymap[dir]->list); - kfree(help->help.ct_pptp_info.keymap[dir]); - help->help.ct_pptp_info.keymap[dir] = NULL; + ct_pptp_info->keymap[dir]); + list_del(&ct_pptp_info->keymap[dir]->list); + kfree(ct_pptp_info->keymap[dir]); + ct_pptp_info->keymap[dir] = NULL; } } write_unlock_bh(&net_gre->keymap_lock); diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c index ec3fc18c4ef6..295429f39088 100644 --- a/net/netfilter/nf_conntrack_sane.c +++ b/net/netfilter/nf_conntrack_sane.c @@ -69,13 +69,12 @@ static int help(struct sk_buff *skb, void *sb_ptr; int ret = NF_ACCEPT; int dir = CTINFO2DIR(ctinfo); - struct nf_ct_sane_master *ct_sane_info; + struct nf_ct_sane_master *ct_sane_info = nfct_help_data(ct); struct nf_conntrack_expect *exp; struct nf_conntrack_tuple *tuple; struct sane_request *req; struct sane_reply_net_start *reply; - ct_sane_info = &nfct_help(ct)->help.ct_sane_info; /* Until there's been traffic both ways, don't look in packets. */ if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) @@ -203,6 +202,7 @@ static int __init nf_conntrack_sane_init(void) sane[i][0].tuple.src.l3num = PF_INET; sane[i][1].tuple.src.l3num = PF_INET6; for (j = 0; j < 2; j++) { + sane[i][j].data_len = sizeof(struct nf_ct_sane_master); sane[i][j].tuple.src.u.tcp.port = htons(ports[i]); sane[i][j].tuple.dst.protonum = IPPROTO_TCP; sane[i][j].expect_policy = &sane_exp_policy; diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index dfd3ff382243..758a1bacc126 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1075,12 +1075,12 @@ static int process_invite_response(struct sk_buff *skb, unsigned int dataoff, { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - struct nf_conn_help *help = nfct_help(ct); + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); if ((code >= 100 && code <= 199) || (code >= 200 && code <= 299)) return process_sdp(skb, dataoff, dptr, datalen, cseq); - else if (help->help.ct_sip_info.invite_cseq == cseq) + else if (ct_sip_info->invite_cseq == cseq) flush_expectations(ct, true); return NF_ACCEPT; } @@ -1091,12 +1091,12 @@ static int process_update_response(struct sk_buff *skb, unsigned int dataoff, { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - struct nf_conn_help *help = nfct_help(ct); + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); if ((code >= 100 && code <= 199) || (code >= 200 && code <= 299)) return process_sdp(skb, dataoff, dptr, datalen, cseq); - else if (help->help.ct_sip_info.invite_cseq == cseq) + else if (ct_sip_info->invite_cseq == cseq) flush_expectations(ct, true); return NF_ACCEPT; } @@ -1107,12 +1107,12 @@ static int process_prack_response(struct sk_buff *skb, unsigned int dataoff, { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - struct nf_conn_help *help = nfct_help(ct); + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); if ((code >= 100 && code <= 199) || (code >= 200 && code <= 299)) return process_sdp(skb, dataoff, dptr, datalen, cseq); - else if (help->help.ct_sip_info.invite_cseq == cseq) + else if (ct_sip_info->invite_cseq == cseq) flush_expectations(ct, true); return NF_ACCEPT; } @@ -1123,13 +1123,13 @@ static int process_invite_request(struct sk_buff *skb, unsigned int dataoff, { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - struct nf_conn_help *help = nfct_help(ct); + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); unsigned int ret; flush_expectations(ct, true); ret = process_sdp(skb, dataoff, dptr, datalen, cseq); if (ret == NF_ACCEPT) - help->help.ct_sip_info.invite_cseq = cseq; + ct_sip_info->invite_cseq = cseq; return ret; } @@ -1154,7 +1154,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int dataoff, { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - struct nf_conn_help *help = nfct_help(ct); + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); unsigned int matchoff, matchlen; struct nf_conntrack_expect *exp; @@ -1235,7 +1235,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int dataoff, store_cseq: if (ret == NF_ACCEPT) - help->help.ct_sip_info.register_cseq = cseq; + ct_sip_info->register_cseq = cseq; return ret; } @@ -1245,7 +1245,7 @@ static int process_register_response(struct sk_buff *skb, unsigned int dataoff, { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - struct nf_conn_help *help = nfct_help(ct); + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); union nf_inet_addr addr; __be16 port; @@ -1262,7 +1262,7 @@ static int process_register_response(struct sk_buff *skb, unsigned int dataoff, * responses, so we store the sequence number of the last valid * request and compare it here. */ - if (help->help.ct_sip_info.register_cseq != cseq) + if (ct_sip_info->register_cseq != cseq) return NF_ACCEPT; if (code >= 100 && code <= 199) @@ -1578,6 +1578,7 @@ static int __init nf_conntrack_sip_init(void) sip[i][3].help = sip_help_tcp; for (j = 0; j < ARRAY_SIZE(sip[i]); j++) { + sip[i][j].data_len = sizeof(struct nf_ct_sip_master); sip[i][j].tuple.src.u.udp.port = htons(ports[i]); sip[i][j].expect_policy = sip_exp_policy; sip[i][j].expect_class_max = SIP_EXPECT_MAX; diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index a51de9b052be..116018560c60 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -112,6 +112,8 @@ static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par) goto err3; if (info->helper[0]) { + struct nf_conntrack_helper *helper; + ret = -ENOENT; proto = xt_ct_find_proto(par); if (!proto) { @@ -120,19 +122,21 @@ static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par) goto err3; } - ret = -ENOMEM; - help = nf_ct_helper_ext_add(ct, GFP_KERNEL); - if (help == NULL) - goto err3; - ret = -ENOENT; - help->helper = nf_conntrack_helper_try_module_get(info->helper, - par->family, - proto); - if (help->helper == NULL) { + helper = nf_conntrack_helper_try_module_get(info->helper, + par->family, + proto); + if (helper == NULL) { pr_info("No such helper \"%s\"\n", info->helper); goto err3; } + + ret = -ENOMEM; + help = nf_ct_helper_ext_add(ct, helper, GFP_KERNEL); + if (help == NULL) + goto err3; + + help->helper = helper; } __set_bit(IPS_TEMPLATE_BIT, &ct->status); @@ -202,6 +206,8 @@ static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par) goto err3; if (info->helper[0]) { + struct nf_conntrack_helper *helper; + ret = -ENOENT; proto = xt_ct_find_proto(par); if (!proto) { @@ -210,19 +216,21 @@ static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par) goto err3; } - ret = -ENOMEM; - help = nf_ct_helper_ext_add(ct, GFP_KERNEL); - if (help == NULL) - goto err3; - ret = -ENOENT; - help->helper = nf_conntrack_helper_try_module_get(info->helper, - par->family, - proto); - if (help->helper == NULL) { + helper = nf_conntrack_helper_try_module_get(info->helper, + par->family, + proto); + if (helper == NULL) { pr_info("No such helper \"%s\"\n", info->helper); goto err3; } + + ret = -ENOMEM; + help = nf_ct_helper_ext_add(ct, helper, GFP_KERNEL); + if (help == NULL) + goto err3; + + help->helper = helper; } #ifdef CONFIG_NF_CONNTRACK_TIMEOUT -- cgit v1.2.3-59-g8ed1b From 9cb0176654a7dc33a32af8a0bc9e0b2f9f9ebb0f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 7 Jun 2012 12:13:39 +0200 Subject: netfilter: add glue code to integrate nfnetlink_queue and ctnetlink This patch allows you to include the conntrack information together with the packet that is sent to user-space via NFQUEUE. Previously, there was no integration between ctnetlink and nfnetlink_queue. If you wanted to access conntrack information from your libnetfilter_queue program, you required to query ctnetlink from user-space to obtain it. Thus, delaying the packet processing even more. Including the conntrack information is optional, you can set it via NFQA_CFG_F_CONNTRACK flag with the new NFQA_CFG_FLAGS attribute. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 10 +++ include/linux/netfilter/nfnetlink_queue.h | 3 + net/netfilter/core.c | 4 + net/netfilter/nf_conntrack_netlink.c | 144 +++++++++++++++++++++++++++++- net/netfilter/nfnetlink_queue.c | 48 ++++++++++ 5 files changed, 208 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 4541f33dbfc3..ba65bfbd7f74 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -393,6 +393,16 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family) extern void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *) __rcu; extern void nf_ct_attach(struct sk_buff *, struct sk_buff *); extern void (*nf_ct_destroy)(struct nf_conntrack *) __rcu; + +struct nf_conn; +struct nlattr; + +struct nfq_ct_hook { + size_t (*build_size)(const struct nf_conn *ct); + int (*build)(struct sk_buff *skb, struct nf_conn *ct); + int (*parse)(const struct nlattr *attr, struct nf_conn *ct); +}; +extern struct nfq_ct_hook *nfq_ct_hook; #else static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} #endif diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h index a6c1ddac05cc..e0d8fd8d4d24 100644 --- a/include/linux/netfilter/nfnetlink_queue.h +++ b/include/linux/netfilter/nfnetlink_queue.h @@ -42,6 +42,8 @@ enum nfqnl_attr_type { NFQA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */ NFQA_HWADDR, /* nfqnl_msg_packet_hw */ NFQA_PAYLOAD, /* opaque data payload */ + NFQA_CT, /* nf_conntrack_netlink.h */ + NFQA_CT_INFO, /* enum ip_conntrack_info */ __NFQA_MAX }; @@ -92,5 +94,6 @@ enum nfqnl_attr_config { /* Flags for NFQA_CFG_FLAGS */ #define NFQA_CFG_F_FAIL_OPEN (1 << 0) +#define NFQA_CFG_F_CONNTRACK (1 << 1) #endif /* _NFNETLINK_QUEUE_H */ diff --git a/net/netfilter/core.c b/net/netfilter/core.c index e19f3653db23..7eef8453b909 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -264,6 +264,10 @@ void nf_conntrack_destroy(struct nf_conntrack *nfct) rcu_read_unlock(); } EXPORT_SYMBOL(nf_conntrack_destroy); + +struct nfq_ct_hook *nfq_ct_hook; +EXPORT_SYMBOL_GPL(nfq_ct_hook); + #endif /* CONFIG_NF_CONNTRACK */ #ifdef CONFIG_PROC_FS diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index a08892048b46..d304d5917950 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1620,6 +1620,140 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, return err; } +#if defined(CONFIG_NETFILTER_NETLINK_QUEUE) || \ + defined(CONFIG_NETFILTER_NETLINK_QUEUE_MODULE) +static size_t +ctnetlink_nfqueue_build_size(const struct nf_conn *ct) +{ + return 3 * nla_total_size(0) /* CTA_TUPLE_ORIG|REPL|MASTER */ + + 3 * nla_total_size(0) /* CTA_TUPLE_IP */ + + 3 * nla_total_size(0) /* CTA_TUPLE_PROTO */ + + 3 * nla_total_size(sizeof(u_int8_t)) /* CTA_PROTO_NUM */ + + nla_total_size(sizeof(u_int32_t)) /* CTA_ID */ + + nla_total_size(sizeof(u_int32_t)) /* CTA_STATUS */ + + nla_total_size(sizeof(u_int32_t)) /* CTA_TIMEOUT */ + + nla_total_size(0) /* CTA_PROTOINFO */ + + nla_total_size(0) /* CTA_HELP */ + + nla_total_size(NF_CT_HELPER_NAME_LEN) /* CTA_HELP_NAME */ + + ctnetlink_secctx_size(ct) +#ifdef CONFIG_NF_NAT_NEEDED + + 2 * nla_total_size(0) /* CTA_NAT_SEQ_ADJ_ORIG|REPL */ + + 6 * nla_total_size(sizeof(u_int32_t)) /* CTA_NAT_SEQ_OFFSET */ +#endif +#ifdef CONFIG_NF_CONNTRACK_MARK + + nla_total_size(sizeof(u_int32_t)) /* CTA_MARK */ +#endif + + ctnetlink_proto_size(ct) + ; +} + +static int +ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct) +{ + struct nlattr *nest_parms; + + rcu_read_lock(); + nest_parms = nla_nest_start(skb, CTA_TUPLE_ORIG | NLA_F_NESTED); + if (!nest_parms) + goto nla_put_failure; + if (ctnetlink_dump_tuples(skb, nf_ct_tuple(ct, IP_CT_DIR_ORIGINAL)) < 0) + goto nla_put_failure; + nla_nest_end(skb, nest_parms); + + nest_parms = nla_nest_start(skb, CTA_TUPLE_REPLY | NLA_F_NESTED); + if (!nest_parms) + goto nla_put_failure; + if (ctnetlink_dump_tuples(skb, nf_ct_tuple(ct, IP_CT_DIR_REPLY)) < 0) + goto nla_put_failure; + nla_nest_end(skb, nest_parms); + + if (nf_ct_zone(ct)) { + if (nla_put_be16(skb, CTA_ZONE, htons(nf_ct_zone(ct)))) + goto nla_put_failure; + } + + if (ctnetlink_dump_id(skb, ct) < 0) + goto nla_put_failure; + + if (ctnetlink_dump_status(skb, ct) < 0) + goto nla_put_failure; + + if (ctnetlink_dump_timeout(skb, ct) < 0) + goto nla_put_failure; + + if (ctnetlink_dump_protoinfo(skb, ct) < 0) + goto nla_put_failure; + + if (ctnetlink_dump_helpinfo(skb, ct) < 0) + goto nla_put_failure; + +#ifdef CONFIG_NF_CONNTRACK_SECMARK + if (ct->secmark && ctnetlink_dump_secctx(skb, ct) < 0) + goto nla_put_failure; +#endif + if (ct->master && ctnetlink_dump_master(skb, ct) < 0) + goto nla_put_failure; + + if ((ct->status & IPS_SEQ_ADJUST) && + ctnetlink_dump_nat_seq_adj(skb, ct) < 0) + goto nla_put_failure; + +#ifdef CONFIG_NF_CONNTRACK_MARK + if (ct->mark && ctnetlink_dump_mark(skb, ct) < 0) + goto nla_put_failure; +#endif + rcu_read_unlock(); + return 0; + +nla_put_failure: + rcu_read_unlock(); + return -ENOSPC; +} + +static int +ctnetlink_nfqueue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct) +{ + int err; + + if (cda[CTA_TIMEOUT]) { + err = ctnetlink_change_timeout(ct, cda); + if (err < 0) + return err; + } + if (cda[CTA_STATUS]) { + err = ctnetlink_change_status(ct, cda); + if (err < 0) + return err; + } + if (cda[CTA_HELP]) { + err = ctnetlink_change_helper(ct, cda); + if (err < 0) + return err; + } +#if defined(CONFIG_NF_CONNTRACK_MARK) + if (cda[CTA_MARK]) + ct->mark = ntohl(nla_get_be32(cda[CTA_MARK])); +#endif + return 0; +} + +static int +ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct) +{ + struct nlattr *cda[CTA_MAX+1]; + + nla_parse_nested(cda, CTA_MAX, attr, ct_nla_policy); + + return ctnetlink_nfqueue_parse_ct((const struct nlattr **)cda, ct); +} + +static struct nfq_ct_hook ctnetlink_nfqueue_hook = { + .build_size = ctnetlink_nfqueue_build_size, + .build = ctnetlink_nfqueue_build, + .parse = ctnetlink_nfqueue_parse, +}; +#endif /* CONFIG_NETFILTER_NETLINK_QUEUE */ + /*********************************************************************** * EXPECT ***********************************************************************/ @@ -2424,7 +2558,11 @@ static int __init ctnetlink_init(void) pr_err("ctnetlink_init: cannot register pernet operations\n"); goto err_unreg_exp_subsys; } - +#if defined(CONFIG_NETFILTER_NETLINK_QUEUE) || \ + defined(CONFIG_NETFILTER_NETLINK_QUEUE_MODULE) + /* setup interaction between nf_queue and nf_conntrack_netlink. */ + RCU_INIT_POINTER(nfq_ct_hook, &ctnetlink_nfqueue_hook); +#endif return 0; err_unreg_exp_subsys: @@ -2442,6 +2580,10 @@ static void __exit ctnetlink_exit(void) unregister_pernet_subsys(&ctnetlink_net_ops); nfnetlink_subsys_unregister(&ctnl_exp_subsys); nfnetlink_subsys_unregister(&ctnl_subsys); +#if defined(CONFIG_NETFILTER_NETLINK_QUEUE) || \ + defined(CONFIG_NETFILTER_NETLINK_QUEUE_MODULE) + RCU_INIT_POINTER(nfq_ct_hook, NULL); +#endif } module_init(ctnetlink_init); diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 630da3d2c62a..647923ae9230 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -233,6 +234,9 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue, struct sk_buff *entskb = entry->skb; struct net_device *indev; struct net_device *outdev; + struct nfq_ct_hook *nfq_ct; + struct nf_conn *ct = NULL; + enum ip_conntrack_info uninitialized_var(ctinfo); size = NLMSG_SPACE(sizeof(struct nfgenmsg)) + nla_total_size(sizeof(struct nfqnl_msg_packet_hdr)) @@ -266,6 +270,17 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue, break; } + /* rcu_read_lock()ed by __nf_queue already. */ + nfq_ct = rcu_dereference(nfq_ct_hook); + if (nfq_ct != NULL && (queue->flags & NFQA_CFG_F_CONNTRACK)) { + ct = nf_ct_get(entskb, &ctinfo); + if (ct) { + if (!nf_ct_is_untracked(ct)) + size += nfq_ct->build_size(ct); + else + ct = NULL; + } + } skb = alloc_skb(size, GFP_ATOMIC); if (!skb) @@ -389,6 +404,24 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue, BUG(); } + if (ct) { + struct nlattr *nest_parms; + u_int32_t tmp; + + nest_parms = nla_nest_start(skb, NFQA_CT | NLA_F_NESTED); + if (!nest_parms) + goto nla_put_failure; + + if (nfq_ct->build(skb, ct) < 0) + goto nla_put_failure; + + nla_nest_end(skb, nest_parms); + + tmp = ctinfo; + if (nla_put_u32(skb, NFQA_CT_INFO, htonl(ctinfo))) + goto nla_put_failure; + } + nlh->nlmsg_len = skb->tail - old_tail; return skb; @@ -632,6 +665,7 @@ static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = { [NFQA_VERDICT_HDR] = { .len = sizeof(struct nfqnl_msg_verdict_hdr) }, [NFQA_MARK] = { .type = NLA_U32 }, [NFQA_PAYLOAD] = { .type = NLA_UNSPEC }, + [NFQA_CT] = { .type = NLA_UNSPEC }, }; static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = { @@ -732,6 +766,7 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, struct nfqnl_instance *queue; unsigned int verdict; struct nf_queue_entry *entry; + struct nfq_ct_hook *nfq_ct; queue = instance_lookup(queue_num); if (!queue) @@ -750,6 +785,19 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, if (entry == NULL) return -ENOENT; + rcu_read_lock(); + nfq_ct = rcu_dereference(nfq_ct_hook); + if (nfq_ct != NULL && + (queue->flags & NFQA_CFG_F_CONNTRACK) && nfqa[NFQA_CT]) { + enum ip_conntrack_info ctinfo; + struct nf_conn *ct; + + ct = nf_ct_get(entry->skb, &ctinfo); + if (ct && !nf_ct_is_untracked(ct)) + nfq_ct->parse(nfqa[NFQA_CT], ct); + } + rcu_read_unlock(); + if (nfqa[NFQA_PAYLOAD]) { if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]), nla_len(nfqa[NFQA_PAYLOAD]), entry) < 0) -- cgit v1.2.3-59-g8ed1b From 8c88f87cb27ad09086940bdd3e6955e5325ec89a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 7 Jun 2012 13:31:25 +0200 Subject: netfilter: nfnetlink_queue: add NAT TCP sequence adjustment if packet mangled User-space programs that receive traffic via NFQUEUE may mangle packets. If NAT is enabled, this usually puzzles sequence tracking, leading to traffic disruptions. With this patch, nfnl_queue will make the corresponding NAT TCP sequence adjustment if: 1) The packet has been mangled, 2) the NFQA_CFG_F_CONNTRACK flag has been set, and 3) NAT is detected. There are some records on the Internet complaning about this issue: http://stackoverflow.com/questions/260757/packet-mangling-utilities-besides-iptables By now, we only support TCP since we have no helpers for DCCP or SCTP. Better to add this if we ever have some helper over those layer 4 protocols. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 2 ++ include/net/netfilter/nf_nat_helper.h | 4 ++++ net/ipv4/netfilter/nf_nat_helper.c | 13 +++++++++++++ net/netfilter/nf_conntrack_netlink.c | 4 ++++ net/netfilter/nfnetlink_queue.c | 19 +++++++++++-------- 5 files changed, 34 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index ba65bfbd7f74..dca19e61b30a 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -401,6 +401,8 @@ struct nfq_ct_hook { size_t (*build_size)(const struct nf_conn *ct); int (*build)(struct sk_buff *skb, struct nf_conn *ct); int (*parse)(const struct nlattr *attr, struct nf_conn *ct); + void (*seq_adjust)(struct sk_buff *skb, struct nf_conn *ct, + u32 ctinfo, int off); }; extern struct nfq_ct_hook *nfq_ct_hook; #else diff --git a/include/net/netfilter/nf_nat_helper.h b/include/net/netfilter/nf_nat_helper.h index 02bb6c29dc3d..7d8fb7b46c44 100644 --- a/include/net/netfilter/nf_nat_helper.h +++ b/include/net/netfilter/nf_nat_helper.h @@ -54,4 +54,8 @@ extern void nf_nat_follow_master(struct nf_conn *ct, extern s16 nf_nat_get_offset(const struct nf_conn *ct, enum ip_conntrack_dir dir, u32 seq); + +extern void nf_nat_tcp_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, + u32 dir, int off); + #endif diff --git a/net/ipv4/netfilter/nf_nat_helper.c b/net/ipv4/netfilter/nf_nat_helper.c index af65958f6308..2e59ad0b90ca 100644 --- a/net/ipv4/netfilter/nf_nat_helper.c +++ b/net/ipv4/netfilter/nf_nat_helper.c @@ -153,6 +153,19 @@ void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo, } EXPORT_SYMBOL_GPL(nf_nat_set_seq_adjust); +void nf_nat_tcp_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, + u32 ctinfo, int off) +{ + const struct tcphdr *th; + + if (nf_ct_protonum(ct) != IPPROTO_TCP) + return; + + th = (struct tcphdr *)(skb_network_header(skb)+ ip_hdrlen(skb)); + nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off); +} +EXPORT_SYMBOL_GPL(nf_nat_tcp_seq_adjust); + static void nf_nat_csum(struct sk_buff *skb, const struct iphdr *iph, void *data, int datalen, __sum16 *check, int oldlen) { diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index d304d5917950..8be0ab9b4758 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -46,6 +46,7 @@ #ifdef CONFIG_NF_NAT_NEEDED #include #include +#include #endif #include @@ -1751,6 +1752,9 @@ static struct nfq_ct_hook ctnetlink_nfqueue_hook = { .build_size = ctnetlink_nfqueue_build_size, .build = ctnetlink_nfqueue_build, .parse = ctnetlink_nfqueue_parse, +#ifdef CONFIG_NF_NAT_NEEDED + .seq_adjust = nf_nat_tcp_seq_adjust, +#endif }; #endif /* CONFIG_NETFILTER_NETLINK_QUEUE */ diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 647923ae9230..ff82c7933dfd 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -502,12 +502,10 @@ err_out: } static int -nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e) +nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e, int diff) { struct sk_buff *nskb; - int diff; - diff = data_len - e->skb->len; if (diff < 0) { if (pskb_trim(e->skb, data_len)) return -ENOMEM; @@ -767,6 +765,8 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, unsigned int verdict; struct nf_queue_entry *entry; struct nfq_ct_hook *nfq_ct; + enum ip_conntrack_info uninitialized_var(ctinfo); + struct nf_conn *ct = NULL; queue = instance_lookup(queue_num); if (!queue) @@ -789,20 +789,23 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, nfq_ct = rcu_dereference(nfq_ct_hook); if (nfq_ct != NULL && (queue->flags & NFQA_CFG_F_CONNTRACK) && nfqa[NFQA_CT]) { - enum ip_conntrack_info ctinfo; - struct nf_conn *ct; - ct = nf_ct_get(entry->skb, &ctinfo); if (ct && !nf_ct_is_untracked(ct)) nfq_ct->parse(nfqa[NFQA_CT], ct); } - rcu_read_unlock(); if (nfqa[NFQA_PAYLOAD]) { + u16 payload_len = nla_len(nfqa[NFQA_PAYLOAD]); + int diff = payload_len - entry->skb->len; + if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]), - nla_len(nfqa[NFQA_PAYLOAD]), entry) < 0) + payload_len, entry, diff) < 0) verdict = NF_DROP; + + if (ct && (ct->status & IPS_NAT_MASK) && diff) + nfq_ct->seq_adjust(skb, ct, ctinfo, diff); } + rcu_read_unlock(); if (nfqa[NFQA_MARK]) entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); -- cgit v1.2.3-59-g8ed1b From ae243bee397102c51fbf9db440eca3b077e0e702 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 7 Jun 2012 14:19:42 +0200 Subject: netfilter: ctnetlink: add CTA_HELP_INFO attribute This attribute can be used to modify and to dump the internal protocol information. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nfnetlink_conntrack.h | 1 + include/net/netfilter/nf_conntrack_helper.h | 1 + net/netfilter/nf_conntrack_netlink.c | 23 ++++++++++++++++++----- 3 files changed, 20 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index e58e4b93c108..768883370080 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -191,6 +191,7 @@ enum ctattr_expect_nat { enum ctattr_help { CTA_HELP_UNSPEC, CTA_HELP_NAME, + CTA_HELP_INFO, __CTA_HELP_MAX }; #define CTA_HELP_MAX (__CTA_HELP_MAX - 1) diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 061352f71a84..84b24c3a3834 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -39,6 +39,7 @@ struct nf_conntrack_helper { void (*destroy)(struct nf_conn *ct); + int (*from_nlattr)(struct nlattr *attr, struct nf_conn *ct); int (*to_nlattr)(struct sk_buff *skb, const struct nf_conn *ct); unsigned int expect_class_max; }; diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 8be0ab9b4758..ae156dff4887 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -902,7 +902,8 @@ static const struct nla_policy help_nla_policy[CTA_HELP_MAX+1] = { }; static inline int -ctnetlink_parse_help(const struct nlattr *attr, char **helper_name) +ctnetlink_parse_help(const struct nlattr *attr, char **helper_name, + struct nlattr **helpinfo) { struct nlattr *tb[CTA_HELP_MAX+1]; @@ -913,6 +914,9 @@ ctnetlink_parse_help(const struct nlattr *attr, char **helper_name) *helper_name = nla_data(tb[CTA_HELP_NAME]); + if (tb[CTA_HELP_INFO]) + *helpinfo = tb[CTA_HELP_INFO]; + return 0; } @@ -1173,13 +1177,14 @@ ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[]) struct nf_conntrack_helper *helper; struct nf_conn_help *help = nfct_help(ct); char *helpname = NULL; + struct nlattr *helpinfo = NULL; int err; /* don't change helper of sibling connections */ if (ct->master) return -EBUSY; - err = ctnetlink_parse_help(cda[CTA_HELP], &helpname); + err = ctnetlink_parse_help(cda[CTA_HELP], &helpname, &helpinfo); if (err < 0) return err; @@ -1214,8 +1219,12 @@ ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[]) } if (help) { - if (help->helper == helper) + if (help->helper == helper) { + /* update private helper data if allowed. */ + if (helper->from_nlattr && helpinfo) + helper->from_nlattr(helpinfo, ct); return 0; + } if (help->helper) return -EBUSY; /* need to zero data of old helper */ @@ -1411,8 +1420,9 @@ ctnetlink_create_conntrack(struct net *net, u16 zone, rcu_read_lock(); if (cda[CTA_HELP]) { char *helpname = NULL; - - err = ctnetlink_parse_help(cda[CTA_HELP], &helpname); + struct nlattr *helpinfo = NULL; + + err = ctnetlink_parse_help(cda[CTA_HELP], &helpname, &helpinfo); if (err < 0) goto err2; @@ -1446,6 +1456,9 @@ ctnetlink_create_conntrack(struct net *net, u16 zone, err = -ENOMEM; goto err2; } + /* set private helper data if allowed. */ + if (helper->from_nlattr && helpinfo) + helper->from_nlattr(helpinfo, ct); /* not in hash table yet so not strictly necessary */ RCU_INIT_POINTER(help->helper, helper); -- cgit v1.2.3-59-g8ed1b From 12f7a505331e6b2754684b509f2ac8f0011ce644 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 13 May 2012 21:44:54 +0200 Subject: netfilter: add user-space connection tracking helper infrastructure There are good reasons to supports helpers in user-space instead: * Rapid connection tracking helper development, as developing code in user-space is usually faster. * Reliability: A buggy helper does not crash the kernel. Moreover, we can monitor the helper process and restart it in case of problems. * Security: Avoid complex string matching and mangling in kernel-space running in privileged mode. Going further, we can even think about running user-space helpers as a non-root process. * Extensibility: It allows the development of very specific helpers (most likely non-standard proprietary protocols) that are very likely not to be accepted for mainline inclusion in the form of kernel-space connection tracking helpers. This patch adds the infrastructure to allow the implementation of user-space conntrack helpers by means of the new nfnetlink subsystem `nfnetlink_cthelper' and the existing queueing infrastructure (nfnetlink_queue). I had to add the new hook NF_IP6_PRI_CONNTRACK_HELPER to register ipv[4|6]_helper which results from splitting ipv[4|6]_confirm into two pieces. This change is required not to break NAT sequence adjustment and conntrack confirmation for traffic that is enqueued to our user-space conntrack helpers. Basic operation, in a few steps: 1) Register user-space helper by means of `nfct': nfct helper add ftp inet tcp [ It must be a valid existing helper supported by conntrack-tools ] 2) Add rules to enable the FTP user-space helper which is used to track traffic going to TCP port 21. For locally generated packets: iptables -I OUTPUT -t raw -p tcp --dport 21 -j CT --helper ftp For non-locally generated packets: iptables -I PREROUTING -t raw -p tcp --dport 21 -j CT --helper ftp 3) Run the test conntrackd in helper mode (see example files under doc/helper/conntrackd.conf conntrackd 4) Generate FTP traffic going, if everything is OK, then conntrackd should create expectations (you can check that with `conntrack': conntrack -E expect [NEW] 301 proto=6 src=192.168.1.136 dst=130.89.148.12 sport=0 dport=54037 mask-src=255.255.255.255 mask-dst=255.255.255.255 sport=0 dport=65535 master-src=192.168.1.136 master-dst=130.89.148.12 sport=57127 dport=21 class=0 helper=ftp [DESTROY] 301 proto=6 src=192.168.1.136 dst=130.89.148.12 sport=0 dport=54037 mask-src=255.255.255.255 mask-dst=255.255.255.255 sport=0 dport=65535 master-src=192.168.1.136 master-dst=130.89.148.12 sport=57127 dport=21 class=0 helper=ftp This confirms that our test helper is receiving packets including the conntrack information, and adding expectations in kernel-space. The user-space helper can also store its private tracking information in the conntrack structure in the kernel via the CTA_HELP_INFO. The kernel will consider this a binary blob whose layout is unknown. This information will be included in the information that is transfered to user-space via glue code that integrates nfnetlink_queue and ctnetlink. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/Kbuild | 1 + include/linux/netfilter/nfnetlink.h | 3 +- include/linux/netfilter/nfnetlink_cthelper.h | 55 ++ include/linux/netfilter_ipv4.h | 1 + include/linux/netfilter_ipv6.h | 1 + include/net/netfilter/nf_conntrack_helper.h | 11 + net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 48 +- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 43 +- net/netfilter/Kconfig | 8 + net/netfilter/Makefile | 1 + net/netfilter/nf_conntrack_helper.c | 21 +- net/netfilter/nfnetlink_cthelper.c | 672 +++++++++++++++++++++++++ 12 files changed, 839 insertions(+), 26 deletions(-) create mode 100644 include/linux/netfilter/nfnetlink_cthelper.h create mode 100644 net/netfilter/nfnetlink_cthelper.c (limited to 'include/linux') diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index 1697036336b6..874ae8f2706b 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -10,6 +10,7 @@ header-y += nfnetlink.h header-y += nfnetlink_acct.h header-y += nfnetlink_compat.h header-y += nfnetlink_conntrack.h +header-y += nfnetlink_cthelper.h header-y += nfnetlink_cttimeout.h header-y += nfnetlink_log.h header-y += nfnetlink_queue.h diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index a1048c1587d1..18341cdb2443 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h @@ -50,7 +50,8 @@ struct nfgenmsg { #define NFNL_SUBSYS_IPSET 6 #define NFNL_SUBSYS_ACCT 7 #define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8 -#define NFNL_SUBSYS_COUNT 9 +#define NFNL_SUBSYS_CTHELPER 9 +#define NFNL_SUBSYS_COUNT 10 #ifdef __KERNEL__ diff --git a/include/linux/netfilter/nfnetlink_cthelper.h b/include/linux/netfilter/nfnetlink_cthelper.h new file mode 100644 index 000000000000..33659f6fad3e --- /dev/null +++ b/include/linux/netfilter/nfnetlink_cthelper.h @@ -0,0 +1,55 @@ +#ifndef _NFNL_CTHELPER_H_ +#define _NFNL_CTHELPER_H_ + +#define NFCT_HELPER_STATUS_DISABLED 0 +#define NFCT_HELPER_STATUS_ENABLED 1 + +enum nfnl_acct_msg_types { + NFNL_MSG_CTHELPER_NEW, + NFNL_MSG_CTHELPER_GET, + NFNL_MSG_CTHELPER_DEL, + NFNL_MSG_CTHELPER_MAX +}; + +enum nfnl_cthelper_type { + NFCTH_UNSPEC, + NFCTH_NAME, + NFCTH_TUPLE, + NFCTH_QUEUE_NUM, + NFCTH_POLICY, + NFCTH_PRIV_DATA_LEN, + NFCTH_STATUS, + __NFCTH_MAX +}; +#define NFCTH_MAX (__NFCTH_MAX - 1) + +enum nfnl_cthelper_policy_type { + NFCTH_POLICY_SET_UNSPEC, + NFCTH_POLICY_SET_NUM, + NFCTH_POLICY_SET, + NFCTH_POLICY_SET1 = NFCTH_POLICY_SET, + NFCTH_POLICY_SET2, + NFCTH_POLICY_SET3, + NFCTH_POLICY_SET4, + __NFCTH_POLICY_SET_MAX +}; +#define NFCTH_POLICY_SET_MAX (__NFCTH_POLICY_SET_MAX - 1) + +enum nfnl_cthelper_pol_type { + NFCTH_POLICY_UNSPEC, + NFCTH_POLICY_NAME, + NFCTH_POLICY_EXPECT_MAX, + NFCTH_POLICY_EXPECT_TIMEOUT, + __NFCTH_POLICY_MAX +}; +#define NFCTH_POLICY_MAX (__NFCTH_POLICY_MAX - 1) + +enum nfnl_cthelper_tuple_type { + NFCTH_TUPLE_UNSPEC, + NFCTH_TUPLE_L3PROTONUM, + NFCTH_TUPLE_L4PROTONUM, + __NFCTH_TUPLE_MAX, +}; +#define NFCTH_TUPLE_MAX (__NFCTH_TUPLE_MAX - 1) + +#endif /* _NFNL_CTHELPER_H */ diff --git a/include/linux/netfilter_ipv4.h b/include/linux/netfilter_ipv4.h index fa0946c549d3..e2b12801378d 100644 --- a/include/linux/netfilter_ipv4.h +++ b/include/linux/netfilter_ipv4.h @@ -66,6 +66,7 @@ enum nf_ip_hook_priorities { NF_IP_PRI_SECURITY = 50, NF_IP_PRI_NAT_SRC = 100, NF_IP_PRI_SELINUX_LAST = 225, + NF_IP_PRI_CONNTRACK_HELPER = 300, NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX, NF_IP_PRI_LAST = INT_MAX, }; diff --git a/include/linux/netfilter_ipv6.h b/include/linux/netfilter_ipv6.h index 57c025127f1d..7c8a513ce7a3 100644 --- a/include/linux/netfilter_ipv6.h +++ b/include/linux/netfilter_ipv6.h @@ -71,6 +71,7 @@ enum nf_ip6_hook_priorities { NF_IP6_PRI_SECURITY = 50, NF_IP6_PRI_NAT_SRC = 100, NF_IP6_PRI_SELINUX_LAST = 225, + NF_IP6_PRI_CONNTRACK_HELPER = 300, NF_IP6_PRI_LAST = INT_MAX, }; diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 84b24c3a3834..9aad956d1008 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -15,6 +15,11 @@ struct module; +enum nf_ct_helper_flags { + NF_CT_HELPER_F_USERSPACE = (1 << 0), + NF_CT_HELPER_F_CONFIGURED = (1 << 1), +}; + #define NF_CT_HELPER_NAME_LEN 16 struct nf_conntrack_helper { @@ -42,6 +47,9 @@ struct nf_conntrack_helper { int (*from_nlattr)(struct nlattr *attr, struct nf_conn *ct); int (*to_nlattr)(struct sk_buff *skb, const struct nf_conn *ct); unsigned int expect_class_max; + + unsigned int flags; + unsigned int queue_num; /* For user-space helpers. */ }; extern struct nf_conntrack_helper * @@ -96,4 +104,7 @@ nf_ct_helper_expectfn_find_by_name(const char *name); struct nf_ct_helper_expectfn * nf_ct_helper_expectfn_find_by_symbol(const void *symbol); +extern struct hlist_head *nf_ct_helper_hash; +extern unsigned int nf_ct_helper_hsize; + #endif /*_NF_CONNTRACK_HELPER_H*/ diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index d79b961a8009..e7ff2dcab6ce 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -95,11 +95,11 @@ static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, return NF_ACCEPT; } -static unsigned int ipv4_confirm(unsigned int hooknum, - struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) +static unsigned int ipv4_helper(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; @@ -110,24 +110,38 @@ static unsigned int ipv4_confirm(unsigned int hooknum, /* This is where we call the helper: as the packet goes out. */ ct = nf_ct_get(skb, &ctinfo); if (!ct || ctinfo == IP_CT_RELATED_REPLY) - goto out; + return NF_ACCEPT; help = nfct_help(ct); if (!help) - goto out; + return NF_ACCEPT; /* rcu_read_lock()ed by nf_hook_slow */ helper = rcu_dereference(help->helper); if (!helper) - goto out; + return NF_ACCEPT; ret = helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb), ct, ctinfo); - if (ret != NF_ACCEPT) { + if (ret != NF_ACCEPT && (ret & NF_VERDICT_MASK) != NF_QUEUE) { nf_log_packet(NFPROTO_IPV4, hooknum, skb, in, out, NULL, "nf_ct_%s: dropping packet", helper->name); - return ret; } + return ret; +} + +static unsigned int ipv4_confirm(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + + ct = nf_ct_get(skb, &ctinfo); + if (!ct || ctinfo == IP_CT_RELATED_REPLY) + goto out; /* adjust seqs for loopback traffic only in outgoing direction */ if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && @@ -184,6 +198,13 @@ static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = { .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_CONNTRACK, }, + { + .hook = ipv4_helper, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP_PRI_CONNTRACK_HELPER, + }, { .hook = ipv4_confirm, .owner = THIS_MODULE, @@ -191,6 +212,13 @@ static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = { .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP_PRI_CONNTRACK_CONFIRM, }, + { + .hook = ipv4_helper, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_LOCAL_IN, + .priority = NF_IP_PRI_CONNTRACK_HELPER, + }, { .hook = ipv4_confirm, .owner = THIS_MODULE, diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index fca10da80ea7..4794f96cf2e0 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -143,11 +143,11 @@ static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, return NF_ACCEPT; } -static unsigned int ipv6_confirm(unsigned int hooknum, - struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) +static unsigned int ipv6_helper(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) { struct nf_conn *ct; const struct nf_conn_help *help; @@ -161,15 +161,15 @@ static unsigned int ipv6_confirm(unsigned int hooknum, /* This is where we call the helper: as the packet goes out. */ ct = nf_ct_get(skb, &ctinfo); if (!ct || ctinfo == IP_CT_RELATED_REPLY) - goto out; + return NF_ACCEPT; help = nfct_help(ct); if (!help) - goto out; + return NF_ACCEPT; /* rcu_read_lock()ed by nf_hook_slow */ helper = rcu_dereference(help->helper); if (!helper) - goto out; + return NF_ACCEPT; protoff = nf_ct_ipv6_skip_exthdr(skb, extoff, &pnum, skb->len - extoff); @@ -179,12 +179,19 @@ static unsigned int ipv6_confirm(unsigned int hooknum, } ret = helper->help(skb, protoff, ct, ctinfo); - if (ret != NF_ACCEPT) { + if (ret != NF_ACCEPT && (ret & NF_VERDICT_MASK) != NF_QUEUE) { nf_log_packet(NFPROTO_IPV6, hooknum, skb, in, out, NULL, "nf_ct_%s: dropping packet", helper->name); - return ret; } -out: + return ret; +} + +static unsigned int ipv6_confirm(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ /* We've seen it coming out the other side: confirm it */ return nf_conntrack_confirm(skb); } @@ -253,6 +260,13 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP6_PRI_CONNTRACK, }, + { + .hook = ipv6_helper, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP6_PRI_CONNTRACK_HELPER, + }, { .hook = ipv6_confirm, .owner = THIS_MODULE, @@ -260,6 +274,13 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP6_PRI_LAST, }, + { + .hook = ipv6_helper, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_LOCAL_IN, + .priority = NF_IP6_PRI_CONNTRACK_HELPER, + }, { .hook = ipv6_confirm, .owner = THIS_MODULE, diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 209c1ed43368..aae6c628991d 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -12,6 +12,14 @@ tristate "Netfilter NFACCT over NFNETLINK interface" If this option is enabled, the kernel will include support for extended accounting via NFNETLINK. +config NETFILTER_NETLINK_CTHELPER +tristate "Netfilter CTHELPER over NFNETLINK interface" + depends on NETFILTER_ADVANCED + select NETFILTER_NETLINK + help + If this option is enabled, the kernel will include support + for user-space connection tracking helpers via NFNETLINK. + config NETFILTER_NETLINK_QUEUE tristate "Netfilter NFQUEUE over NFNETLINK interface" depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 4e7960cc7b97..2f3bc0f647ba 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_NETFILTER) = netfilter.o obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o obj-$(CONFIG_NETFILTER_NETLINK_ACCT) += nfnetlink_acct.o +obj-$(CONFIG_NETFILTER_NETLINK_CTHELPER) += nfnetlink_cthelper.o obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 9c18ecb0ab81..2918ec2e4509 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -30,8 +30,10 @@ #include static DEFINE_MUTEX(nf_ct_helper_mutex); -static struct hlist_head *nf_ct_helper_hash __read_mostly; -static unsigned int nf_ct_helper_hsize __read_mostly; +struct hlist_head *nf_ct_helper_hash __read_mostly; +EXPORT_SYMBOL_GPL(nf_ct_helper_hash); +unsigned int nf_ct_helper_hsize __read_mostly; +EXPORT_SYMBOL_GPL(nf_ct_helper_hsize); static unsigned int nf_ct_helper_count __read_mostly; static bool nf_ct_auto_assign_helper __read_mostly = true; @@ -322,6 +324,9 @@ EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol); int nf_conntrack_helper_register(struct nf_conntrack_helper *me) { + int ret = 0; + struct nf_conntrack_helper *cur; + struct hlist_node *n; unsigned int h = helper_hash(&me->tuple); BUG_ON(me->expect_policy == NULL); @@ -329,11 +334,19 @@ int nf_conntrack_helper_register(struct nf_conntrack_helper *me) BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1); mutex_lock(&nf_ct_helper_mutex); + hlist_for_each_entry(cur, n, &nf_ct_helper_hash[h], hnode) { + if (strncmp(cur->name, me->name, NF_CT_HELPER_NAME_LEN) == 0 && + cur->tuple.src.l3num == me->tuple.src.l3num && + cur->tuple.dst.protonum == me->tuple.dst.protonum) { + ret = -EEXIST; + goto out; + } + } hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]); nf_ct_helper_count++; +out: mutex_unlock(&nf_ct_helper_mutex); - - return 0; + return ret; } EXPORT_SYMBOL_GPL(nf_conntrack_helper_register); diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c new file mode 100644 index 000000000000..d6836193d479 --- /dev/null +++ b/net/netfilter/nfnetlink_cthelper.c @@ -0,0 +1,672 @@ +/* + * (C) 2012 Pablo Neira Ayuso + * + * 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 (or any later at your option). + * + * This software has been sponsored by Vyatta Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pablo Neira Ayuso "); +MODULE_DESCRIPTION("nfnl_cthelper: User-space connection tracking helpers"); + +static int +nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff, + struct nf_conn *ct, enum ip_conntrack_info ctinfo) +{ + const struct nf_conn_help *help; + struct nf_conntrack_helper *helper; + + help = nfct_help(ct); + if (help == NULL) + return NF_DROP; + + /* rcu_read_lock()ed by nf_hook_slow */ + helper = rcu_dereference(help->helper); + if (helper == NULL) + return NF_DROP; + + /* This is an user-space helper not yet configured, skip. */ + if ((helper->flags & + (NF_CT_HELPER_F_USERSPACE | NF_CT_HELPER_F_CONFIGURED)) == + NF_CT_HELPER_F_USERSPACE) + return NF_ACCEPT; + + /* If the user-space helper is not available, don't block traffic. */ + return NF_QUEUE_NR(helper->queue_num) | NF_VERDICT_FLAG_QUEUE_BYPASS; +} + +static const struct nla_policy nfnl_cthelper_tuple_pol[NFCTH_TUPLE_MAX+1] = { + [NFCTH_TUPLE_L3PROTONUM] = { .type = NLA_U16, }, + [NFCTH_TUPLE_L4PROTONUM] = { .type = NLA_U8, }, +}; + +static int +nfnl_cthelper_parse_tuple(struct nf_conntrack_tuple *tuple, + const struct nlattr *attr) +{ + struct nlattr *tb[NFCTH_TUPLE_MAX+1]; + + nla_parse_nested(tb, NFCTH_TUPLE_MAX, attr, nfnl_cthelper_tuple_pol); + + if (!tb[NFCTH_TUPLE_L3PROTONUM] || !tb[NFCTH_TUPLE_L4PROTONUM]) + return -EINVAL; + + tuple->src.l3num = ntohs(nla_get_u16(tb[NFCTH_TUPLE_L3PROTONUM])); + tuple->dst.protonum = nla_get_u8(tb[NFCTH_TUPLE_L4PROTONUM]); + + return 0; +} + +static int +nfnl_cthelper_from_nlattr(struct nlattr *attr, struct nf_conn *ct) +{ + const struct nf_conn_help *help = nfct_help(ct); + + if (help->helper->data_len == 0) + return -EINVAL; + + memcpy(&help->data, nla_data(attr), help->helper->data_len); + return 0; +} + +static int +nfnl_cthelper_to_nlattr(struct sk_buff *skb, const struct nf_conn *ct) +{ + const struct nf_conn_help *help = nfct_help(ct); + + if (help->helper->data_len && + nla_put(skb, CTA_HELP_INFO, help->helper->data_len, &help->data)) + goto nla_put_failure; + + return 0; + +nla_put_failure: + return -ENOSPC; +} + +static const struct nla_policy nfnl_cthelper_expect_pol[NFCTH_POLICY_MAX+1] = { + [NFCTH_POLICY_NAME] = { .type = NLA_NUL_STRING, + .len = NF_CT_HELPER_NAME_LEN-1 }, + [NFCTH_POLICY_EXPECT_MAX] = { .type = NLA_U32, }, + [NFCTH_POLICY_EXPECT_TIMEOUT] = { .type = NLA_U32, }, +}; + +static int +nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy, + const struct nlattr *attr) +{ + struct nlattr *tb[NFCTH_POLICY_MAX+1]; + + nla_parse_nested(tb, NFCTH_POLICY_MAX, attr, nfnl_cthelper_expect_pol); + + if (!tb[NFCTH_POLICY_NAME] || + !tb[NFCTH_POLICY_EXPECT_MAX] || + !tb[NFCTH_POLICY_EXPECT_TIMEOUT]) + return -EINVAL; + + strncpy(expect_policy->name, + nla_data(tb[NFCTH_POLICY_NAME]), NF_CT_HELPER_NAME_LEN); + expect_policy->max_expected = + ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX])); + expect_policy->timeout = + ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_TIMEOUT])); + + return 0; +} + +static const struct nla_policy +nfnl_cthelper_expect_policy_set[NFCTH_POLICY_SET_MAX+1] = { + [NFCTH_POLICY_SET_NUM] = { .type = NLA_U32, }, +}; + +static int +nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper, + const struct nlattr *attr) +{ + int i, ret; + struct nf_conntrack_expect_policy *expect_policy; + struct nlattr *tb[NFCTH_POLICY_SET_MAX+1]; + + nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr, + nfnl_cthelper_expect_policy_set); + + if (!tb[NFCTH_POLICY_SET_NUM]) + return -EINVAL; + + helper->expect_class_max = + ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM])); + + if (helper->expect_class_max != 0 && + helper->expect_class_max > NF_CT_MAX_EXPECT_CLASSES) + return -EOVERFLOW; + + expect_policy = kzalloc(sizeof(struct nf_conntrack_expect_policy) * + helper->expect_class_max, GFP_KERNEL); + if (expect_policy == NULL) + return -ENOMEM; + + for (i=0; iexpect_class_max; i++) { + if (!tb[NFCTH_POLICY_SET+i]) + goto err; + + ret = nfnl_cthelper_expect_policy(&expect_policy[i], + tb[NFCTH_POLICY_SET+i]); + if (ret < 0) + goto err; + } + helper->expect_policy = expect_policy; + return 0; +err: + kfree(expect_policy); + return -EINVAL; +} + +static int +nfnl_cthelper_create(const struct nlattr * const tb[], + struct nf_conntrack_tuple *tuple) +{ + struct nf_conntrack_helper *helper; + int ret; + + if (!tb[NFCTH_TUPLE] || !tb[NFCTH_POLICY] || !tb[NFCTH_PRIV_DATA_LEN]) + return -EINVAL; + + helper = kzalloc(sizeof(struct nf_conntrack_helper), GFP_KERNEL); + if (helper == NULL) + return -ENOMEM; + + ret = nfnl_cthelper_parse_expect_policy(helper, tb[NFCTH_POLICY]); + if (ret < 0) + goto err; + + strncpy(helper->name, nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN); + helper->data_len = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN])); + helper->flags |= NF_CT_HELPER_F_USERSPACE; + memcpy(&helper->tuple, tuple, sizeof(struct nf_conntrack_tuple)); + + helper->me = THIS_MODULE; + helper->help = nfnl_userspace_cthelper; + helper->from_nlattr = nfnl_cthelper_from_nlattr; + helper->to_nlattr = nfnl_cthelper_to_nlattr; + + /* Default to queue number zero, this can be updated at any time. */ + if (tb[NFCTH_QUEUE_NUM]) + helper->queue_num = ntohl(nla_get_be32(tb[NFCTH_QUEUE_NUM])); + + if (tb[NFCTH_STATUS]) { + int status = ntohl(nla_get_be32(tb[NFCTH_STATUS])); + + switch(status) { + case NFCT_HELPER_STATUS_ENABLED: + helper->flags |= NF_CT_HELPER_F_CONFIGURED; + break; + case NFCT_HELPER_STATUS_DISABLED: + helper->flags &= ~NF_CT_HELPER_F_CONFIGURED; + break; + } + } + + ret = nf_conntrack_helper_register(helper); + if (ret < 0) + goto err; + + return 0; +err: + kfree(helper); + return ret; +} + +static int +nfnl_cthelper_update(const struct nlattr * const tb[], + struct nf_conntrack_helper *helper) +{ + int ret; + + if (tb[NFCTH_PRIV_DATA_LEN]) + return -EBUSY; + + if (tb[NFCTH_POLICY]) { + ret = nfnl_cthelper_parse_expect_policy(helper, + tb[NFCTH_POLICY]); + if (ret < 0) + return ret; + } + if (tb[NFCTH_QUEUE_NUM]) + helper->queue_num = ntohl(nla_get_be32(tb[NFCTH_QUEUE_NUM])); + + if (tb[NFCTH_STATUS]) { + int status = ntohl(nla_get_be32(tb[NFCTH_STATUS])); + + switch(status) { + case NFCT_HELPER_STATUS_ENABLED: + helper->flags |= NF_CT_HELPER_F_CONFIGURED; + break; + case NFCT_HELPER_STATUS_DISABLED: + helper->flags &= ~NF_CT_HELPER_F_CONFIGURED; + break; + } + } + return 0; +} + +static int +nfnl_cthelper_new(struct sock *nfnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, const struct nlattr * const tb[]) +{ + const char *helper_name; + struct nf_conntrack_helper *cur, *helper = NULL; + struct nf_conntrack_tuple tuple; + struct hlist_node *n; + int ret = 0, i; + + if (!tb[NFCTH_NAME] || !tb[NFCTH_TUPLE]) + return -EINVAL; + + helper_name = nla_data(tb[NFCTH_NAME]); + + ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]); + if (ret < 0) + return ret; + + rcu_read_lock(); + for (i = 0; i < nf_ct_helper_hsize && !helper; i++) { + hlist_for_each_entry_rcu(cur, n, &nf_ct_helper_hash[i], hnode) { + + /* skip non-userspace conntrack helpers. */ + if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) + continue; + + if (strncmp(cur->name, helper_name, + NF_CT_HELPER_NAME_LEN) != 0) + continue; + + if ((tuple.src.l3num != cur->tuple.src.l3num || + tuple.dst.protonum != cur->tuple.dst.protonum)) + continue; + + if (nlh->nlmsg_flags & NLM_F_EXCL) { + ret = -EEXIST; + goto err; + } + helper = cur; + break; + } + } + rcu_read_unlock(); + + if (helper == NULL) + ret = nfnl_cthelper_create(tb, &tuple); + else + ret = nfnl_cthelper_update(tb, helper); + + return ret; +err: + rcu_read_unlock(); + return ret; +} + +static int +nfnl_cthelper_dump_tuple(struct sk_buff *skb, + struct nf_conntrack_helper *helper) +{ + struct nlattr *nest_parms; + + nest_parms = nla_nest_start(skb, NFCTH_TUPLE | NLA_F_NESTED); + if (nest_parms == NULL) + goto nla_put_failure; + + if (nla_put_be16(skb, NFCTH_TUPLE_L3PROTONUM, + htons(helper->tuple.src.l3num))) + goto nla_put_failure; + + if (nla_put_u8(skb, NFCTH_TUPLE_L4PROTONUM, helper->tuple.dst.protonum)) + goto nla_put_failure; + + nla_nest_end(skb, nest_parms); + return 0; + +nla_put_failure: + return -1; +} + +static int +nfnl_cthelper_dump_policy(struct sk_buff *skb, + struct nf_conntrack_helper *helper) +{ + int i; + struct nlattr *nest_parms1, *nest_parms2; + + nest_parms1 = nla_nest_start(skb, NFCTH_POLICY | NLA_F_NESTED); + if (nest_parms1 == NULL) + goto nla_put_failure; + + if (nla_put_be32(skb, NFCTH_POLICY_SET_NUM, + htonl(helper->expect_class_max))) + goto nla_put_failure; + + for (i=0; iexpect_class_max; i++) { + nest_parms2 = nla_nest_start(skb, + (NFCTH_POLICY_SET+i) | NLA_F_NESTED); + if (nest_parms2 == NULL) + goto nla_put_failure; + + if (nla_put_string(skb, NFCTH_POLICY_NAME, + helper->expect_policy[i].name)) + goto nla_put_failure; + + if (nla_put_be32(skb, NFCTH_POLICY_EXPECT_MAX, + htonl(helper->expect_policy[i].max_expected))) + goto nla_put_failure; + + if (nla_put_be32(skb, NFCTH_POLICY_EXPECT_TIMEOUT, + htonl(helper->expect_policy[i].timeout))) + goto nla_put_failure; + + nla_nest_end(skb, nest_parms2); + } + nla_nest_end(skb, nest_parms1); + return 0; + +nla_put_failure: + return -1; +} + +static int +nfnl_cthelper_fill_info(struct sk_buff *skb, u32 pid, u32 seq, u32 type, + int event, struct nf_conntrack_helper *helper) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfmsg; + unsigned int flags = pid ? NLM_F_MULTI : 0; + int status; + + event |= NFNL_SUBSYS_CTHELPER << 8; + nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); + if (nlh == NULL) + goto nlmsg_failure; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = AF_UNSPEC; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = 0; + + if (nla_put_string(skb, NFCTH_NAME, helper->name)) + goto nla_put_failure; + + if (nla_put_be32(skb, NFCTH_QUEUE_NUM, htonl(helper->queue_num))) + goto nla_put_failure; + + if (nfnl_cthelper_dump_tuple(skb, helper) < 0) + goto nla_put_failure; + + if (nfnl_cthelper_dump_policy(skb, helper) < 0) + goto nla_put_failure; + + if (nla_put_be32(skb, NFCTH_PRIV_DATA_LEN, htonl(helper->data_len))) + goto nla_put_failure; + + if (helper->flags & NF_CT_HELPER_F_CONFIGURED) + status = NFCT_HELPER_STATUS_ENABLED; + else + status = NFCT_HELPER_STATUS_DISABLED; + + if (nla_put_be32(skb, NFCTH_STATUS, htonl(status))) + goto nla_put_failure; + + nlmsg_end(skb, nlh); + return skb->len; + +nlmsg_failure: +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -1; +} + +static int +nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct nf_conntrack_helper *cur, *last; + struct hlist_node *n; + + rcu_read_lock(); + last = (struct nf_conntrack_helper *)cb->args[1]; + for (; cb->args[0] < nf_ct_helper_hsize; cb->args[0]++) { +restart: + hlist_for_each_entry_rcu(cur, n, + &nf_ct_helper_hash[cb->args[0]], hnode) { + + /* skip non-userspace conntrack helpers. */ + if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) + continue; + + if (cb->args[1]) { + if (cur != last) + continue; + cb->args[1] = 0; + } + if (nfnl_cthelper_fill_info(skb, + NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, + NFNL_MSG_TYPE(cb->nlh->nlmsg_type), + NFNL_MSG_CTHELPER_NEW, cur) < 0) { + cb->args[1] = (unsigned long)cur; + goto out; + } + } + } + if (cb->args[1]) { + cb->args[1] = 0; + goto restart; + } +out: + rcu_read_unlock(); + return skb->len; +} + +static int +nfnl_cthelper_get(struct sock *nfnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, const struct nlattr * const tb[]) +{ + int ret = -ENOENT, i; + struct nf_conntrack_helper *cur; + struct hlist_node *n; + struct sk_buff *skb2; + char *helper_name = NULL; + struct nf_conntrack_tuple tuple; + bool tuple_set = false; + + if (nlh->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = nfnl_cthelper_dump_table, + }; + return netlink_dump_start(nfnl, skb, nlh, &c); + } + + if (tb[NFCTH_NAME]) + helper_name = nla_data(tb[NFCTH_NAME]); + + if (tb[NFCTH_TUPLE]) { + ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]); + if (ret < 0) + return ret; + + tuple_set = true; + } + + for (i = 0; i < nf_ct_helper_hsize; i++) { + hlist_for_each_entry_rcu(cur, n, &nf_ct_helper_hash[i], hnode) { + + /* skip non-userspace conntrack helpers. */ + if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) + continue; + + if (helper_name && strncmp(cur->name, helper_name, + NF_CT_HELPER_NAME_LEN) != 0) { + continue; + } + if (tuple_set && + (tuple.src.l3num != cur->tuple.src.l3num || + tuple.dst.protonum != cur->tuple.dst.protonum)) + continue; + + skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (skb2 == NULL) { + ret = -ENOMEM; + break; + } + + ret = nfnl_cthelper_fill_info(skb2, NETLINK_CB(skb).pid, + nlh->nlmsg_seq, + NFNL_MSG_TYPE(nlh->nlmsg_type), + NFNL_MSG_CTHELPER_NEW, cur); + if (ret <= 0) { + kfree_skb(skb2); + break; + } + + ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).pid, + MSG_DONTWAIT); + if (ret > 0) + ret = 0; + + /* this avoids a loop in nfnetlink. */ + return ret == -EAGAIN ? -ENOBUFS : ret; + } + } + return ret; +} + +static int +nfnl_cthelper_del(struct sock *nfnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, const struct nlattr * const tb[]) +{ + char *helper_name = NULL; + struct nf_conntrack_helper *cur; + struct hlist_node *n, *tmp; + struct nf_conntrack_tuple tuple; + bool tuple_set = false, found = false; + int i, j = 0, ret; + + if (tb[NFCTH_NAME]) + helper_name = nla_data(tb[NFCTH_NAME]); + + if (tb[NFCTH_TUPLE]) { + ret = nfnl_cthelper_parse_tuple(&tuple, tb[NFCTH_TUPLE]); + if (ret < 0) + return ret; + + tuple_set = true; + } + + for (i = 0; i < nf_ct_helper_hsize; i++) { + hlist_for_each_entry_safe(cur, n, tmp, &nf_ct_helper_hash[i], + hnode) { + /* skip non-userspace conntrack helpers. */ + if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) + continue; + + j++; + + if (helper_name && strncmp(cur->name, helper_name, + NF_CT_HELPER_NAME_LEN) != 0) { + continue; + } + if (tuple_set && + (tuple.src.l3num != cur->tuple.src.l3num || + tuple.dst.protonum != cur->tuple.dst.protonum)) + continue; + + found = true; + nf_conntrack_helper_unregister(cur); + } + } + /* Make sure we return success if we flush and there is no helpers */ + return (found || j == 0) ? 0 : -ENOENT; +} + +static const struct nla_policy nfnl_cthelper_policy[NFCTH_MAX+1] = { + [NFCTH_NAME] = { .type = NLA_NUL_STRING, + .len = NF_CT_HELPER_NAME_LEN-1 }, + [NFCTH_QUEUE_NUM] = { .type = NLA_U32, }, +}; + +static const struct nfnl_callback nfnl_cthelper_cb[NFNL_MSG_CTHELPER_MAX] = { + [NFNL_MSG_CTHELPER_NEW] = { .call = nfnl_cthelper_new, + .attr_count = NFCTH_MAX, + .policy = nfnl_cthelper_policy }, + [NFNL_MSG_CTHELPER_GET] = { .call = nfnl_cthelper_get, + .attr_count = NFCTH_MAX, + .policy = nfnl_cthelper_policy }, + [NFNL_MSG_CTHELPER_DEL] = { .call = nfnl_cthelper_del, + .attr_count = NFCTH_MAX, + .policy = nfnl_cthelper_policy }, +}; + +static const struct nfnetlink_subsystem nfnl_cthelper_subsys = { + .name = "cthelper", + .subsys_id = NFNL_SUBSYS_CTHELPER, + .cb_count = NFNL_MSG_CTHELPER_MAX, + .cb = nfnl_cthelper_cb, +}; + +MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTHELPER); + +static int __init nfnl_cthelper_init(void) +{ + int ret; + + ret = nfnetlink_subsys_register(&nfnl_cthelper_subsys); + if (ret < 0) { + pr_err("nfnl_cthelper: cannot register with nfnetlink.\n"); + goto err_out; + } + return 0; +err_out: + return ret; +} + +static void __exit nfnl_cthelper_exit(void) +{ + struct nf_conntrack_helper *cur; + struct hlist_node *n, *tmp; + int i; + + nfnetlink_subsys_unregister(&nfnl_cthelper_subsys); + + for (i=0; iflags & NF_CT_HELPER_F_USERSPACE)) + continue; + + nf_conntrack_helper_unregister(cur); + } + } +} + +module_init(nfnl_cthelper_init); +module_exit(nfnl_cthelper_exit); -- cgit v1.2.3-59-g8ed1b From fbebb9fd22581b6422d60669c4ff86ce99d6cdba Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 16 Jun 2012 14:40:22 -0600 Subject: PCI: add infrastructure for devices with broken INTx masking pci_intx_mask_supported() assumes INTx masking is supported if the PCI_COMMAND_INTX_DISABLE bit is writable. But when that bit is set, some devices don't actually mask INTx or update PCI_STATUS_INTERRUPT as we expect. This patch adds a way for quirks to identify these broken devices. [bhelgaas: split out from Chelsio quirk addition] Signed-off-by: Jan Kiszka Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 3 +++ drivers/pci/quirks.c | 10 ++++++++++ include/linux/pci.h | 1 + 3 files changed, 14 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..9ae517a68360 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2876,6 +2876,9 @@ bool pci_intx_mask_supported(struct pci_dev *dev) bool mask_supported = false; u16 orig, new; + if (dev->broken_intx_masking) + return false; + pci_cfg_access_lock(dev); pci_read_config_word(dev, PCI_COMMAND, &orig); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 2a7521677541..cc13415416d7 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2929,6 +2929,16 @@ static void __devinit disable_igfx_irq(struct pci_dev *dev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0102, disable_igfx_irq); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq); +/* + * Some devices may pass our check in pci_intx_mask_supported if + * PCI_COMMAND_INTX_DISABLE works though they actually do not properly + * support this feature. + */ +static void __devinit quirk_broken_intx_masking(struct pci_dev *dev) +{ + dev->broken_intx_masking = 1; +} + static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) { diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..40a039f1dffb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -324,6 +324,7 @@ struct pci_dev { unsigned int is_hotplug_bridge:1; unsigned int __aer_firmware_first_valid:1; unsigned int __aer_firmware_first:1; + unsigned int broken_intx_masking:1; pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ -- cgit v1.2.3-59-g8ed1b From 6f0b2c696ca340cc2da381fe693fda3f8fdb2149 Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Mon, 11 Jun 2012 17:41:08 +0530 Subject: regulator: Add ramp_delay configuration to constraints For some hardwares ramp_delay for BUCKs is a configurable parameter which can be configured through DT or board file.This patch adds ramp_delay to regulator constraints and allow user to configure it for regulators which supports this feature, through DT or board file. It will provide two ways of setting the ramp_delay for a regulator: First, by setting it as constraints in board file(for configurable regulators) and set_machine_constraints() will take care of setting it on hardware by calling(the provided) .set_ramp_delay() operation(callback). Second, by setting it as data in regulator_desc(as fixed/default ramp_delay rate) for a regulator in driver. regulator_set_voltage_time_sel() will give preference to constraints->ramp_delay while reading ramp_delay rate for regulator. Similarly users should also take care accordingly while refering ramp_delay rate(in case of implementing their private .set_voltage_time_sel() callbacks for different regulators). [Rewrote subject for 80 columns -- broonie] Signed-off-by: Yadwinder Singh Brar Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/regulator.txt | 1 + drivers/regulator/core.c | 23 ++++++++++++++++++---- drivers/regulator/of_regulator.c | 6 +++++- include/linux/regulator/driver.h | 3 +++ include/linux/regulator/machine.h | 3 +++ 5 files changed, 31 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt index 5b7a408acdaa..d0a7b1296a36 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.txt +++ b/Documentation/devicetree/bindings/regulator/regulator.txt @@ -10,6 +10,7 @@ Optional properties: - regulator-always-on: boolean, regulator should never be disabled - regulator-boot-on: bootloader/firmware enabled regulator - -supply: phandle to the parent supply/regulator node +- regulator-ramp-delay: ramp delay for regulator(in mV/uS) Example: diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 6ffca9b32388..b615ae6606db 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -967,6 +967,14 @@ static int set_machine_constraints(struct regulator_dev *rdev, } } + if (rdev->constraints->ramp_delay && ops->set_ramp_delay) { + ret = ops->set_ramp_delay(rdev, rdev->constraints->ramp_delay); + if (ret < 0) { + rdev_err(rdev, "failed to set ramp_delay\n"); + goto out; + } + } + print_constraints(rdev); return 0; out: @@ -2296,10 +2304,17 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int old_selector, unsigned int new_selector) { - if (rdev->desc->ramp_delay && rdev->desc->uV_step) - return DIV_ROUND_UP(rdev->desc->uV_step * - abs(new_selector - old_selector), - rdev->desc->ramp_delay * 1000); + if (rdev->desc->uV_step) { + if (rdev->constraints->ramp_delay) + return DIV_ROUND_UP(rdev->desc->uV_step * + abs(new_selector - old_selector), + rdev->constraints->ramp_delay * 1000); + if (rdev->desc->ramp_delay) + return DIV_ROUND_UP(rdev->desc->uV_step * + abs(new_selector - old_selector), + rdev->desc->ramp_delay * 1000); + rdev_warn(rdev, "ramp_delay not set\n"); + } return 0; } diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 56593b75168a..e2a731079066 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -20,7 +20,7 @@ static void of_get_regulation_constraints(struct device_node *np, struct regulator_init_data **init_data) { const __be32 *min_uV, *max_uV, *uV_offset; - const __be32 *min_uA, *max_uA; + const __be32 *min_uA, *max_uA, *ramp_delay; struct regulation_constraints *constraints = &(*init_data)->constraints; constraints->name = of_get_property(np, "regulator-name", NULL); @@ -60,6 +60,10 @@ static void of_get_regulation_constraints(struct device_node *np, constraints->always_on = true; else /* status change should be possible if not always on. */ constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS; + + ramp_delay = of_get_property(np, "regulator-ramp-delay", NULL); + if (ramp_delay) + constraints->min_uV = be32_to_cpu(*ramp_delay); } /** diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index ae5c25379237..ddc155d262da 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -67,6 +67,8 @@ enum regulator_status { * * @enable_time: Time taken for the regulator voltage output voltage to * stabilise after being enabled, in microseconds. + * @set_ramp_delay: Set the ramp delay for the regulator. The driver should + * select ramp delay equal to or less than(closest) ramp_delay. * @set_voltage_time_sel: Time taken for the regulator voltage output voltage * to stabilise after being set to a new value, in microseconds. * The function provides the from and to voltage selector, the @@ -113,6 +115,7 @@ struct regulator_ops { /* Time taken to enable or set voltage on the regulator */ int (*enable_time) (struct regulator_dev *); + int (*set_ramp_delay) (struct regulator_dev *, int ramp_delay); int (*set_voltage_time_sel) (struct regulator_dev *, unsigned int old_selector, unsigned int new_selector); diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index b02108446be7..5f37ad3cc172 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -92,6 +92,7 @@ struct regulator_state { * mode. * @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: mV/us) */ struct regulation_constraints { @@ -125,6 +126,8 @@ struct regulation_constraints { /* mode to set on startup */ unsigned int initial_mode; + unsigned int ramp_delay; + /* constraint flags */ unsigned always_on:1; /* regulator never off when system is on */ unsigned boot_on:1; /* bootloader/firmware enabled regulator */ -- cgit v1.2.3-59-g8ed1b From 6863ca6227598d15c372f1e03449bbb4cfbcca7f Mon Sep 17 00:00:00 2001 From: Krystian Garbaciak Date: Fri, 15 Jun 2012 11:23:56 +0100 Subject: regmap: Add support for register indirect addressing. Devices with register paging or indirectly accessed registers can configure register mapping to map those on virtual address range. During access to virtually mapped register range, indirect addressing is processed automatically, in following steps: 1. selector for page or indirect register is updated (when needed); 2. register in data window is accessed. Configuration should provide minimum and maximum register for virtual range, details of selector field for page selection, minimum and maximum register of data window for indirect access. Virtual range registers are managed by cache as well as direct access registers. In order to make indirect access more efficient, selector register should be declared as non-volatile, if possible. struct regmap_config is extended with the following: struct regmap_range_cfg *ranges; unsigned int n_ranges; [Also reordered debugfs init to later on since the cleanup code was conflicting with the new cleanup code for ranges anyway -- broonie] Signed-off-by: Krystian Garbaciak Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 17 ++++ drivers/base/regmap/regmap.c | 201 ++++++++++++++++++++++++++++++++++++++++- include/linux/regmap.h | 39 ++++++++ 3 files changed, 252 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index b986b8660b0c..80f9ab9c3aa4 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -95,6 +95,9 @@ struct regmap { /* if set, converts bulk rw to single rw */ bool use_single_rw; + + struct rb_root range_tree; + void *selector_work_buf; /* Scratch buffer used for selector */ }; struct regcache_ops { @@ -115,6 +118,20 @@ bool regmap_precious(struct regmap *map, unsigned int reg); int _regmap_write(struct regmap *map, unsigned int reg, unsigned int val); +struct regmap_range_node { + struct rb_node node; + + unsigned int range_min; + unsigned int range_max; + + unsigned int selector_reg; + unsigned int selector_mask; + int selector_shift; + + unsigned int window_start; + unsigned int window_len; +}; + #ifdef CONFIG_DEBUG_FS extern void regmap_debugfs_initcall(void); extern void regmap_debugfs_init(struct regmap *map, const char *name); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 652017991da6..83a0166420a4 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -15,12 +15,17 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include #include "internal.h" +static int _regmap_update_bits(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change); + bool regmap_writeable(struct regmap *map, unsigned int reg) { if (map->max_register && reg > map->max_register) @@ -208,6 +213,67 @@ static void dev_get_regmap_release(struct device *dev, void *res) */ } +static bool _regmap_range_add(struct regmap *map, + struct regmap_range_node *data) +{ + struct rb_root *root = &map->range_tree; + struct rb_node **new = &(root->rb_node), *parent = NULL; + + while (*new) { + struct regmap_range_node *this = + container_of(*new, struct regmap_range_node, node); + + parent = *new; + if (data->range_max < this->range_min) + new = &((*new)->rb_left); + else if (data->range_min > this->range_max) + new = &((*new)->rb_right); + else + return false; + } + + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); + + return true; +} + +static struct regmap_range_node *_regmap_range_lookup(struct regmap *map, + unsigned int reg) +{ + struct rb_node *node = map->range_tree.rb_node; + + while (node) { + struct regmap_range_node *this = + container_of(node, struct regmap_range_node, node); + + if (reg < this->range_min) + node = node->rb_left; + else if (reg > this->range_max) + node = node->rb_right; + else + return this; + } + + return NULL; +} + +static void regmap_range_exit(struct regmap *map) +{ + struct rb_node *next; + struct regmap_range_node *range_node; + + next = rb_first(&map->range_tree); + while (next) { + range_node = rb_entry(next, struct regmap_range_node, node); + next = rb_next(&range_node->node); + rb_erase(&range_node->node, &map->range_tree); + kfree(range_node); + } + + kfree(map->selector_work_buf); +} + /** * regmap_init(): Initialise register map * @@ -227,6 +293,7 @@ struct regmap *regmap_init(struct device *dev, { struct regmap *map, **m; int ret = -EINVAL; + int i, j; if (!bus || !config) goto err; @@ -364,27 +431,88 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } - regmap_debugfs_init(map, config->name); + map->range_tree = RB_ROOT; + for (i = 0; i < config->n_ranges; i++) { + const struct regmap_range_cfg *range_cfg = &config->ranges[i]; + struct regmap_range_node *new; + + /* Sanity check */ + if (range_cfg->range_max < range_cfg->range_min || + range_cfg->range_max > map->max_register || + range_cfg->selector_reg > map->max_register || + range_cfg->window_len == 0) + goto err_range; + + /* Make sure, that this register range has no selector + or data window within its boundary */ + for (j = 0; j < config->n_ranges; j++) { + unsigned sel_reg = config->ranges[j].selector_reg; + unsigned win_min = config->ranges[j].window_start; + unsigned win_max = win_min + + config->ranges[j].window_len - 1; + + if (range_cfg->range_min <= sel_reg && + sel_reg <= range_cfg->range_max) { + goto err_range; + } + + if (!(win_max < range_cfg->range_min || + win_min > range_cfg->range_max)) { + goto err_range; + } + } + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (new == NULL) { + ret = -ENOMEM; + goto err_range; + } + + new->range_min = range_cfg->range_min; + new->range_max = range_cfg->range_max; + new->selector_reg = range_cfg->selector_reg; + new->selector_mask = range_cfg->selector_mask; + new->selector_shift = range_cfg->selector_shift; + new->window_start = range_cfg->window_start; + new->window_len = range_cfg->window_len; + + if (_regmap_range_add(map, new) == false) { + kfree(new); + goto err_range; + } + + if (map->selector_work_buf == NULL) { + map->selector_work_buf = + kzalloc(map->format.buf_size, GFP_KERNEL); + if (map->selector_work_buf == NULL) { + ret = -ENOMEM; + goto err_range; + } + } + } ret = regcache_init(map, config); if (ret < 0) - goto err_debugfs; + goto err_range; + + regmap_debugfs_init(map, config->name); /* Add a devres resource for dev_get_regmap() */ m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL); if (!m) { ret = -ENOMEM; - goto err_cache; + goto err_debugfs; } *m = map; devres_add(dev, m); return map; -err_cache: - regcache_exit(map); err_debugfs: regmap_debugfs_exit(map); + regcache_exit(map); +err_range: + regmap_range_exit(map); kfree(map->work_buf); err_map: kfree(map); @@ -481,6 +609,7 @@ void regmap_exit(struct regmap *map) { regcache_exit(map); regmap_debugfs_exit(map); + regmap_range_exit(map); if (map->bus->free_context) map->bus->free_context(map->bus_context); kfree(map->work_buf); @@ -526,6 +655,56 @@ struct regmap *dev_get_regmap(struct device *dev, const char *name) } EXPORT_SYMBOL_GPL(dev_get_regmap); +static int _regmap_select_page(struct regmap *map, unsigned int *reg, + unsigned int val_num) +{ + struct regmap_range_node *range; + void *orig_work_buf; + unsigned int win_offset; + unsigned int win_page; + bool page_chg; + int ret; + + range = _regmap_range_lookup(map, *reg); + if (range) { + win_offset = (*reg - range->range_min) % range->window_len; + win_page = (*reg - range->range_min) / range->window_len; + + if (val_num > 1) { + /* Bulk write shouldn't cross range boundary */ + if (*reg + val_num - 1 > range->range_max) + return -EINVAL; + + /* ... or single page boundary */ + if (val_num > range->window_len - win_offset) + return -EINVAL; + } + + /* It is possible to have selector register inside data window. + In that case, selector register is located on every page and + it needs no page switching, when accessed alone. */ + if (val_num > 1 || + range->window_start + win_offset != range->selector_reg) { + /* Use separate work_buf during page switching */ + orig_work_buf = map->work_buf; + map->work_buf = map->selector_work_buf; + + ret = _regmap_update_bits(map, range->selector_reg, + range->selector_mask, + win_page << range->selector_shift, + &page_chg); + if (ret < 0) + return ret; + + map->work_buf = orig_work_buf; + } + + *reg = range->window_start + win_offset; + } + + return 0; +} + static int _regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len) { @@ -563,6 +742,10 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, } } + ret = _regmap_select_page(map, ®, val_len / map->format.val_bytes); + if (ret < 0) + return ret; + map->format.format_reg(map->work_buf, reg, map->reg_shift); u8[0] |= map->write_flag_mask; @@ -626,6 +809,10 @@ int _regmap_write(struct regmap *map, unsigned int reg, trace_regmap_reg_write(map->dev, reg, val); if (map->format.format_write) { + ret = _regmap_select_page(map, ®, 1); + if (ret < 0) + return ret; + map->format.format_write(map, reg, val); trace_regmap_hw_write_start(map->dev, reg, 1); @@ -783,6 +970,10 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, u8 *u8 = map->work_buf; int ret; + ret = _regmap_select_page(map, ®, val_len / map->format.val_bytes); + if (ret < 0) + return ret; + map->format.format_reg(map->work_buf, reg, map->reg_shift); /* diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 56af22ec9aba..5f69d4ad3eb1 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -14,12 +14,14 @@ */ #include +#include struct module; struct device; struct i2c_client; struct spi_device; struct regmap; +struct regmap_range_cfg; /* An enum of all the supported cache types */ enum regcache_type { @@ -84,6 +86,9 @@ struct reg_default { * @reg_defaults_raw: Power on reset values for registers (for use with * register cache support). * @num_reg_defaults_raw: Number of elements in reg_defaults_raw. + * + * @ranges: Array of configuration entries for virtual address ranges. + * @num_ranges: Number of range configuration entries. */ struct regmap_config { const char *name; @@ -109,6 +114,40 @@ struct regmap_config { u8 write_flag_mask; bool use_single_rw; + + const struct regmap_range_cfg *ranges; + unsigned int n_ranges; +}; + +/** + * Configuration for indirectly accessed or paged registers. + * Registers, mapped to this virtual range, are accessed in two steps: + * 1. page selector register update; + * 2. access through data window registers. + * + * @range_min: Address of the lowest register address in virtual range. + * @range_max: Address of the highest register in virtual range. + * + * @page_sel_reg: Register with selector field. + * @page_sel_mask: Bit shift for selector value. + * @page_sel_shift: Bit mask for selector value. + * + * @window_start: Address of first (lowest) register in data window. + * @window_len: Number of registers in data window. + */ +struct regmap_range_cfg { + /* Registers of virtual address range */ + unsigned int range_min; + unsigned int range_max; + + /* Page selector for indirect addressing */ + unsigned int selector_reg; + unsigned int selector_mask; + int selector_shift; + + /* Data window (per each page) */ + unsigned int window_start; + unsigned int window_len; }; typedef int (*regmap_hw_write)(void *context, const void *data, -- cgit v1.2.3-59-g8ed1b From ea38d13fd1666bc030cb1c0feec5b0da2f89f9b2 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 18 Jun 2012 14:03:16 +0800 Subject: regulator: core: Change the unit of ramp_delay from mV/uS to uV/uS This change makes it possible to set ramp_delay with 0.xxx mV/uS without truncation issue. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/regulator.txt | 2 +- drivers/regulator/core.c | 4 ++-- include/linux/regulator/driver.h | 2 +- include/linux/regulator/machine.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt index d0a7b1296a36..bec5d5747411 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.txt +++ b/Documentation/devicetree/bindings/regulator/regulator.txt @@ -10,7 +10,7 @@ Optional properties: - regulator-always-on: boolean, regulator should never be disabled - regulator-boot-on: bootloader/firmware enabled regulator - -supply: phandle to the parent supply/regulator node -- regulator-ramp-delay: ramp delay for regulator(in mV/uS) +- regulator-ramp-delay: ramp delay for regulator(in uV/uS) Example: diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 73a3d874ca6e..ce0a3462e0de 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2319,11 +2319,11 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, if (rdev->desc->uV_step) { return DIV_ROUND_UP(rdev->desc->uV_step * abs(new_selector - old_selector), - ramp_delay * 1000); + ramp_delay); } else if (rdev->desc->volt_table) { return DIV_ROUND_UP(abs(rdev->desc->volt_table[new_selector] - rdev->desc->volt_table[old_selector]), - ramp_delay * 1000); + ramp_delay); } else { rdev_warn(rdev, "Unsupported voltage mapping settings\n"); } diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index ddc155d262da..84f999ed394b 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -173,7 +173,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) - * @ramp_delay: Time to settle down after voltage change (unit: mV/us) + * @ramp_delay: Time to settle down after voltage change (unit: uV/us) * @volt_table: Voltage mapping table (if table based mapping) * * @vsel_reg: Register for selector when using regulator_regmap_X_voltage_ diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 5f37ad3cc172..40dd0a394cfa 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -92,7 +92,7 @@ struct regulator_state { * mode. * @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: mV/us) + * @ramp_delay: Time to settle down after voltage change (unit: uV/us) */ struct regulation_constraints { -- cgit v1.2.3-59-g8ed1b From 0cda4c023132aa93f2dd94811061f812e88daf4c Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 15 Jun 2012 14:31:33 +0800 Subject: perf: Introduce perf_pmu_migrate_context() Originally from Peter Zijlstra. The helper migrates perf events from one cpu to another cpu. Signed-off-by: Zheng Yan Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1339741902-8449-5-git-send-email-zheng.z.yan@intel.com Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 2 ++ kernel/events/core.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) (limited to 'include/linux') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 1ce887abcc5c..76c5c8b724a7 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1107,6 +1107,8 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, struct task_struct *task, perf_overflow_handler_t callback, void *context); +extern void perf_pmu_migrate_context(struct pmu *pmu, + int src_cpu, int dst_cpu); extern u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running); diff --git a/kernel/events/core.c b/kernel/events/core.c index fa36a39e8bb7..f1cf0edeb39a 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1645,6 +1645,8 @@ perf_install_in_context(struct perf_event_context *ctx, lockdep_assert_held(&ctx->mutex); event->ctx = ctx; + if (event->cpu != -1) + event->cpu = cpu; if (!task) { /* @@ -6379,6 +6381,7 @@ SYSCALL_DEFINE5(perf_event_open, mutex_lock(&ctx->mutex); if (move_group) { + synchronize_rcu(); perf_install_in_context(ctx, group_leader, event->cpu); get_ctx(ctx); list_for_each_entry(sibling, &group_leader->sibling_list, @@ -6484,6 +6487,39 @@ err: } EXPORT_SYMBOL_GPL(perf_event_create_kernel_counter); +void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu) +{ + struct perf_event_context *src_ctx; + struct perf_event_context *dst_ctx; + struct perf_event *event, *tmp; + LIST_HEAD(events); + + src_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, src_cpu)->ctx; + dst_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, dst_cpu)->ctx; + + mutex_lock(&src_ctx->mutex); + list_for_each_entry_safe(event, tmp, &src_ctx->event_list, + event_entry) { + perf_remove_from_context(event); + put_ctx(src_ctx); + list_add(&event->event_entry, &events); + } + mutex_unlock(&src_ctx->mutex); + + synchronize_rcu(); + + mutex_lock(&dst_ctx->mutex); + list_for_each_entry_safe(event, tmp, &events, event_entry) { + list_del(&event->event_entry); + if (event->state >= PERF_EVENT_STATE_OFF) + event->state = PERF_EVENT_STATE_INACTIVE; + perf_install_in_context(dst_ctx, event, dst_cpu); + get_ctx(dst_ctx); + } + mutex_unlock(&dst_ctx->mutex); +} +EXPORT_SYMBOL_GPL(perf_pmu_migrate_context); + static void sync_child_event(struct perf_event *child_event, struct task_struct *child) { -- cgit v1.2.3-59-g8ed1b From 7c94ee2e0917b2ea56498bff939c8aa55da27207 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 15 Jun 2012 14:31:37 +0800 Subject: perf/x86: Add Intel Nehalem and Sandy Bridge-EP uncore support The uncore subsystem in Sandy Bridge-EP consists of 8 components: Ubox, Cacheing Agent, Home Agent, Memory controller, Power Control, QPI Link Layer, R2PCIe, R3QPI. Signed-off-by: Zheng Yan Signed-off-by: Peter Zijlstra Cc: Linus Torvalds Cc: Andrew Morton Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/1339741902-8449-9-git-send-email-zheng.z.yan@intel.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel_uncore.c | 484 ++++++++++++++++++++++++++ arch/x86/kernel/cpu/perf_event_intel_uncore.h | 86 +++++ include/linux/pci_ids.h | 11 + 3 files changed, 581 insertions(+) (limited to 'include/linux') diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c index e20c65a0e108..d34f68bf990b 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c @@ -21,6 +21,482 @@ DEFINE_UNCORE_FORMAT_ATTR(edge, edge, "config:18"); DEFINE_UNCORE_FORMAT_ATTR(inv, inv, "config:23"); DEFINE_UNCORE_FORMAT_ATTR(cmask5, cmask, "config:24-28"); DEFINE_UNCORE_FORMAT_ATTR(cmask8, cmask, "config:24-31"); +DEFINE_UNCORE_FORMAT_ATTR(thresh8, thresh, "config:24-31"); +DEFINE_UNCORE_FORMAT_ATTR(thresh5, thresh, "config:24-28"); +DEFINE_UNCORE_FORMAT_ATTR(occ_sel, occ_sel, "config:14-15"); +DEFINE_UNCORE_FORMAT_ATTR(occ_invert, occ_invert, "config:30"); +DEFINE_UNCORE_FORMAT_ATTR(occ_edge, occ_edge, "config:14-51"); + +/* Sandy Bridge-EP uncore support */ +static void snbep_uncore_pci_disable_box(struct intel_uncore_box *box) +{ + struct pci_dev *pdev = box->pci_dev; + int box_ctl = uncore_pci_box_ctl(box); + u32 config; + + pci_read_config_dword(pdev, box_ctl, &config); + config |= SNBEP_PMON_BOX_CTL_FRZ; + pci_write_config_dword(pdev, box_ctl, config); +} + +static void snbep_uncore_pci_enable_box(struct intel_uncore_box *box) +{ + struct pci_dev *pdev = box->pci_dev; + int box_ctl = uncore_pci_box_ctl(box); + u32 config; + + pci_read_config_dword(pdev, box_ctl, &config); + config &= ~SNBEP_PMON_BOX_CTL_FRZ; + pci_write_config_dword(pdev, box_ctl, config); +} + +static void snbep_uncore_pci_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, hwc->config_base, hwc->config | + SNBEP_PMON_CTL_EN); +} + +static void snbep_uncore_pci_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, hwc->config_base, hwc->config); +} + +static u64 snbep_uncore_pci_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; + + pci_read_config_dword(pdev, hwc->event_base, (u32 *)&count); + pci_read_config_dword(pdev, hwc->event_base + 4, (u32 *)&count + 1); + return count; +} + +static void snbep_uncore_pci_init_box(struct intel_uncore_box *box) +{ + struct pci_dev *pdev = box->pci_dev; + pci_write_config_dword(pdev, SNBEP_PCI_PMON_BOX_CTL, + SNBEP_PMON_BOX_CTL_INT); +} + +static void snbep_uncore_msr_disable_box(struct intel_uncore_box *box) +{ + u64 config; + unsigned msr; + + msr = uncore_msr_box_ctl(box); + if (msr) { + rdmsrl(msr, config); + config |= SNBEP_PMON_BOX_CTL_FRZ; + wrmsrl(msr, config); + return; + } +} + +static void snbep_uncore_msr_enable_box(struct intel_uncore_box *box) +{ + u64 config; + unsigned msr; + + msr = uncore_msr_box_ctl(box); + if (msr) { + rdmsrl(msr, config); + config &= ~SNBEP_PMON_BOX_CTL_FRZ; + wrmsrl(msr, config); + return; + } +} + +static void snbep_uncore_msr_enable_event(struct intel_uncore_box *box, + struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + + wrmsrl(hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN); +} + +static void snbep_uncore_msr_disable_event(struct intel_uncore_box *box, + struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + + wrmsrl(hwc->config_base, hwc->config); +} + +static u64 snbep_uncore_msr_read_counter(struct intel_uncore_box *box, + struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + u64 count; + + rdmsrl(hwc->event_base, count); + return count; +} + +static void snbep_uncore_msr_init_box(struct intel_uncore_box *box) +{ + unsigned msr = uncore_msr_box_ctl(box); + if (msr) + wrmsrl(msr, SNBEP_PMON_BOX_CTL_INT); +} + +static struct attribute *snbep_uncore_formats_attr[] = { + &format_attr_event.attr, + &format_attr_umask.attr, + &format_attr_edge.attr, + &format_attr_inv.attr, + &format_attr_thresh8.attr, + NULL, +}; + +static struct attribute *snbep_uncore_ubox_formats_attr[] = { + &format_attr_event.attr, + &format_attr_umask.attr, + &format_attr_edge.attr, + &format_attr_inv.attr, + &format_attr_thresh5.attr, + NULL, +}; + +static struct attribute *snbep_uncore_pcu_formats_attr[] = { + &format_attr_event.attr, + &format_attr_occ_sel.attr, + &format_attr_edge.attr, + &format_attr_inv.attr, + &format_attr_thresh5.attr, + &format_attr_occ_invert.attr, + &format_attr_occ_edge.attr, + NULL, +}; + +static struct uncore_event_desc snbep_uncore_imc_events[] = { + INTEL_UNCORE_EVENT_DESC(CLOCKTICKS, "config=0xffff"), + /* read */ + INTEL_UNCORE_EVENT_DESC(CAS_COUNT_RD, "event=0x4,umask=0x3"), + /* write */ + INTEL_UNCORE_EVENT_DESC(CAS_COUNT_WR, "event=0x4,umask=0xc"), + { /* end: all zeroes */ }, +}; + +static struct uncore_event_desc snbep_uncore_qpi_events[] = { + INTEL_UNCORE_EVENT_DESC(CLOCKTICKS, "event=0x14"), + /* outgoing data+nondata flits */ + INTEL_UNCORE_EVENT_DESC(TxL_FLITS_ACTIVE, "event=0x0,umask=0x6"), + /* DRS data received */ + INTEL_UNCORE_EVENT_DESC(DRS_DATA, "event=0x2,umask=0x8"), + /* NCB data received */ + INTEL_UNCORE_EVENT_DESC(NCB_DATA, "event=0x3,umask=0x4"), + { /* end: all zeroes */ }, +}; + +static struct attribute_group snbep_uncore_format_group = { + .name = "format", + .attrs = snbep_uncore_formats_attr, +}; + +static struct attribute_group snbep_uncore_ubox_format_group = { + .name = "format", + .attrs = snbep_uncore_ubox_formats_attr, +}; + +static struct attribute_group snbep_uncore_pcu_format_group = { + .name = "format", + .attrs = snbep_uncore_pcu_formats_attr, +}; + +static struct intel_uncore_ops snbep_uncore_msr_ops = { + .init_box = snbep_uncore_msr_init_box, + .disable_box = snbep_uncore_msr_disable_box, + .enable_box = snbep_uncore_msr_enable_box, + .disable_event = snbep_uncore_msr_disable_event, + .enable_event = snbep_uncore_msr_enable_event, + .read_counter = snbep_uncore_msr_read_counter, +}; + +static struct intel_uncore_ops snbep_uncore_pci_ops = { + .init_box = snbep_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_uncore_pci_enable_event, + .read_counter = snbep_uncore_pci_read_counter, +}; + +static struct event_constraint snbep_uncore_cbox_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x01, 0x1), + UNCORE_EVENT_CONSTRAINT(0x02, 0x3), + UNCORE_EVENT_CONSTRAINT(0x04, 0x3), + UNCORE_EVENT_CONSTRAINT(0x05, 0x3), + UNCORE_EVENT_CONSTRAINT(0x07, 0x3), + UNCORE_EVENT_CONSTRAINT(0x11, 0x1), + UNCORE_EVENT_CONSTRAINT(0x12, 0x3), + UNCORE_EVENT_CONSTRAINT(0x13, 0x3), + UNCORE_EVENT_CONSTRAINT(0x1b, 0xc), + UNCORE_EVENT_CONSTRAINT(0x1c, 0xc), + UNCORE_EVENT_CONSTRAINT(0x1d, 0xc), + UNCORE_EVENT_CONSTRAINT(0x1e, 0xc), + UNCORE_EVENT_CONSTRAINT(0x1f, 0xe), + UNCORE_EVENT_CONSTRAINT(0x21, 0x3), + UNCORE_EVENT_CONSTRAINT(0x23, 0x3), + UNCORE_EVENT_CONSTRAINT(0x31, 0x3), + UNCORE_EVENT_CONSTRAINT(0x32, 0x3), + UNCORE_EVENT_CONSTRAINT(0x33, 0x3), + UNCORE_EVENT_CONSTRAINT(0x34, 0x3), + UNCORE_EVENT_CONSTRAINT(0x35, 0x3), + UNCORE_EVENT_CONSTRAINT(0x36, 0x1), + UNCORE_EVENT_CONSTRAINT(0x37, 0x3), + UNCORE_EVENT_CONSTRAINT(0x38, 0x3), + UNCORE_EVENT_CONSTRAINT(0x39, 0x3), + UNCORE_EVENT_CONSTRAINT(0x3b, 0x1), + EVENT_CONSTRAINT_END +}; + +static struct event_constraint snbep_uncore_r2pcie_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x10, 0x3), + UNCORE_EVENT_CONSTRAINT(0x11, 0x3), + UNCORE_EVENT_CONSTRAINT(0x12, 0x1), + UNCORE_EVENT_CONSTRAINT(0x23, 0x3), + UNCORE_EVENT_CONSTRAINT(0x24, 0x3), + UNCORE_EVENT_CONSTRAINT(0x25, 0x3), + UNCORE_EVENT_CONSTRAINT(0x26, 0x3), + UNCORE_EVENT_CONSTRAINT(0x32, 0x3), + UNCORE_EVENT_CONSTRAINT(0x33, 0x3), + UNCORE_EVENT_CONSTRAINT(0x34, 0x3), + EVENT_CONSTRAINT_END +}; + +static struct event_constraint snbep_uncore_r3qpi_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x10, 0x3), + UNCORE_EVENT_CONSTRAINT(0x11, 0x3), + UNCORE_EVENT_CONSTRAINT(0x12, 0x3), + UNCORE_EVENT_CONSTRAINT(0x13, 0x1), + UNCORE_EVENT_CONSTRAINT(0x20, 0x3), + UNCORE_EVENT_CONSTRAINT(0x21, 0x3), + UNCORE_EVENT_CONSTRAINT(0x22, 0x3), + UNCORE_EVENT_CONSTRAINT(0x23, 0x3), + UNCORE_EVENT_CONSTRAINT(0x24, 0x3), + UNCORE_EVENT_CONSTRAINT(0x25, 0x3), + UNCORE_EVENT_CONSTRAINT(0x26, 0x3), + UNCORE_EVENT_CONSTRAINT(0x30, 0x3), + UNCORE_EVENT_CONSTRAINT(0x31, 0x3), + UNCORE_EVENT_CONSTRAINT(0x32, 0x3), + UNCORE_EVENT_CONSTRAINT(0x33, 0x3), + UNCORE_EVENT_CONSTRAINT(0x34, 0x3), + UNCORE_EVENT_CONSTRAINT(0x36, 0x3), + UNCORE_EVENT_CONSTRAINT(0x37, 0x3), + EVENT_CONSTRAINT_END +}; + +static struct intel_uncore_type snbep_uncore_ubox = { + .name = "ubox", + .num_counters = 2, + .num_boxes = 1, + .perf_ctr_bits = 44, + .fixed_ctr_bits = 48, + .perf_ctr = SNBEP_U_MSR_PMON_CTR0, + .event_ctl = SNBEP_U_MSR_PMON_CTL0, + .event_mask = SNBEP_U_MSR_PMON_RAW_EVENT_MASK, + .fixed_ctr = SNBEP_U_MSR_PMON_UCLK_FIXED_CTR, + .fixed_ctl = SNBEP_U_MSR_PMON_UCLK_FIXED_CTL, + .ops = &snbep_uncore_msr_ops, + .format_group = &snbep_uncore_ubox_format_group, +}; + +static struct intel_uncore_type snbep_uncore_cbox = { + .name = "cbox", + .num_counters = 4, + .num_boxes = 8, + .perf_ctr_bits = 44, + .event_ctl = SNBEP_C0_MSR_PMON_CTL0, + .perf_ctr = SNBEP_C0_MSR_PMON_CTR0, + .event_mask = SNBEP_PMON_RAW_EVENT_MASK, + .box_ctl = SNBEP_C0_MSR_PMON_BOX_CTL, + .msr_offset = SNBEP_CBO_MSR_OFFSET, + .constraints = snbep_uncore_cbox_constraints, + .ops = &snbep_uncore_msr_ops, + .format_group = &snbep_uncore_format_group, +}; + +static struct intel_uncore_type snbep_uncore_pcu = { + .name = "pcu", + .num_counters = 4, + .num_boxes = 1, + .perf_ctr_bits = 48, + .perf_ctr = SNBEP_PCU_MSR_PMON_CTR0, + .event_ctl = SNBEP_PCU_MSR_PMON_CTL0, + .event_mask = SNBEP_PCU_MSR_PMON_RAW_EVENT_MASK, + .box_ctl = SNBEP_PCU_MSR_PMON_BOX_CTL, + .ops = &snbep_uncore_msr_ops, + .format_group = &snbep_uncore_pcu_format_group, +}; + +static struct intel_uncore_type *snbep_msr_uncores[] = { + &snbep_uncore_ubox, + &snbep_uncore_cbox, + &snbep_uncore_pcu, + NULL, +}; + +#define SNBEP_UNCORE_PCI_COMMON_INIT() \ + .perf_ctr = SNBEP_PCI_PMON_CTR0, \ + .event_ctl = SNBEP_PCI_PMON_CTL0, \ + .event_mask = SNBEP_PMON_RAW_EVENT_MASK, \ + .box_ctl = SNBEP_PCI_PMON_BOX_CTL, \ + .ops = &snbep_uncore_pci_ops, \ + .format_group = &snbep_uncore_format_group + +static struct intel_uncore_type snbep_uncore_ha = { + .name = "ha", + .num_counters = 4, + .num_boxes = 1, + .perf_ctr_bits = 48, + SNBEP_UNCORE_PCI_COMMON_INIT(), +}; + +static struct intel_uncore_type snbep_uncore_imc = { + .name = "imc", + .num_counters = 4, + .num_boxes = 4, + .perf_ctr_bits = 48, + .fixed_ctr_bits = 48, + .fixed_ctr = SNBEP_MC_CHy_PCI_PMON_FIXED_CTR, + .fixed_ctl = SNBEP_MC_CHy_PCI_PMON_FIXED_CTL, + .event_descs = snbep_uncore_imc_events, + SNBEP_UNCORE_PCI_COMMON_INIT(), +}; + +static struct intel_uncore_type snbep_uncore_qpi = { + .name = "qpi", + .num_counters = 4, + .num_boxes = 2, + .perf_ctr_bits = 48, + .event_descs = snbep_uncore_qpi_events, + SNBEP_UNCORE_PCI_COMMON_INIT(), +}; + + +static struct intel_uncore_type snbep_uncore_r2pcie = { + .name = "r2pcie", + .num_counters = 4, + .num_boxes = 1, + .perf_ctr_bits = 44, + .constraints = snbep_uncore_r2pcie_constraints, + SNBEP_UNCORE_PCI_COMMON_INIT(), +}; + +static struct intel_uncore_type snbep_uncore_r3qpi = { + .name = "r3qpi", + .num_counters = 3, + .num_boxes = 2, + .perf_ctr_bits = 44, + .constraints = snbep_uncore_r3qpi_constraints, + SNBEP_UNCORE_PCI_COMMON_INIT(), +}; + +static struct intel_uncore_type *snbep_pci_uncores[] = { + &snbep_uncore_ha, + &snbep_uncore_imc, + &snbep_uncore_qpi, + &snbep_uncore_r2pcie, + &snbep_uncore_r3qpi, + NULL, +}; + +static DEFINE_PCI_DEVICE_TABLE(snbep_uncore_pci_ids) = { + { /* Home Agent */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_HA), + .driver_data = (unsigned long)&snbep_uncore_ha, + }, + { /* MC Channel 0 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_IMC0), + .driver_data = (unsigned long)&snbep_uncore_imc, + }, + { /* MC Channel 1 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_IMC1), + .driver_data = (unsigned long)&snbep_uncore_imc, + }, + { /* MC Channel 2 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_IMC2), + .driver_data = (unsigned long)&snbep_uncore_imc, + }, + { /* MC Channel 3 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_IMC3), + .driver_data = (unsigned long)&snbep_uncore_imc, + }, + { /* QPI Port 0 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_QPI0), + .driver_data = (unsigned long)&snbep_uncore_qpi, + }, + { /* QPI Port 1 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_QPI1), + .driver_data = (unsigned long)&snbep_uncore_qpi, + }, + { /* P2PCIe */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_R2PCIE), + .driver_data = (unsigned long)&snbep_uncore_r2pcie, + }, + { /* R3QPI Link 0 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_R3QPI0), + .driver_data = (unsigned long)&snbep_uncore_r3qpi, + }, + { /* R3QPI Link 1 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_R3QPI1), + .driver_data = (unsigned long)&snbep_uncore_r3qpi, + }, + { /* end: all zeroes */ } +}; + +static struct pci_driver snbep_uncore_pci_driver = { + .name = "snbep_uncore", + .id_table = snbep_uncore_pci_ids, +}; + +/* + * build pci bus to socket mapping + */ +static void snbep_pci2phy_map_init(void) +{ + struct pci_dev *ubox_dev = NULL; + int i, bus, nodeid; + u32 config; + + while (1) { + /* find the UBOX device */ + ubox_dev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_JAKETOWN_UBOX, + ubox_dev); + if (!ubox_dev) + break; + bus = ubox_dev->bus->number; + /* get the Node ID of the local register */ + pci_read_config_dword(ubox_dev, 0x40, &config); + nodeid = config; + /* get the Node ID mapping */ + pci_read_config_dword(ubox_dev, 0x54, &config); + /* + * every three bits in the Node ID mapping register maps + * to a particular node. + */ + for (i = 0; i < 8; i++) { + if (nodeid == ((config >> (3 * i)) & 0x7)) { + pcibus_to_physid[bus] = i; + break; + } + } + }; + return; +} +/* end of Sandy Bridge-EP uncore support */ + /* Sandy Bridge uncore support */ static void snb_uncore_msr_enable_event(struct intel_uncore_box *box, @@ -894,6 +1370,11 @@ static int __init uncore_pci_init(void) int ret; switch (boot_cpu_data.x86_model) { + case 45: /* Sandy Bridge-EP */ + pci_uncores = snbep_pci_uncores; + uncore_pci_driver = &snbep_uncore_pci_driver; + snbep_pci2phy_map_init(); + break; default: return 0; } @@ -1155,6 +1636,9 @@ static int __init uncore_cpu_init(void) case 42: /* Sandy Bridge */ msr_uncores = snb_msr_uncores; break; + case 45: /* Sandy Birdge-EP */ + msr_uncores = snbep_msr_uncores; + break; default: return 0; } diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.h b/arch/x86/kernel/cpu/perf_event_intel_uncore.h index aa01df87b8de..4d52db0d1dfe 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.h +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.h @@ -65,6 +65,92 @@ #define NHM_UNC_PERFEVTSEL0 0x3c0 #define NHM_UNC_UNCORE_PMC0 0x3b0 +/* SNB-EP Box level control */ +#define SNBEP_PMON_BOX_CTL_RST_CTRL (1 << 0) +#define SNBEP_PMON_BOX_CTL_RST_CTRS (1 << 1) +#define SNBEP_PMON_BOX_CTL_FRZ (1 << 8) +#define SNBEP_PMON_BOX_CTL_FRZ_EN (1 << 16) +#define SNBEP_PMON_BOX_CTL_INT (SNBEP_PMON_BOX_CTL_RST_CTRL | \ + SNBEP_PMON_BOX_CTL_RST_CTRS | \ + SNBEP_PMON_BOX_CTL_FRZ_EN) +/* SNB-EP event control */ +#define SNBEP_PMON_CTL_EV_SEL_MASK 0x000000ff +#define SNBEP_PMON_CTL_UMASK_MASK 0x0000ff00 +#define SNBEP_PMON_CTL_RST (1 << 17) +#define SNBEP_PMON_CTL_EDGE_DET (1 << 18) +#define SNBEP_PMON_CTL_EV_SEL_EXT (1 << 21) /* only for QPI */ +#define SNBEP_PMON_CTL_EN (1 << 22) +#define SNBEP_PMON_CTL_INVERT (1 << 23) +#define SNBEP_PMON_CTL_TRESH_MASK 0xff000000 +#define SNBEP_PMON_RAW_EVENT_MASK (SNBEP_PMON_CTL_EV_SEL_MASK | \ + SNBEP_PMON_CTL_UMASK_MASK | \ + SNBEP_PMON_CTL_EDGE_DET | \ + SNBEP_PMON_CTL_INVERT | \ + SNBEP_PMON_CTL_TRESH_MASK) + +/* SNB-EP Ubox event control */ +#define SNBEP_U_MSR_PMON_CTL_TRESH_MASK 0x1f000000 +#define SNBEP_U_MSR_PMON_RAW_EVENT_MASK \ + (SNBEP_PMON_CTL_EV_SEL_MASK | \ + SNBEP_PMON_CTL_UMASK_MASK | \ + SNBEP_PMON_CTL_EDGE_DET | \ + SNBEP_PMON_CTL_INVERT | \ + SNBEP_U_MSR_PMON_CTL_TRESH_MASK) + +/* SNB-EP PCU event control */ +#define SNBEP_PCU_MSR_PMON_CTL_OCC_SEL_MASK 0x0000c000 +#define SNBEP_PCU_MSR_PMON_CTL_TRESH_MASK 0x1f000000 +#define SNBEP_PCU_MSR_PMON_CTL_OCC_INVERT (1 << 30) +#define SNBEP_PCU_MSR_PMON_CTL_OCC_EDGE_DET (1 << 31) +#define SNBEP_PCU_MSR_PMON_RAW_EVENT_MASK \ + (SNBEP_PMON_CTL_EV_SEL_MASK | \ + SNBEP_PCU_MSR_PMON_CTL_OCC_SEL_MASK | \ + SNBEP_PMON_CTL_EDGE_DET | \ + SNBEP_PMON_CTL_INVERT | \ + SNBEP_PCU_MSR_PMON_CTL_TRESH_MASK | \ + SNBEP_PCU_MSR_PMON_CTL_OCC_INVERT | \ + SNBEP_PCU_MSR_PMON_CTL_OCC_EDGE_DET) + +/* SNB-EP pci control register */ +#define SNBEP_PCI_PMON_BOX_CTL 0xf4 +#define SNBEP_PCI_PMON_CTL0 0xd8 +/* SNB-EP pci counter register */ +#define SNBEP_PCI_PMON_CTR0 0xa0 + +/* SNB-EP home agent register */ +#define SNBEP_HA_PCI_PMON_BOX_ADDRMATCH0 0x40 +#define SNBEP_HA_PCI_PMON_BOX_ADDRMATCH1 0x44 +#define SNBEP_HA_PCI_PMON_BOX_OPCODEMATCH 0x48 +/* SNB-EP memory controller register */ +#define SNBEP_MC_CHy_PCI_PMON_FIXED_CTL 0xf0 +#define SNBEP_MC_CHy_PCI_PMON_FIXED_CTR 0xd0 +/* SNB-EP QPI register */ +#define SNBEP_Q_Py_PCI_PMON_PKT_MATCH0 0x228 +#define SNBEP_Q_Py_PCI_PMON_PKT_MATCH1 0x22c +#define SNBEP_Q_Py_PCI_PMON_PKT_MASK0 0x238 +#define SNBEP_Q_Py_PCI_PMON_PKT_MASK1 0x23c + +/* SNB-EP Ubox register */ +#define SNBEP_U_MSR_PMON_CTR0 0xc16 +#define SNBEP_U_MSR_PMON_CTL0 0xc10 + +#define SNBEP_U_MSR_PMON_UCLK_FIXED_CTL 0xc08 +#define SNBEP_U_MSR_PMON_UCLK_FIXED_CTR 0xc09 + +/* SNB-EP Cbo register */ +#define SNBEP_C0_MSR_PMON_CTR0 0xd16 +#define SNBEP_C0_MSR_PMON_CTL0 0xd10 +#define SNBEP_C0_MSR_PMON_BOX_FILTER 0xd14 +#define SNBEP_C0_MSR_PMON_BOX_CTL 0xd04 +#define SNBEP_CBO_MSR_OFFSET 0x20 + +/* SNB-EP PCU register */ +#define SNBEP_PCU_MSR_PMON_CTR0 0xc36 +#define SNBEP_PCU_MSR_PMON_CTL0 0xc30 +#define SNBEP_PCU_MSR_PMON_BOX_FILTER 0xc34 +#define SNBEP_PCU_MSR_PMON_BOX_CTL 0xc24 +#define SNBEP_PCU_MSR_CORE_C3_CTR 0x3fc +#define SNBEP_PCU_MSR_CORE_C6_CTR 0x3fd struct intel_uncore_ops; struct intel_uncore_pmu; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index ab741b0d0074..5f187026b812 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2755,6 +2755,17 @@ #define PCI_DEVICE_ID_INTEL_IOAT_SNB7 0x3c27 #define PCI_DEVICE_ID_INTEL_IOAT_SNB8 0x3c2e #define PCI_DEVICE_ID_INTEL_IOAT_SNB9 0x3c2f +#define PCI_DEVICE_ID_INTEL_UNC_HA 0x3c46 +#define PCI_DEVICE_ID_INTEL_UNC_IMC0 0x3cb0 +#define PCI_DEVICE_ID_INTEL_UNC_IMC1 0x3cb1 +#define PCI_DEVICE_ID_INTEL_UNC_IMC2 0x3cb4 +#define PCI_DEVICE_ID_INTEL_UNC_IMC3 0x3cb5 +#define PCI_DEVICE_ID_INTEL_UNC_QPI0 0x3c41 +#define PCI_DEVICE_ID_INTEL_UNC_QPI1 0x3c42 +#define PCI_DEVICE_ID_INTEL_UNC_R2PCIE 0x3c43 +#define PCI_DEVICE_ID_INTEL_UNC_R3QPI0 0x3c44 +#define PCI_DEVICE_ID_INTEL_UNC_R3QPI1 0x3c45 +#define PCI_DEVICE_ID_INTEL_JAKETOWN_UBOX 0x3ce0 #define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f #define PCI_DEVICE_ID_INTEL_5100_16 0x65f0 #define PCI_DEVICE_ID_INTEL_5100_21 0x65f5 -- cgit v1.2.3-59-g8ed1b From ace3d8614ab0e6544f5f85921085b55b915fe9aa Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:14 +0200 Subject: HID: uhid: add internal message buffer When receiving messages from the HID subsystem, we need to process them and store them in an internal buffer so user-space can read() on the char device to retrieve the messages. This adds a static buffer for 32 messages to each uhid device. Each message is dynamically allocated so the uhid_device structure does not get too big. uhid_queue() adds a message to the buffer. If the buffer is full, the message is discarded. uhid_queue_event() is an helper for messages without payload. This also adds a public header: uhid.h. It contains the declarations for the user-space API. It is built around "struct uhid_event" which contains a type field which specifies the event type and each event can then add a variable-length payload. For now, there is only a dummy event but later patches will add new event types and payloads. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/Kbuild | 1 + include/linux/uhid.h | 33 ++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 include/linux/uhid.h (limited to 'include/linux') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 5b02d6cb0e60..05ef4b05a63e 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -25,16 +25,81 @@ #include #define UHID_NAME "uhid" +#define UHID_BUFSIZE 32 + +struct uhid_device { + struct hid_device *hid; + + wait_queue_head_t waitq; + spinlock_t qlock; + __u8 head; + __u8 tail; + struct uhid_event *outq[UHID_BUFSIZE]; +}; static struct miscdevice uhid_misc; +static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev) +{ + __u8 newhead; + + newhead = (uhid->head + 1) % UHID_BUFSIZE; + + if (newhead != uhid->tail) { + uhid->outq[uhid->head] = ev; + uhid->head = newhead; + wake_up_interruptible(&uhid->waitq); + } else { + hid_warn(uhid->hid, "Output queue is full\n"); + kfree(ev); + } +} + +static int uhid_queue_event(struct uhid_device *uhid, __u32 event) +{ + unsigned long flags; + struct uhid_event *ev; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = event; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { + struct uhid_device *uhid; + + uhid = kzalloc(sizeof(*uhid), GFP_KERNEL); + if (!uhid) + return -ENOMEM; + + spin_lock_init(&uhid->qlock); + init_waitqueue_head(&uhid->waitq); + + file->private_data = uhid; + nonseekable_open(inode, file); + return 0; } static int uhid_char_release(struct inode *inode, struct file *file) { + struct uhid_device *uhid = file->private_data; + unsigned int i; + + for (i = 0; i < UHID_BUFSIZE; ++i) + kfree(uhid->outq[i]); + + kfree(uhid); + return 0; } diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 39737839ce29..8cdabecfbe27 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -373,6 +373,7 @@ header-y += tty.h header-y += types.h header-y += udf_fs_i.h header-y += udp.h +header-y += uhid.h header-y += uinput.h header-y += uio.h header-y += ultrasound.h diff --git a/include/linux/uhid.h b/include/linux/uhid.h new file mode 100644 index 000000000000..16b786a2b18f --- /dev/null +++ b/include/linux/uhid.h @@ -0,0 +1,33 @@ +#ifndef __UHID_H_ +#define __UHID_H_ + +/* + * User-space I/O driver support for HID subsystem + * Copyright (c) 2012 David Herrmann + */ + +/* + * 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. + */ + +/* + * Public header for user-space communication. We try to keep every structure + * aligned but to be safe we also use __attribute__((__packed__)). Therefore, + * the communication should be ABI compatible even between architectures. + */ + +#include +#include + +enum uhid_event_type { + UHID_DUMMY, +}; + +struct uhid_event { + __u32 type; +} __attribute__((__packed__)); + +#endif /* __UHID_H_ */ -- cgit v1.2.3-59-g8ed1b From d365c6cfd337a2bccdc65eacce271a311ea1072c Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:18 +0200 Subject: HID: uhid: add UHID_CREATE and UHID_DESTROY events UHID_CREATE and UHID_DESTROY are used to create and destroy a device on an open uhid char-device. Internally, we allocate and register an HID device with the HID core and immediately start the device. From now on events may be received or sent to the device. The UHID_CREATE event has a payload similar to the data used by Bluetooth-HIDP when creating a new connection. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/uhid.h | 21 +++++++- 2 files changed, 164 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 31e8379cfd15..61ee7cc32ccf 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -29,6 +29,11 @@ struct uhid_device { struct mutex devlock; + bool running; + + __u8 *rd_data; + uint rd_size; + struct hid_device *hid; struct uhid_event input_buf; @@ -75,6 +80,136 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event) return 0; } +static int uhid_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void uhid_hid_stop(struct hid_device *hid) +{ +} + +static int uhid_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void uhid_hid_close(struct hid_device *hid) +{ +} + +static int uhid_hid_input(struct input_dev *input, unsigned int type, + unsigned int code, int value) +{ + return 0; +} + +static int uhid_hid_parse(struct hid_device *hid) +{ + return 0; +} + +static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, + __u8 *buf, size_t count, unsigned char rtype) +{ + return 0; +} + +static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, + unsigned char report_type) +{ + return 0; +} + +static struct hid_ll_driver uhid_hid_driver = { + .start = uhid_hid_start, + .stop = uhid_hid_stop, + .open = uhid_hid_open, + .close = uhid_hid_close, + .hidinput_input_event = uhid_hid_input, + .parse = uhid_hid_parse, +}; + +static int uhid_dev_create(struct uhid_device *uhid, + const struct uhid_event *ev) +{ + struct hid_device *hid; + int ret; + + if (uhid->running) + return -EALREADY; + + uhid->rd_size = ev->u.create.rd_size; + if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE) + return -EINVAL; + + uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL); + if (!uhid->rd_data) + return -ENOMEM; + + if (copy_from_user(uhid->rd_data, ev->u.create.rd_data, + uhid->rd_size)) { + ret = -EFAULT; + goto err_free; + } + + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + ret = PTR_ERR(hid); + goto err_free; + } + + strncpy(hid->name, ev->u.create.name, 127); + hid->name[127] = 0; + strncpy(hid->phys, ev->u.create.phys, 63); + hid->phys[63] = 0; + strncpy(hid->uniq, ev->u.create.uniq, 63); + hid->uniq[63] = 0; + + hid->ll_driver = &uhid_hid_driver; + hid->hid_get_raw_report = uhid_hid_get_raw; + hid->hid_output_raw_report = uhid_hid_output_raw; + hid->bus = ev->u.create.bus; + hid->vendor = ev->u.create.vendor; + hid->product = ev->u.create.product; + hid->version = ev->u.create.version; + hid->country = ev->u.create.country; + hid->driver_data = uhid; + hid->dev.parent = uhid_misc.this_device; + + uhid->hid = hid; + uhid->running = true; + + ret = hid_add_device(hid); + if (ret) { + hid_err(hid, "Cannot register HID device\n"); + goto err_hid; + } + + return 0; + +err_hid: + hid_destroy_device(hid); + uhid->hid = NULL; + uhid->running = false; +err_free: + kfree(uhid->rd_data); + return ret; +} + +static int uhid_dev_destroy(struct uhid_device *uhid) +{ + if (!uhid->running) + return -EINVAL; + + uhid->running = false; + + hid_destroy_device(uhid->hid); + kfree(uhid->rd_data); + + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { struct uhid_device *uhid; @@ -86,6 +221,7 @@ static int uhid_char_open(struct inode *inode, struct file *file) mutex_init(&uhid->devlock); spin_lock_init(&uhid->qlock); init_waitqueue_head(&uhid->waitq); + uhid->running = false; file->private_data = uhid; nonseekable_open(inode, file); @@ -98,6 +234,8 @@ static int uhid_char_release(struct inode *inode, struct file *file) struct uhid_device *uhid = file->private_data; unsigned int i; + uhid_dev_destroy(uhid); + for (i = 0; i < UHID_BUFSIZE; ++i) kfree(uhid->outq[i]); @@ -177,6 +315,12 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, } switch (uhid->input_buf.type) { + case UHID_CREATE: + ret = uhid_dev_create(uhid, &uhid->input_buf); + break; + case UHID_DESTROY: + ret = uhid_dev_destroy(uhid); + break; default: ret = -EOPNOTSUPP; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 16b786a2b18f..8a493e604a77 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -23,11 +23,30 @@ #include enum uhid_event_type { - UHID_DUMMY, + UHID_CREATE, + UHID_DESTROY, }; +struct uhid_create_req { + __u8 name[128]; + __u8 phys[64]; + __u8 uniq[64]; + __u8 __user *rd_data; + __u16 rd_size; + + __u16 bus; + __u32 vendor; + __u32 product; + __u32 version; + __u32 country; +} __attribute__((__packed__)); + struct uhid_event { __u32 type; + + union { + struct uhid_create_req create; + } u; } __attribute__((__packed__)); #endif /* __UHID_H_ */ -- cgit v1.2.3-59-g8ed1b From 5e87a36ae375297b71cc21ac7e32846832bcfb34 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:19 +0200 Subject: HID: uhid: allow feeding input data into uhid devices This adds a new event type UHID_INPUT which allows user-space to feed raw HID reports into the HID subsystem. We copy the data into kernel memory and directly feed it into the HID core. There is no error handling of the events couldn't be parsed so user-space should consider all events successfull unless read() returns an error. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 14 ++++++++++++++ include/linux/uhid.h | 9 +++++++++ 2 files changed, 23 insertions(+) (limited to 'include/linux') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 61ee7cc32ccf..3d1ebda122e5 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -210,6 +210,17 @@ static int uhid_dev_destroy(struct uhid_device *uhid) return 0; } +static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) +{ + if (!uhid->running) + return -EINVAL; + + hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, + min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0); + + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { struct uhid_device *uhid; @@ -321,6 +332,9 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, case UHID_DESTROY: ret = uhid_dev_destroy(uhid); break; + case UHID_INPUT: + ret = uhid_dev_input(uhid, &uhid->input_buf); + break; default: ret = -EOPNOTSUPP; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 8a493e604a77..6eb42bea86a2 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -25,6 +25,7 @@ enum uhid_event_type { UHID_CREATE, UHID_DESTROY, + UHID_INPUT, }; struct uhid_create_req { @@ -41,11 +42,19 @@ struct uhid_create_req { __u32 country; } __attribute__((__packed__)); +#define UHID_DATA_MAX 4096 + +struct uhid_input_req { + __u8 data[UHID_DATA_MAX]; + __u16 size; +} __attribute__((__packed__)); + struct uhid_event { __u32 type; union { struct uhid_create_req create; + struct uhid_input_req input; } u; } __attribute__((__packed__)); -- cgit v1.2.3-59-g8ed1b From ec4b7dea453e0f9fd0fbf1761b2d01eff64f264b Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:21 +0200 Subject: HID: uhid: add UHID_START and UHID_STOP events We send UHID_START and UHID_STOP events to user-space when the HID core starts/stops the device. This notifies user-space about driver readiness and data-I/O can start now. This directly forwards the callbacks from hid-core to user-space. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 8 +++++++- include/linux/uhid.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 0d011db30a46..2e7f3a023975 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -82,11 +82,17 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event) static int uhid_hid_start(struct hid_device *hid) { - return 0; + struct uhid_device *uhid = hid->driver_data; + + return uhid_queue_event(uhid, UHID_START); } static void uhid_hid_stop(struct hid_device *hid) { + struct uhid_device *uhid = hid->driver_data; + + hid->claimed = 0; + uhid_queue_event(uhid, UHID_STOP); } static int uhid_hid_open(struct hid_device *hid) diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 6eb42bea86a2..f8ce6f7571d8 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -25,6 +25,8 @@ enum uhid_event_type { UHID_CREATE, UHID_DESTROY, + UHID_START, + UHID_STOP, UHID_INPUT, }; -- cgit v1.2.3-59-g8ed1b From e7191474a5459e6ba7039fadca6a32f3a5731909 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:22 +0200 Subject: HID: uhid: forward open/close events to user-space HID core notifies us with *_open/*_close callbacks when there is an actual user of our device. We forward these to user-space so they can react on this. This allows user-space to skip I/O unless they receive an OPEN event. When they receive a CLOSE event they can stop I/O again to save energy. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 7 ++++++- include/linux/uhid.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 2e7f3a023975..0226ba3f5307 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -97,11 +97,16 @@ static void uhid_hid_stop(struct hid_device *hid) static int uhid_hid_open(struct hid_device *hid) { - return 0; + struct uhid_device *uhid = hid->driver_data; + + return uhid_queue_event(uhid, UHID_OPEN); } static void uhid_hid_close(struct hid_device *hid) { + struct uhid_device *uhid = hid->driver_data; + + uhid_queue_event(uhid, UHID_CLOSE); } static int uhid_hid_input(struct input_dev *input, unsigned int type, diff --git a/include/linux/uhid.h b/include/linux/uhid.h index f8ce6f7571d8..351650b7a0f6 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -27,6 +27,8 @@ enum uhid_event_type { UHID_DESTROY, UHID_START, UHID_STOP, + UHID_OPEN, + UHID_CLOSE, UHID_INPUT, }; -- cgit v1.2.3-59-g8ed1b From f80e13601c51a836b2aac583b8a3b4327c0c27ce Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:23 +0200 Subject: HID: uhid: forward output request to user-space If the hid-driver wants to send standardized data to the device it uses a linux input_event. We forward this to the user-space transport-level driver so they can perform the requested action on the device. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 18 ++++++++++++++++++ include/linux/uhid.h | 8 ++++++++ 2 files changed, 26 insertions(+) (limited to 'include/linux') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 0226ba3f5307..4dd693e1c8b8 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -112,6 +112,24 @@ static void uhid_hid_close(struct hid_device *hid) static int uhid_hid_input(struct input_dev *input, unsigned int type, unsigned int code, int value) { + struct hid_device *hid = input_get_drvdata(input); + struct uhid_device *uhid = hid->driver_data; + unsigned long flags; + struct uhid_event *ev; + + ev = kzalloc(sizeof(*ev), GFP_ATOMIC); + if (!ev) + return -ENOMEM; + + ev->type = UHID_OUTPUT_EV; + ev->u.output_ev.type = type; + ev->u.output_ev.code = code; + ev->u.output_ev.value = value; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + return 0; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 351650b7a0f6..3fa484921010 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -29,6 +29,7 @@ enum uhid_event_type { UHID_STOP, UHID_OPEN, UHID_CLOSE, + UHID_OUTPUT_EV, UHID_INPUT, }; @@ -53,12 +54,19 @@ struct uhid_input_req { __u16 size; } __attribute__((__packed__)); +struct uhid_output_ev_req { + __u16 type; + __u16 code; + __s32 value; +} __attribute__((__packed__)); + struct uhid_event { __u32 type; union { struct uhid_create_req create; struct uhid_input_req input; + struct uhid_output_ev_req output_ev; } u; } __attribute__((__packed__)); -- cgit v1.2.3-59-g8ed1b From 3b3baa82e4306b5160692643fab2fa322ceb94f9 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:24 +0200 Subject: HID: uhid: forward raw output reports to user-space Some drivers that use non-standard HID features require raw output reports sent to the device. We now forward these requests directly to user-space so the transport-level driver can correctly send it to the device or handle it correspondingly. There is no way to signal back whether the transmission was successful, moreover, there might be lots of messages coming out from the driver flushing the output-queue. However, there is currently no driver that causes this so we are safe. If some drivers need to transmit lots of data this way, we need a method to synchronize this and can implement another UHID_OUTPUT_SYNC event. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 34 +++++++++++++++++++++++++++++++++- include/linux/uhid.h | 14 ++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 4dd693e1c8b8..421c492dc824 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -149,7 +149,39 @@ static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { - return 0; + struct uhid_device *uhid = hid->driver_data; + __u8 rtype; + unsigned long flags; + struct uhid_event *ev; + + switch (report_type) { + case HID_FEATURE_REPORT: + rtype = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + rtype = UHID_OUTPUT_REPORT; + break; + default: + return -EINVAL; + } + + if (count < 1 || count > UHID_DATA_MAX) + return -EINVAL; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = UHID_OUTPUT; + ev->u.output.size = count; + ev->u.output.rtype = rtype; + memcpy(ev->u.output.data, buf, count); + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return count; } static struct hid_ll_driver uhid_hid_driver = { diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 3fa484921010..2c972550a624 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -29,6 +29,7 @@ enum uhid_event_type { UHID_STOP, UHID_OPEN, UHID_CLOSE, + UHID_OUTPUT, UHID_OUTPUT_EV, UHID_INPUT, }; @@ -49,11 +50,23 @@ struct uhid_create_req { #define UHID_DATA_MAX 4096 +enum uhid_report_type { + UHID_FEATURE_REPORT, + UHID_OUTPUT_REPORT, + UHID_INPUT_REPORT, +}; + struct uhid_input_req { __u8 data[UHID_DATA_MAX]; __u16 size; } __attribute__((__packed__)); +struct uhid_output_req { + __u8 data[UHID_DATA_MAX]; + __u16 size; + __u8 rtype; +} __attribute__((__packed__)); + struct uhid_output_ev_req { __u16 type; __u16 code; @@ -66,6 +79,7 @@ struct uhid_event { union { struct uhid_create_req create; struct uhid_input_req input; + struct uhid_output_req output; struct uhid_output_ev_req output_ev; } u; } __attribute__((__packed__)); -- cgit v1.2.3-59-g8ed1b From fcfcf0deb89ece6eb9ae23768fec1bc1718f9b7f Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:25 +0200 Subject: HID: uhid: implement feature requests HID standard allows sending a feature request to the device which is answered by an HID report. uhid implements this by sending a UHID_FEATURE event to user-space which then must answer with UHID_FEATURE_ANSWER. If it doesn't do this in a timely manner, the request is discarded silently. We serialize the feature requests, that is, there is always only a single active feature-request sent to user-space, other requests have to wait. HIDP and USB-HID do it the same way. Because we discard feature-requests silently, we must make sure to match a response to the corresponding request. We use sequence-IDs for this so user-space must copy the ID from the request into the answer. Feature-answers are ignored if they do not contain the same ID as the currently pending feature request. Internally, we must make sure that feature-requests are synchronized with UHID_DESTROY and close() events. We must not dead-lock when closing the HID device, either, so we have to use separate locks. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/uhid.h | 17 ++++++++ 2 files changed, 136 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 421c492dc824..ea560bfa033d 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -42,6 +42,12 @@ struct uhid_device { __u8 head; __u8 tail; struct uhid_event *outq[UHID_BUFSIZE]; + + struct mutex report_lock; + wait_queue_head_t report_wait; + atomic_t report_done; + atomic_t report_id; + struct uhid_event report_buf; }; static struct miscdevice uhid_misc; @@ -143,7 +149,84 @@ static int uhid_hid_parse(struct hid_device *hid) static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, __u8 *buf, size_t count, unsigned char rtype) { - return 0; + struct uhid_device *uhid = hid->driver_data; + __u8 report_type; + struct uhid_event *ev; + unsigned long flags; + int ret; + size_t len; + struct uhid_feature_answer_req *req; + + if (!uhid->running) + return -EIO; + + switch (rtype) { + case HID_FEATURE_REPORT: + report_type = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + report_type = UHID_OUTPUT_REPORT; + break; + case HID_INPUT_REPORT: + report_type = UHID_INPUT_REPORT; + break; + default: + return -EINVAL; + } + + ret = mutex_lock_interruptible(&uhid->report_lock); + if (ret) + return ret; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) { + ret = -ENOMEM; + goto unlock; + } + + spin_lock_irqsave(&uhid->qlock, flags); + ev->type = UHID_FEATURE; + ev->u.feature.id = atomic_inc_return(&uhid->report_id); + ev->u.feature.rnum = rnum; + ev->u.feature.rtype = report_type; + + atomic_set(&uhid->report_done, 0); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + ret = wait_event_interruptible_timeout(uhid->report_wait, + atomic_read(&uhid->report_done), 5 * HZ); + + /* + * Make sure "uhid->running" is cleared on shutdown before + * "uhid->report_done" is set. + */ + smp_rmb(); + if (!ret || !uhid->running) { + ret = -EIO; + } else if (ret < 0) { + ret = -ERESTARTSYS; + } else { + spin_lock_irqsave(&uhid->qlock, flags); + req = &uhid->report_buf.u.feature_answer; + + if (req->err) { + ret = -EIO; + } else { + ret = 0; + len = min(count, + min_t(size_t, req->size, UHID_DATA_MAX)); + memcpy(buf, req->data, len); + } + + spin_unlock_irqrestore(&uhid->qlock, flags); + } + + atomic_set(&uhid->report_done, 1); + +unlock: + mutex_unlock(&uhid->report_lock); + return ret ? ret : len; } static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, @@ -265,7 +348,11 @@ static int uhid_dev_destroy(struct uhid_device *uhid) if (!uhid->running) return -EINVAL; + /* clear "running" before setting "report_done" */ uhid->running = false; + smp_wmb(); + atomic_set(&uhid->report_done, 1); + wake_up_interruptible(&uhid->report_wait); hid_destroy_device(uhid->hid); kfree(uhid->rd_data); @@ -284,6 +371,31 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) return 0; } +static int uhid_dev_feature_answer(struct uhid_device *uhid, + struct uhid_event *ev) +{ + unsigned long flags; + + if (!uhid->running) + return -EINVAL; + + spin_lock_irqsave(&uhid->qlock, flags); + + /* id for old report; drop it silently */ + if (atomic_read(&uhid->report_id) != ev->u.feature_answer.id) + goto unlock; + if (atomic_read(&uhid->report_done)) + goto unlock; + + memcpy(&uhid->report_buf, ev, sizeof(*ev)); + atomic_set(&uhid->report_done, 1); + wake_up_interruptible(&uhid->report_wait); + +unlock: + spin_unlock_irqrestore(&uhid->qlock, flags); + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { struct uhid_device *uhid; @@ -293,9 +405,12 @@ static int uhid_char_open(struct inode *inode, struct file *file) return -ENOMEM; mutex_init(&uhid->devlock); + mutex_init(&uhid->report_lock); spin_lock_init(&uhid->qlock); init_waitqueue_head(&uhid->waitq); + init_waitqueue_head(&uhid->report_wait); uhid->running = false; + atomic_set(&uhid->report_done, 1); file->private_data = uhid; nonseekable_open(inode, file); @@ -398,6 +513,9 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, case UHID_INPUT: ret = uhid_dev_input(uhid, &uhid->input_buf); break; + case UHID_FEATURE_ANSWER: + ret = uhid_dev_feature_answer(uhid, &uhid->input_buf); + break; default: ret = -EOPNOTSUPP; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 2c972550a624..9c6974f16966 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -32,6 +32,8 @@ enum uhid_event_type { UHID_OUTPUT, UHID_OUTPUT_EV, UHID_INPUT, + UHID_FEATURE, + UHID_FEATURE_ANSWER, }; struct uhid_create_req { @@ -73,6 +75,19 @@ struct uhid_output_ev_req { __s32 value; } __attribute__((__packed__)); +struct uhid_feature_req { + __u32 id; + __u8 rnum; + __u8 rtype; +} __attribute__((__packed__)); + +struct uhid_feature_answer_req { + __u32 id; + __u16 err; + __u16 size; + __u8 data[UHID_DATA_MAX]; +}; + struct uhid_event { __u32 type; @@ -81,6 +96,8 @@ struct uhid_event { struct uhid_input_req input; struct uhid_output_req output; struct uhid_output_ev_req output_ev; + struct uhid_feature_req feature; + struct uhid_feature_answer_req feature_answer; } u; } __attribute__((__packed__)); -- cgit v1.2.3-59-g8ed1b From 728b19e5fb9bbebbd580784a092b786fe379ed8e Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Thu, 14 Jun 2012 02:06:10 +0800 Subject: {nl,cfg,mac}80211: implement dot11MeshHWMPconfirmationInterval As defined in section 13.10.9.3 Case D (802.11-2012), this control variable is used to limit the mesh STA to send only one PREQ to a root mesh STA within this interval of time (in TUs). The default value for this variable is set to 2000 TUs. However, for current implementation, the maximum configurable of dot11MeshHWMPconfirmationInterval is restricted by dot11MeshHWMPactivePathTimeout. Signed-off-by: Chun-Yeow Yeoh [line-break commit log] Signed-off-by: Johannes Berg --- include/linux/nl80211.h | 5 +++++ include/net/cfg80211.h | 4 ++++ net/mac80211/cfg.c | 3 +++ net/mac80211/debugfs_netdev.c | 3 +++ net/mac80211/mesh.h | 2 ++ net/mac80211/mesh_hwmp.c | 7 ++++++- net/wireless/mesh.c | 2 ++ net/wireless/nl80211.c | 9 ++++++++- 8 files changed, 33 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 6936fabe8797..b7c3b737ddde 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -2192,6 +2192,10 @@ enum nl80211_mntr_flags { * @NL80211_MESHCONF_HWMP_ROOT_INTERVAL: The interval of time (in TUs) between * proactive PREQs are transmitted. * + * @NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL: The minimum interval of time + * (in TUs) during which a mesh STA can send only one Action frame + * containing a PREQ element for root path confirmation. + * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ enum nl80211_meshconf_params { @@ -2220,6 +2224,7 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_HT_OPMODE, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, + NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e52b38d7b1b6..f0163a10b8ce 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -849,6 +849,9 @@ struct bss_parameters { * * @dot11MeshHWMProotInterval: The interval of time (in TUs) between proactive * PREQs are transmitted. + * @dot11MeshHWMPconfirmationInterval: The minimum interval of time (in TUs) + * during which a mesh STA can send only one Action frame containing + * a PREQ element for root path confirmation. */ struct mesh_config { u16 dot11MeshRetryTimeout; @@ -875,6 +878,7 @@ struct mesh_config { u16 ht_opmode; u32 dot11MeshHWMPactivePathToRootTimeout; u16 dot11MeshHWMProotInterval; + u16 dot11MeshHWMPconfirmationInterval; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 5bd316c0a63d..6e25ac4873c7 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1596,6 +1596,9 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ROOT_INTERVAL, mask)) conf->dot11MeshHWMProotInterval = nconf->dot11MeshHWMProotInterval; + if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, mask)) + conf->dot11MeshHWMPconfirmationInterval = + nconf->dot11MeshHWMPconfirmationInterval; return 0; } diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index a8cea70902e4..512c894893d6 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -514,6 +514,8 @@ IEEE80211_IF_FILE(dot11MeshHWMPactivePathToRootTimeout, u.mesh.mshcfg.dot11MeshHWMPactivePathToRootTimeout, DEC); IEEE80211_IF_FILE(dot11MeshHWMProotInterval, u.mesh.mshcfg.dot11MeshHWMProotInterval, DEC); +IEEE80211_IF_FILE(dot11MeshHWMPconfirmationInterval, + u.mesh.mshcfg.dot11MeshHWMPconfirmationInterval, DEC); #endif #define DEBUGFS_ADD_MODE(name, mode) \ @@ -617,6 +619,7 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata) MESHPARAMS_ADD(ht_opmode); MESHPARAMS_ADD(dot11MeshHWMPactivePathToRootTimeout); MESHPARAMS_ADD(dot11MeshHWMProotInterval); + MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval); #undef MESHPARAMS_ADD } #endif diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index c7400a23b64b..faaa39bcfd10 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -104,6 +104,7 @@ enum mesh_deferred_task_flags { * an mpath to a hash bucket on a path table. * @rann_snd_addr: the RANN sender address * @rann_metric: the aggregated path metric towards the root node + * @last_preq_to_root: Timestamp of last PREQ sent to root * @is_root: the destination station of this path is a root node * @is_gate: the destination station of this path is a mesh gate * @@ -131,6 +132,7 @@ struct mesh_path { spinlock_t state_lock; u8 rann_snd_addr[ETH_ALEN]; u32 rann_metric; + unsigned long last_preq_to_root; bool is_root; bool is_gate; }; diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 35e3acbe2262..bea52479e3aa 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -98,6 +98,8 @@ static inline u32 u16_field_get(u8 *preq_elem, int offset, bool ae) #define max_preq_retries(s) (s->u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries) #define disc_timeout_jiff(s) \ msecs_to_jiffies(sdata->u.mesh.mshcfg.min_discovery_timeout) +#define root_path_confirmation_jiffies(s) \ + msecs_to_jiffies(sdata->u.mesh.mshcfg.dot11MeshHWMPconfirmationInterval) enum mpath_frame_type { MPATH_PREQ = 0, @@ -811,11 +813,14 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, } if ((!(mpath->flags & (MESH_PATH_ACTIVE | MESH_PATH_RESOLVING)) || - time_after(jiffies, mpath->exp_time - 1*HZ)) && + (time_after(jiffies, mpath->last_preq_to_root + + root_path_confirmation_jiffies(sdata)) || + time_before(jiffies, mpath->last_preq_to_root))) && !(mpath->flags & MESH_PATH_FIXED)) { mhwmp_dbg("%s time to refresh root mpath %pM", sdata->name, orig_addr); mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH); + mpath->last_preq_to_root = jiffies; } if ((SN_LT(mpath->sn, orig_sn) || (mpath->sn == orig_sn && diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 2f141cfd581e..3b73b07486cf 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -16,6 +16,7 @@ #define MESH_RANN_INTERVAL 5000 #define MESH_PATH_TO_ROOT_TIMEOUT 6000 #define MESH_ROOT_INTERVAL 5000 +#define MESH_ROOT_CONFIRMATION_INTERVAL 2000 /* * Minimum interval between two consecutive PREQs originated by the same @@ -66,6 +67,7 @@ const struct mesh_config default_mesh_config = { .ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED, .dot11MeshHWMPactivePathToRootTimeout = MESH_PATH_TO_ROOT_TIMEOUT, .dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL, + .dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL, }; const struct mesh_setup default_mesh_setup = { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f8930db613df..a363ca17bfc5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3473,7 +3473,9 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, nla_put_u32(msg, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, cur_params.dot11MeshHWMPactivePathToRootTimeout) || nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, - cur_params.dot11MeshHWMProotInterval)) + cur_params.dot11MeshHWMProotInterval) || + nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, + cur_params.dot11MeshHWMPconfirmationInterval)) goto nla_put_failure; nla_nest_end(msg, pinfoattr); genlmsg_end(msg, hdr); @@ -3511,6 +3513,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A [NL80211_MESHCONF_HT_OPMODE] = { .type = NLA_U16 }, [NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 }, [NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 }, + [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 }, }; static const struct nla_policy @@ -3625,6 +3628,10 @@ do {\ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, + dot11MeshHWMPconfirmationInterval, mask, + NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, + nla_get_u16); if (mask_out) *mask_out = mask; -- cgit v1.2.3-59-g8ed1b From 08aed2f6fb09c9f8efc9258c152072ed614db226 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 11 Jun 2012 20:14:24 +0800 Subject: regulator: stubs: Make stub regulator_get_voltage() return an error Returning 0 isn't useful, it's not even meaningful if there is a real regulator there. Reported-by: Guennadi Liakhovetski Signed-off-by: Mark Brown --- include/linux/regulator/consumer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 4ed1b30ac5fc..9ff29ef317c2 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -291,7 +291,7 @@ static inline int regulator_set_voltage(struct regulator *regulator, static inline int regulator_get_voltage(struct regulator *regulator) { - return 0; + return -EINVAL; } static inline int regulator_set_current_limit(struct regulator *regulator, -- cgit v1.2.3-59-g8ed1b From 9900b4b48b095895cf962054e45aafa49ef70f74 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 15 Jun 2012 15:07:02 -0400 Subject: KVM: use KVM_CAP_IRQ_ROUTING to protect the routing related code The KVM code sometimes uses CONFIG_HAVE_KVM_IRQCHIP to protect code that is related to IRQ routing, which not all in-kernel irqchips may support. Use KVM_CAP_IRQ_ROUTING instead. Signed-off-by: Marc Zyngier Signed-off-by: Christoffer Dall Signed-off-by: Avi Kivity --- include/linux/kvm_host.h | 2 +- virt/kvm/kvm_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 27ac8a4767fa..c7f77876c9b3 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -802,7 +802,7 @@ static inline int mmu_notifier_retry(struct kvm_vcpu *vcpu, unsigned long mmu_se } #endif -#ifdef CONFIG_HAVE_KVM_IRQCHIP +#ifdef KVM_CAP_IRQ_ROUTING #define KVM_MAX_IRQ_ROUTES 1024 diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 02cb440f802d..636bd08bb399 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2225,7 +2225,7 @@ static long kvm_dev_ioctl_check_extension_generic(long arg) case KVM_CAP_SIGNAL_MSI: #endif return 1; -#ifdef CONFIG_HAVE_KVM_IRQCHIP +#ifdef KVM_CAP_IRQ_ROUTING case KVM_CAP_IRQ_ROUTING: return KVM_MAX_IRQ_ROUTES; #endif -- cgit v1.2.3-59-g8ed1b From c848bc8538cd29e92223d5e72e8fb60e6f2e176e Mon Sep 17 00:00:00 2001 From: Sangbeom Kim Date: Mon, 18 Jun 2012 09:49:20 +0900 Subject: regulator: s5m8767a: Support AP watchdog reset operation The S5M8767A can't know status of ap reset. So, After AP watchdog reset, AP can't boot normally. Problem can be happened like below condition. - AP Bootable lowest voltage(vdd_arm): 0.9v - AP DVFS voltage table: 0.8v, 0.9v, 1.0v - During AP works on lowest voltage(0.8V), watchdog reset is asserted - AP can't boot, because vdd arm is still 0.8v Solution - Basic concept: After ap watchdog reset, GPIO configuration is changed by default value - S5M8767A has function of voltage control with gpio (8 levels with 3 gpios) - Set bootable voltage on level 0 -> can work with default gpio configuration - In the probing, Change voltage control level from level 0 to level 1 - Execute normal dvfs operation on level 1 - After watchdog reset, ap gpio is set by default value - PMIC operation mode is changed by ap reset (level1 -> level0) - Regardless of previous vdd_arm voltage, AP always can be booted. Signed-off-by: Sangbeom Kim Signed-off-by: Mark Brown --- drivers/regulator/s5m8767.c | 137 ++++++++++++++++++++++++----------- include/linux/mfd/s5m87xx/s5m-core.h | 5 ++ 2 files changed, 98 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index a4a3c7eefd1f..fd89574c405c 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -41,6 +41,7 @@ struct s5m8767_info { u8 buck3_vol[8]; u8 buck4_vol[8]; int buck_gpios[3]; + int buck_ds[3]; int buck_gpioindex; }; @@ -283,17 +284,17 @@ static int s5m8767_get_voltage_register(struct regulator_dev *rdev, int *_reg) reg = S5M8767_REG_BUCK1CTRL2; break; case S5M8767_BUCK2: - reg = S5M8767_REG_BUCK2DVS1; + reg = S5M8767_REG_BUCK2DVS2; if (s5m8767->buck2_gpiodvs) reg += s5m8767->buck_gpioindex; break; case S5M8767_BUCK3: - reg = S5M8767_REG_BUCK3DVS1; + reg = S5M8767_REG_BUCK3DVS2; if (s5m8767->buck3_gpiodvs) reg += s5m8767->buck_gpioindex; break; case S5M8767_BUCK4: - reg = S5M8767_REG_BUCK4DVS1; + reg = S5M8767_REG_BUCK4DVS2; if (s5m8767->buck4_gpiodvs) reg += s5m8767->buck_gpioindex; break; @@ -512,7 +513,7 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) struct regulator_config config = { }; struct regulator_dev **rdev; struct s5m8767_info *s5m8767; - int i, ret, size; + int i, ret, size, buck_init; if (!pdata) { dev_err(pdev->dev.parent, "Platform data not supplied\n"); @@ -563,12 +564,37 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) s5m8767->buck_gpios[0] = pdata->buck_gpios[0]; s5m8767->buck_gpios[1] = pdata->buck_gpios[1]; s5m8767->buck_gpios[2] = pdata->buck_gpios[2]; + s5m8767->buck_ds[0] = pdata->buck_ds[0]; + s5m8767->buck_ds[1] = pdata->buck_ds[1]; + s5m8767->buck_ds[2] = pdata->buck_ds[2]; + s5m8767->ramp_delay = pdata->buck_ramp_delay; s5m8767->buck2_ramp = pdata->buck2_ramp_enable; s5m8767->buck3_ramp = pdata->buck3_ramp_enable; s5m8767->buck4_ramp = pdata->buck4_ramp_enable; s5m8767->opmode = pdata->opmode; + buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2, + pdata->buck2_init, + pdata->buck2_init + + buck_voltage_val2.step); + + s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS2, buck_init); + + buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2, + pdata->buck3_init, + pdata->buck3_init + + buck_voltage_val2.step); + + s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS2, buck_init); + + buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2, + pdata->buck4_init, + pdata->buck4_init + + buck_voltage_val2.step); + + s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS2, buck_init); + for (i = 0; i < 8; i++) { if (s5m8767->buck2_gpiodvs) { s5m8767->buck2_vol[i] = @@ -598,48 +624,71 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) } } - if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || - pdata->buck4_gpiodvs) { - if (gpio_is_valid(pdata->buck_gpios[0]) && - gpio_is_valid(pdata->buck_gpios[1]) && - gpio_is_valid(pdata->buck_gpios[2])) { - ret = gpio_request(pdata->buck_gpios[0], - "S5M8767 SET1"); - if (ret == -EBUSY) - dev_warn(&pdev->dev, "Duplicated gpio request for SET1\n"); - - ret = gpio_request(pdata->buck_gpios[1], - "S5M8767 SET2"); - if (ret == -EBUSY) - dev_warn(&pdev->dev, "Duplicated gpio request for SET2\n"); - - ret = gpio_request(pdata->buck_gpios[2], - "S5M8767 SET3"); - if (ret == -EBUSY) - dev_warn(&pdev->dev, "Duplicated gpio request for SET3\n"); - /* SET1 GPIO */ - gpio_direction_output(pdata->buck_gpios[0], - (s5m8767->buck_gpioindex >> 2) & 0x1); - /* SET2 GPIO */ - gpio_direction_output(pdata->buck_gpios[1], - (s5m8767->buck_gpioindex >> 1) & 0x1); - /* SET3 GPIO */ - gpio_direction_output(pdata->buck_gpios[2], - (s5m8767->buck_gpioindex >> 0) & 0x1); - ret = 0; - } else { - dev_err(&pdev->dev, "GPIO NOT VALID\n"); - ret = -EINVAL; - return ret; - } + if (gpio_is_valid(pdata->buck_gpios[0]) && + gpio_is_valid(pdata->buck_gpios[1]) && + gpio_is_valid(pdata->buck_gpios[2])) { + ret = gpio_request(pdata->buck_gpios[0], "S5M8767 SET1"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request" + " for SET1\n"); + + ret = gpio_request(pdata->buck_gpios[1], "S5M8767 SET2"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request" + " for SET2\n"); + + ret = gpio_request(pdata->buck_gpios[2], "S5M8767 SET3"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request" + " for SET3\n"); + /* SET1 GPIO */ + gpio_direction_output(pdata->buck_gpios[0], + (s5m8767->buck_gpioindex >> 2) & 0x1); + /* SET2 GPIO */ + gpio_direction_output(pdata->buck_gpios[1], + (s5m8767->buck_gpioindex >> 1) & 0x1); + /* SET3 GPIO */ + gpio_direction_output(pdata->buck_gpios[2], + (s5m8767->buck_gpioindex >> 0) & 0x1); + ret = 0; + + } else { + dev_err(&pdev->dev, "GPIO NOT VALID\n"); + ret = -EINVAL; + return ret; } - s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL, - (pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1); - s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL, - (pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1); - s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL, - (pdata->buck4_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1); + ret = gpio_request(pdata->buck_ds[0], "S5M8767 DS2"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request for DS2\n"); + + ret = gpio_request(pdata->buck_ds[1], "S5M8767 DS3"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request for DS3\n"); + + ret = gpio_request(pdata->buck_ds[2], "S5M8767 DS4"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request for DS4\n"); + + /* DS2 GPIO */ + gpio_direction_output(pdata->buck_ds[0], 0x0); + /* DS3 GPIO */ + gpio_direction_output(pdata->buck_ds[1], 0x0); + /* DS4 GPIO */ + gpio_direction_output(pdata->buck_ds[2], 0x0); + + if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || + pdata->buck4_gpiodvs) { + s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL, + (pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1), + 1 << 1); + s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL, + (pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1), + 1 << 1); + s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL, + (pdata->buck4_gpiodvs) ? (1 << 1) : (0 << 1), + 1 << 1); + } /* Initialize GPIO DVS registers */ for (i = 0; i < 8; i++) { diff --git a/include/linux/mfd/s5m87xx/s5m-core.h b/include/linux/mfd/s5m87xx/s5m-core.h index 21603b42f22f..0b2e0ed309f5 100644 --- a/include/linux/mfd/s5m87xx/s5m-core.h +++ b/include/linux/mfd/s5m87xx/s5m-core.h @@ -347,6 +347,7 @@ struct s5m_platform_data { bool buck_voltage_lock; int buck_gpios[3]; + int buck_ds[3]; int buck2_voltage[8]; bool buck2_gpiodvs; int buck3_voltage[8]; @@ -369,6 +370,10 @@ struct s5m_platform_data { bool buck2_ramp_enable; bool buck3_ramp_enable; bool buck4_ramp_enable; + + int buck2_init; + int buck3_init; + int buck4_init; }; #endif /* __LINUX_MFD_S5M_CORE_H */ -- cgit v1.2.3-59-g8ed1b From 7c9416365c60f150ef8961a2855fafbc7394ad6b Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 13 Jun 2012 20:04:33 +0200 Subject: canfd: add new data structures and constants - add new struct canfd_frame - check identical element offsets in struct can_frame and struct canfd_frame - new ETH_P_CANFD definition to tag CAN FD skbs correctly - add CAN_MTU and CANFD_MTU definitions for easy frame and mode detection - add CAN[FD]_MAX_[DLC|DLEN] helper constants to remove hard coded values - update existing struct can_frame with helper constants and comments Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/linux/can.h | 59 ++++++++++++++++++++++++++++++++++++++++++++---- include/linux/if_ether.h | 3 ++- net/can/af_can.c | 7 ++++++ 3 files changed, 63 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/can.h b/include/linux/can.h index 17334c09bd93..1a66cf6112ae 100644 --- a/include/linux/can.h +++ b/include/linux/can.h @@ -46,18 +46,67 @@ typedef __u32 canid_t; */ typedef __u32 can_err_mask_t; +/* CAN payload length and DLC definitions according to ISO 11898-1 */ +#define CAN_MAX_DLC 8 +#define CAN_MAX_DLEN 8 + +/* CAN FD payload length and DLC definitions according to ISO 11898-7 */ +#define CANFD_MAX_DLC 15 +#define CANFD_MAX_DLEN 64 + /** * struct can_frame - basic CAN frame structure - * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above. - * @can_dlc: the data length field of the CAN frame - * @data: the CAN frame payload. + * @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition + * @can_dlc: frame payload length in byte (0 .. 8) aka data length code + * N.B. the DLC field from ISO 11898-1 Chapter 8.4.2.3 has a 1:1 + * mapping of the 'data length code' to the real payload length + * @data: CAN frame payload (up to 8 byte) */ struct can_frame { canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ - __u8 can_dlc; /* data length code: 0 .. 8 */ - __u8 data[8] __attribute__((aligned(8))); + __u8 can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */ + __u8 data[CAN_MAX_DLEN] __attribute__((aligned(8))); }; +/* + * defined bits for canfd_frame.flags + * + * As the default for CAN FD should be to support the high data rate in the + * payload section of the frame (HDR) and to support up to 64 byte in the + * data section (EDL) the bits are only set in the non-default case. + * Btw. as long as there's no real implementation for CAN FD network driver + * these bits are only preliminary. + * + * RX: NOHDR/NOEDL - info about received CAN FD frame + * ESI - bit from originating CAN controller + * TX: NOHDR/NOEDL - control per-frame settings if supported by CAN controller + * ESI - bit is set by local CAN controller + */ +#define CANFD_NOHDR 0x01 /* frame without high data rate */ +#define CANFD_NOEDL 0x02 /* frame without extended data length */ +#define CANFD_ESI 0x04 /* error state indicator */ + +/** + * struct canfd_frame - CAN flexible data rate frame structure + * @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition + * @len: frame payload length in byte (0 .. CANFD_MAX_DLEN) + * @flags: additional flags for CAN FD + * @__res0: reserved / padding + * @__res1: reserved / padding + * @data: CAN FD frame payload (up to CANFD_MAX_DLEN byte) + */ +struct canfd_frame { + canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ + __u8 len; /* frame payload length in byte */ + __u8 flags; /* additional flags for CAN FD */ + __u8 __res0; /* reserved / padding */ + __u8 __res1; /* reserved / padding */ + __u8 data[CANFD_MAX_DLEN] __attribute__((aligned(8))); +}; + +#define CAN_MTU (sizeof(struct can_frame)) +#define CANFD_MTU (sizeof(struct canfd_frame)) + /* particular protocols of the protocol family PF_CAN */ #define CAN_RAW 1 /* RAW sockets */ #define CAN_BCM 2 /* Broadcast Manager */ diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index 56d907a2c804..167ce5b363d2 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -105,7 +105,8 @@ #define ETH_P_WAN_PPP 0x0007 /* Dummy type for WAN PPP frames*/ #define ETH_P_PPP_MP 0x0008 /* Dummy type for PPP MP frames */ #define ETH_P_LOCALTALK 0x0009 /* Localtalk pseudo type */ -#define ETH_P_CAN 0x000C /* Controller Area Network */ +#define ETH_P_CAN 0x000C /* CAN: Controller Area Network */ +#define ETH_P_CANFD 0x000D /* CANFD: CAN flexible data rate*/ #define ETH_P_PPPTALK 0x0010 /* Dummy type for Atalk over PPP*/ #define ETH_P_TR_802_2 0x0011 /* 802.2 frames */ #define ETH_P_MOBITEX 0x0015 /* Mobitex (kaz@cafe.net) */ diff --git a/net/can/af_can.c b/net/can/af_can.c index 6efcd37b4bd0..c96140a1458e 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -41,6 +41,7 @@ */ #include +#include #include #include #include @@ -824,6 +825,12 @@ static struct notifier_block can_netdev_notifier __read_mostly = { static __init int can_init(void) { + /* check for correct padding to be able to use the structs similarly */ + BUILD_BUG_ON(offsetof(struct can_frame, can_dlc) != + offsetof(struct canfd_frame, len) || + offsetof(struct can_frame, data) != + offsetof(struct canfd_frame, data)); + printk(banner); memset(&can_rx_alldev_list, 0, sizeof(can_rx_alldev_list)); -- cgit v1.2.3-59-g8ed1b From 8b01939f358d680cea971151375268cfdb6b9635 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 13 Jun 2012 20:33:02 +0200 Subject: canfd: add support for CAN FD in PF_CAN core - handle ETH_P_CAN and ETH_P_CANFD skbuffs - update sanity checks for CAN and CAN FD - make sure the CAN frame can pass the selected CAN netdevice on send - bump core version and abi version to indicate the new CAN FD support Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/linux/can/core.h | 4 +- net/can/af_can.c | 109 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 84 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/include/linux/can/core.h b/include/linux/can/core.h index 0ccc1cd28b95..78c6c52073ad 100644 --- a/include/linux/can/core.h +++ b/include/linux/can/core.h @@ -17,10 +17,10 @@ #include #include -#define CAN_VERSION "20090105" +#define CAN_VERSION "20120528" /* increment this number each time you change some user-space interface */ -#define CAN_ABI_VERSION "8" +#define CAN_ABI_VERSION "9" #define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION diff --git a/net/can/af_can.c b/net/can/af_can.c index c96140a1458e..821022a7214f 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -221,30 +221,46 @@ static int can_create(struct net *net, struct socket *sock, int protocol, * -ENOBUFS on full driver queue (see net_xmit_errno()) * -ENOMEM when local loopback failed at calling skb_clone() * -EPERM when trying to send on a non-CAN interface + * -EMSGSIZE CAN frame size is bigger than CAN interface MTU * -EINVAL when the skb->data does not contain a valid CAN frame */ int can_send(struct sk_buff *skb, int loop) { struct sk_buff *newskb = NULL; - struct can_frame *cf = (struct can_frame *)skb->data; - int err; + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + int err = -EINVAL; + + if (skb->len == CAN_MTU) { + skb->protocol = htons(ETH_P_CAN); + if (unlikely(cfd->len > CAN_MAX_DLEN)) + goto inval_skb; + } else if (skb->len == CANFD_MTU) { + skb->protocol = htons(ETH_P_CANFD); + if (unlikely(cfd->len > CANFD_MAX_DLEN)) + goto inval_skb; + } else + goto inval_skb; - if (skb->len != sizeof(struct can_frame) || cf->can_dlc > 8) { - kfree_skb(skb); - return -EINVAL; + /* + * Make sure the CAN frame can pass the selected CAN netdevice. + * As structs can_frame and canfd_frame are similar, we can provide + * CAN FD frames to legacy CAN drivers as long as the length is <= 8 + */ + if (unlikely(skb->len > skb->dev->mtu && cfd->len > CAN_MAX_DLEN)) { + err = -EMSGSIZE; + goto inval_skb; } - if (skb->dev->type != ARPHRD_CAN) { - kfree_skb(skb); - return -EPERM; + if (unlikely(skb->dev->type != ARPHRD_CAN)) { + err = -EPERM; + goto inval_skb; } - if (!(skb->dev->flags & IFF_UP)) { - kfree_skb(skb); - return -ENETDOWN; + if (unlikely(!(skb->dev->flags & IFF_UP))) { + err = -ENETDOWN; + goto inval_skb; } - skb->protocol = htons(ETH_P_CAN); skb_reset_network_header(skb); skb_reset_transport_header(skb); @@ -301,6 +317,10 @@ int can_send(struct sk_buff *skb, int loop) can_stats.tx_frames_delta++; return 0; + +inval_skb: + kfree_skb(skb); + return err; } EXPORT_SYMBOL(can_send); @@ -633,24 +653,11 @@ static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb) return matches; } -static int can_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) +static void can_receive(struct sk_buff *skb, struct net_device *dev) { struct dev_rcv_lists *d; - struct can_frame *cf = (struct can_frame *)skb->data; int matches; - if (!net_eq(dev_net(dev), &init_net)) - goto drop; - - if (WARN_ONCE(dev->type != ARPHRD_CAN || - skb->len != sizeof(struct can_frame) || - cf->can_dlc > 8, - "PF_CAN: dropped non conform skbuf: " - "dev type %d, len %d, can_dlc %d\n", - dev->type, skb->len, cf->can_dlc)) - goto drop; - /* update statistics */ can_stats.rx_frames++; can_stats.rx_frames_delta++; @@ -674,7 +681,49 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev, can_stats.matches++; can_stats.matches_delta++; } +} + +static int can_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + if (unlikely(!net_eq(dev_net(dev), &init_net))) + goto drop; + + if (WARN_ONCE(dev->type != ARPHRD_CAN || + skb->len != CAN_MTU || + cfd->len > CAN_MAX_DLEN, + "PF_CAN: dropped non conform CAN skbuf: " + "dev type %d, len %d, datalen %d\n", + dev->type, skb->len, cfd->len)) + goto drop; + + can_receive(skb, dev); + return NET_RX_SUCCESS; + +drop: + kfree_skb(skb); + return NET_RX_DROP; +} + +static int canfd_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + + if (unlikely(!net_eq(dev_net(dev), &init_net))) + goto drop; + + if (WARN_ONCE(dev->type != ARPHRD_CAN || + skb->len != CANFD_MTU || + cfd->len > CANFD_MAX_DLEN, + "PF_CAN: dropped non conform CAN FD skbuf: " + "dev type %d, len %d, datalen %d\n", + dev->type, skb->len, cfd->len)) + goto drop; + + can_receive(skb, dev); return NET_RX_SUCCESS; drop: @@ -808,10 +857,14 @@ static int can_notifier(struct notifier_block *nb, unsigned long msg, static struct packet_type can_packet __read_mostly = { .type = cpu_to_be16(ETH_P_CAN), - .dev = NULL, .func = can_rcv, }; +static struct packet_type canfd_packet __read_mostly = { + .type = cpu_to_be16(ETH_P_CANFD), + .func = canfd_rcv, +}; + static const struct net_proto_family can_family_ops = { .family = PF_CAN, .create = can_create, @@ -853,6 +906,7 @@ static __init int can_init(void) sock_register(&can_family_ops); register_netdevice_notifier(&can_netdev_notifier); dev_add_pack(&can_packet); + dev_add_pack(&canfd_packet); return 0; } @@ -867,6 +921,7 @@ static __exit void can_exit(void) can_remove_proc(); /* protocol unregister */ + dev_remove_pack(&canfd_packet); dev_remove_pack(&can_packet); unregister_netdevice_notifier(&can_netdev_notifier); sock_unregister(PF_CAN); -- cgit v1.2.3-59-g8ed1b From e2d265d3b587f5f6f8febc0222aace93302ff0be Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 13 Jun 2012 20:41:31 +0200 Subject: canfd: add support for CAN FD in CAN_RAW sockets - introduce a new sockopt CAN_RAW_FD_FRAMES to allow CAN FD frames - handle CAN frames and CAN FD frames simultaneously when enabled Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/linux/can/raw.h | 3 ++- net/can/raw.c | 50 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/can/raw.h b/include/linux/can/raw.h index 781f3a3701be..a814062b0719 100644 --- a/include/linux/can/raw.h +++ b/include/linux/can/raw.h @@ -23,7 +23,8 @@ enum { CAN_RAW_FILTER = 1, /* set 0 .. n can_filter(s) */ CAN_RAW_ERR_FILTER, /* set filter for error frames */ CAN_RAW_LOOPBACK, /* local loopback (default:on) */ - CAN_RAW_RECV_OWN_MSGS /* receive my own msgs (default:off) */ + CAN_RAW_RECV_OWN_MSGS, /* receive my own msgs (default:off) */ + CAN_RAW_FD_FRAMES, /* allow CAN FD frames (default:off) */ }; #endif diff --git a/net/can/raw.c b/net/can/raw.c index 46cca3a91d19..3e9c89356a93 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -82,6 +82,7 @@ struct raw_sock { struct notifier_block notifier; int loopback; int recv_own_msgs; + int fd_frames; int count; /* number of active filters */ struct can_filter dfilter; /* default/single filter */ struct can_filter *filter; /* pointer to filter(s) */ @@ -119,6 +120,14 @@ static void raw_rcv(struct sk_buff *oskb, void *data) if (!ro->recv_own_msgs && oskb->sk == sk) return; + /* do not pass frames with DLC > 8 to a legacy socket */ + if (!ro->fd_frames) { + struct canfd_frame *cfd = (struct canfd_frame *)oskb->data; + + if (unlikely(cfd->len > CAN_MAX_DLEN)) + return; + } + /* clone the given skb to be able to enqueue it into the rcv queue */ skb = skb_clone(oskb, GFP_ATOMIC); if (!skb) @@ -291,6 +300,7 @@ static int raw_init(struct sock *sk) /* set default loopback behaviour */ ro->loopback = 1; ro->recv_own_msgs = 0; + ro->fd_frames = 0; /* set notifier */ ro->notifier.notifier_call = raw_notifier; @@ -569,6 +579,15 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, break; + case CAN_RAW_FD_FRAMES: + if (optlen != sizeof(ro->fd_frames)) + return -EINVAL; + + if (copy_from_user(&ro->fd_frames, optval, optlen)) + return -EFAULT; + + break; + default: return -ENOPROTOOPT; } @@ -627,6 +646,12 @@ static int raw_getsockopt(struct socket *sock, int level, int optname, val = &ro->recv_own_msgs; break; + case CAN_RAW_FD_FRAMES: + if (len > sizeof(int)) + len = sizeof(int); + val = &ro->fd_frames; + break; + default: return -ENOPROTOOPT; } @@ -662,8 +687,13 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock, } else ifindex = ro->ifindex; - if (size != sizeof(struct can_frame)) - return -EINVAL; + if (ro->fd_frames) { + if (unlikely(size != CANFD_MTU && size != CAN_MTU)) + return -EINVAL; + } else { + if (unlikely(size != CAN_MTU)) + return -EINVAL; + } dev = dev_get_by_index(&init_net, ifindex); if (!dev) @@ -705,7 +735,9 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags) { struct sock *sk = sock->sk; + struct raw_sock *ro = raw_sk(sk); struct sk_buff *skb; + int rxmtu; int err = 0; int noblock; @@ -716,10 +748,20 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock, if (!skb) return err; - if (size < skb->len) + /* + * when serving a legacy socket the DLC <= 8 is already checked inside + * raw_rcv(). Now check if we need to pass a canfd_frame to a legacy + * socket and cut the possible CANFD_MTU/CAN_MTU length to CAN_MTU + */ + if (!ro->fd_frames) + rxmtu = CAN_MTU; + else + rxmtu = skb->len; + + if (size < rxmtu) msg->msg_flags |= MSG_TRUNC; else - size = skb->len; + size = rxmtu; err = memcpy_toiovec(msg->msg_iov, skb->data, size); if (err < 0) { -- cgit v1.2.3-59-g8ed1b From 1e0625facab2e871472472b7df87d8fbe6caf75a Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 13 Jun 2012 20:48:21 +0200 Subject: candev: add/update helpers for CAN FD - update sanity checks - add DLC to length conversion helpers - can_dlc2len() - get data length from can_dlc with sanitized can_dlc - can_len2dlc() - map the sanitized data length to an appropriate DLC Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- drivers/net/can/dev.c | 35 ++++++++++++++++++++++++++++++++++- include/linux/can/dev.h | 33 +++++++++++++++++++++++++-------- 2 files changed, 59 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index f03d7a481a80..239e4dd92ca1 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -33,6 +33,39 @@ MODULE_DESCRIPTION(MOD_DESC); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Wolfgang Grandegger "); +/* CAN DLC to real data length conversion helpers */ + +static const u8 dlc2len[] = {0, 1, 2, 3, 4, 5, 6, 7, + 8, 12, 16, 20, 24, 32, 48, 64}; + +/* get data length from can_dlc with sanitized can_dlc */ +u8 can_dlc2len(u8 can_dlc) +{ + return dlc2len[can_dlc & 0x0F]; +} +EXPORT_SYMBOL_GPL(can_dlc2len); + +static const u8 len2dlc[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0 - 8 */ + 9, 9, 9, 9, /* 9 - 12 */ + 10, 10, 10, 10, /* 13 - 16 */ + 11, 11, 11, 11, /* 17 - 20 */ + 12, 12, 12, 12, /* 21 - 24 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 25 - 32 */ + 14, 14, 14, 14, 14, 14, 14, 14, /* 33 - 40 */ + 14, 14, 14, 14, 14, 14, 14, 14, /* 41 - 48 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 49 - 56 */ + 15, 15, 15, 15, 15, 15, 15, 15}; /* 57 - 64 */ + +/* map the sanitized data length to an appropriate data length code */ +u8 can_len2dlc(u8 len) +{ + if (unlikely(len > 64)) + return 0xF; + + return len2dlc[len]; +} +EXPORT_SYMBOL_GPL(can_len2dlc); + #ifdef CONFIG_CAN_CALC_BITTIMING #define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */ @@ -454,7 +487,7 @@ EXPORT_SYMBOL_GPL(can_bus_off); static void can_setup(struct net_device *dev) { dev->type = ARPHRD_CAN; - dev->mtu = sizeof(struct can_frame); + dev->mtu = CAN_MTU; dev->hard_header_len = 0; dev->addr_len = 0; dev->tx_queue_len = 10; diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h index 5d2efe7e3f1b..ee5a771fb20d 100644 --- a/include/linux/can/dev.h +++ b/include/linux/can/dev.h @@ -61,23 +61,40 @@ struct can_priv { * To be used in the CAN netdriver receive path to ensure conformance with * ISO 11898-1 Chapter 8.4.2.3 (DLC field) */ -#define get_can_dlc(i) (min_t(__u8, (i), 8)) +#define get_can_dlc(i) (min_t(__u8, (i), CAN_MAX_DLC)) +#define get_canfd_dlc(i) (min_t(__u8, (i), CANFD_MAX_DLC)) /* Drop a given socketbuffer if it does not contain a valid CAN frame. */ static inline int can_dropped_invalid_skb(struct net_device *dev, struct sk_buff *skb) { - const struct can_frame *cf = (struct can_frame *)skb->data; - - if (unlikely(skb->len != sizeof(*cf) || cf->can_dlc > 8)) { - kfree_skb(skb); - dev->stats.tx_dropped++; - return 1; - } + const struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + + if (skb->protocol == htons(ETH_P_CAN)) { + if (unlikely(skb->len != CAN_MTU || + cfd->len > CAN_MAX_DLEN)) + goto inval_skb; + } else if (skb->protocol == htons(ETH_P_CANFD)) { + if (unlikely(skb->len != CANFD_MTU || + cfd->len > CANFD_MAX_DLEN)) + goto inval_skb; + } else + goto inval_skb; return 0; + +inval_skb: + kfree_skb(skb); + dev->stats.tx_dropped++; + return 1; } +/* get data length from can_dlc with sanitized can_dlc */ +u8 can_dlc2len(u8 can_dlc); + +/* map the sanitized data length to an appropriate data length code */ +u8 can_len2dlc(u8 len); + struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max); void free_candev(struct net_device *dev); -- cgit v1.2.3-59-g8ed1b From 0402788a6cda4e204a805e83eaaff64fef9e4418 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:03 +0000 Subject: team: make team_mode struct const Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 55 +++++++++++++++++++++---------- drivers/net/team/team_mode_activebackup.c | 2 +- drivers/net/team/team_mode_loadbalance.c | 2 +- drivers/net/team/team_mode_roundrobin.c | 2 +- include/linux/if_team.h | 5 ++- 5 files changed, 43 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index c61ae35a53ce..bdf87a971382 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -371,13 +371,18 @@ static int team_option_set(struct team *team, static LIST_HEAD(mode_list); static DEFINE_SPINLOCK(mode_list_lock); -static struct team_mode *__find_mode(const char *kind) +struct team_mode_item { + struct list_head list; + const struct team_mode *mode; +}; + +static struct team_mode_item *__find_mode(const char *kind) { - struct team_mode *mode; + struct team_mode_item *mitem; - list_for_each_entry(mode, &mode_list, list) { - if (strcmp(mode->kind, kind) == 0) - return mode; + list_for_each_entry(mitem, &mode_list, list) { + if (strcmp(mitem->mode->kind, kind) == 0) + return mitem; } return NULL; } @@ -392,49 +397,65 @@ static bool is_good_mode_name(const char *name) return true; } -int team_mode_register(struct team_mode *mode) +int team_mode_register(const struct team_mode *mode) { int err = 0; + struct team_mode_item *mitem; if (!is_good_mode_name(mode->kind) || mode->priv_size > TEAM_MODE_PRIV_SIZE) return -EINVAL; + + mitem = kmalloc(sizeof(*mitem), GFP_KERNEL); + if (!mitem) + return -ENOMEM; + spin_lock(&mode_list_lock); if (__find_mode(mode->kind)) { err = -EEXIST; + kfree(mitem); goto unlock; } - list_add_tail(&mode->list, &mode_list); + mitem->mode = mode; + list_add_tail(&mitem->list, &mode_list); unlock: spin_unlock(&mode_list_lock); return err; } EXPORT_SYMBOL(team_mode_register); -int team_mode_unregister(struct team_mode *mode) +void team_mode_unregister(const struct team_mode *mode) { + struct team_mode_item *mitem; + spin_lock(&mode_list_lock); - list_del_init(&mode->list); + mitem = __find_mode(mode->kind); + if (mitem) { + list_del_init(&mitem->list); + kfree(mitem); + } spin_unlock(&mode_list_lock); - return 0; } EXPORT_SYMBOL(team_mode_unregister); -static struct team_mode *team_mode_get(const char *kind) +static const struct team_mode *team_mode_get(const char *kind) { - struct team_mode *mode; + struct team_mode_item *mitem; + const struct team_mode *mode = NULL; spin_lock(&mode_list_lock); - mode = __find_mode(kind); - if (!mode) { + mitem = __find_mode(kind); + if (!mitem) { spin_unlock(&mode_list_lock); request_module("team-mode-%s", kind); spin_lock(&mode_list_lock); - mode = __find_mode(kind); + mitem = __find_mode(kind); } - if (mode) + if (mitem) { + mode = mitem->mode; if (!try_module_get(mode->owner)) mode = NULL; + } spin_unlock(&mode_list_lock); return mode; @@ -523,7 +544,7 @@ static int __team_change_mode(struct team *team, static int team_change_mode(struct team *team, const char *kind) { - struct team_mode *new_mode; + const struct team_mode *new_mode; struct net_device *dev = team->dev; int err; diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c index fd6bd03aaa89..acd925f070b5 100644 --- a/drivers/net/team/team_mode_activebackup.c +++ b/drivers/net/team/team_mode_activebackup.c @@ -108,7 +108,7 @@ static const struct team_mode_ops ab_mode_ops = { .port_leave = ab_port_leave, }; -static struct team_mode ab_mode = { +static const struct team_mode ab_mode = { .kind = "activebackup", .owner = THIS_MODULE, .priv_size = sizeof(struct ab_priv), diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index 86e8183c8e3d..6452428a1d6c 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -148,7 +148,7 @@ static const struct team_mode_ops lb_mode_ops = { .transmit = lb_transmit, }; -static struct team_mode lb_mode = { +static const struct team_mode lb_mode = { .kind = "loadbalance", .owner = THIS_MODULE, .priv_size = sizeof(struct lb_priv), diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c index 6abfbdc96be5..daafca2b2da4 100644 --- a/drivers/net/team/team_mode_roundrobin.c +++ b/drivers/net/team/team_mode_roundrobin.c @@ -81,7 +81,7 @@ static const struct team_mode_ops rr_mode_ops = { .port_change_mac = rr_port_change_mac, }; -static struct team_mode rr_mode = { +static const struct team_mode rr_mode = { .kind = "roundrobin", .owner = THIS_MODULE, .priv_size = sizeof(struct rr_priv), diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 8185f57a9c7f..d45fcd5a188d 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -105,7 +105,6 @@ struct team_option { }; struct team_mode { - struct list_head list; const char *kind; struct module *owner; size_t priv_size; @@ -178,8 +177,8 @@ extern int team_options_register(struct team *team, extern void team_options_unregister(struct team *team, const struct team_option *option, size_t option_count); -extern int team_mode_register(struct team_mode *mode); -extern int team_mode_unregister(struct team_mode *mode); +extern int team_mode_register(const struct team_mode *mode); +extern void team_mode_unregister(const struct team_mode *mode); #endif /* __KERNEL__ */ -- cgit v1.2.3-59-g8ed1b From 5149ee58385bdfef260fb07a89a8ff0913be6b25 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:05 +0000 Subject: team: add mode priv to port Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 3 ++- include/linux/if_team.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 343f4ffaf8f0..dea2d8afa2f4 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -793,7 +793,8 @@ static int team_port_add(struct team *team, struct net_device *port_dev) return -EBUSY; } - port = kzalloc(sizeof(struct team_port), GFP_KERNEL); + port = kzalloc(sizeof(struct team_port) + team->mode->port_priv_size, + GFP_KERNEL); if (!port) return -ENOMEM; diff --git a/include/linux/if_team.h b/include/linux/if_team.h index d45fcd5a188d..54af95f5d58b 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -61,6 +61,7 @@ struct team_port { } orig; struct rcu_head rcu; + long mode_priv[0]; }; struct team_mode_ops { @@ -108,6 +109,7 @@ struct team_mode { const char *kind; struct module *owner; size_t priv_size; + size_t port_priv_size; const struct team_mode_ops *ops; }; -- cgit v1.2.3-59-g8ed1b From b13033262d2496e271444d5a09226a2be5ceb989 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:08 +0000 Subject: team: introduce array options Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 75 ++++++++++++++++++++++++++++++++++--------------- include/linux/if_team.h | 3 ++ 2 files changed, 55 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index f50b8ca8dc94..32cb290fb800 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -90,6 +90,7 @@ struct team_option_inst { /* One for each option instance */ struct list_head list; struct team_option *option; struct team_port *port; /* != NULL if per-port */ + u32 array_index; bool changed; bool removed; }; @@ -106,22 +107,6 @@ static struct team_option *__team_find_option(struct team *team, return NULL; } -static int __team_option_inst_add(struct team *team, struct team_option *option, - struct team_port *port) -{ - struct team_option_inst *opt_inst; - - opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL); - if (!opt_inst) - return -ENOMEM; - opt_inst->option = option; - opt_inst->port = port; - opt_inst->changed = true; - opt_inst->removed = false; - list_add_tail(&opt_inst->list, &team->option_inst_list); - return 0; -} - static void __team_option_inst_del(struct team_option_inst *opt_inst) { list_del(&opt_inst->list); @@ -139,14 +124,42 @@ static void __team_option_inst_del_option(struct team *team, } } +static int __team_option_inst_add(struct team *team, struct team_option *option, + struct team_port *port) +{ + struct team_option_inst *opt_inst; + unsigned int array_size; + unsigned int i; + + array_size = option->array_size; + if (!array_size) + array_size = 1; /* No array but still need one instance */ + + for (i = 0; i < array_size; i++) { + opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL); + if (!opt_inst) + return -ENOMEM; + opt_inst->option = option; + opt_inst->port = port; + opt_inst->array_index = i; + opt_inst->changed = true; + opt_inst->removed = false; + list_add_tail(&opt_inst->list, &team->option_inst_list); + } + return 0; +} + static int __team_option_inst_add_option(struct team *team, struct team_option *option) { struct team_port *port; int err; - if (!option->per_port) - return __team_option_inst_add(team, option, 0); + if (!option->per_port) { + err = __team_option_inst_add(team, option, 0); + if (err) + goto inst_del_option; + } list_for_each_entry(port, &team->port_list, list) { err = __team_option_inst_add(team, option, port); @@ -1567,6 +1580,11 @@ static int team_nl_fill_options_get(struct sk_buff *skb, opt_inst->port->dev->ifindex)) goto nla_put_failure; ctx.port = opt_inst->port; + if (opt_inst->option->array_size && + nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX, + opt_inst->array_index)) + goto nla_put_failure; + ctx.array_index = opt_inst->array_index; switch (option->type) { case TEAM_OPTION_TYPE_U32: if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32)) @@ -1668,10 +1686,12 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) { struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1]; - struct nlattr *attr_port_ifindex; + struct nlattr *attr; struct nlattr *attr_data; enum team_option_type opt_type; int opt_port_ifindex = 0; /* != 0 for per-port options */ + u32 opt_array_index = 0; + bool opt_is_array = false; struct team_option_inst *opt_inst; char *opt_name; bool opt_found = false; @@ -1713,9 +1733,15 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) } opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]); - attr_port_ifindex = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX]; - if (attr_port_ifindex) - opt_port_ifindex = nla_get_u32(attr_port_ifindex); + attr = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX]; + if (attr) + opt_port_ifindex = nla_get_u32(attr); + + attr = opt_attrs[TEAM_ATTR_OPTION_ARRAY_INDEX]; + if (attr) { + opt_is_array = true; + opt_array_index = nla_get_u32(attr); + } list_for_each_entry(opt_inst, &team->option_inst_list, list) { struct team_option *option = opt_inst->option; @@ -1726,10 +1752,13 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) opt_inst->port->dev->ifindex : 0; if (option->type != opt_type || strcmp(option->name, opt_name) || - tmp_ifindex != opt_port_ifindex) + tmp_ifindex != opt_port_ifindex || + (option->array_size && !opt_is_array) || + opt_inst->array_index != opt_array_index) continue; opt_found = true; ctx.port = opt_inst->port; + ctx.array_index = opt_inst->array_index; switch (opt_type) { case TEAM_OPTION_TYPE_U32: ctx.data.u32_val = nla_get_u32(attr_data); diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 54af95f5d58b..b1719e239a0b 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -93,6 +93,7 @@ struct team_gsetter_ctx { } bin_val; bool bool_val; } data; + u32 array_index; struct team_port *port; }; @@ -100,6 +101,7 @@ struct team_option { struct list_head list; const char *name; bool per_port; + unsigned int array_size; /* != 0 means the option is array */ enum team_option_type type; int (*getter)(struct team *team, struct team_gsetter_ctx *ctx); int (*setter)(struct team *team, struct team_gsetter_ctx *ctx); @@ -242,6 +244,7 @@ enum { TEAM_ATTR_OPTION_DATA, /* dynamic */ TEAM_ATTR_OPTION_REMOVED, /* flag */ TEAM_ATTR_OPTION_PORT_IFINDEX, /* u32 */ /* for per-port options */ + TEAM_ATTR_OPTION_ARRAY_INDEX, /* u32 */ /* for array options */ __TEAM_ATTR_OPTION_MAX, TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1, -- cgit v1.2.3-59-g8ed1b From 85d59a87248de90e3266e10dce99477b60f524c0 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:10 +0000 Subject: team: push array_index and port into separate structure Introduce struct team_option_inst_info and push option instance info there. It can be then easily passed to gsetter context and used for feature async option changes. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 68 +++++++++++++++++++++++++++++++------------------ include/linux/if_team.h | 9 +++++-- 2 files changed, 50 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 7ec53f81c1c4..cff8e253df72 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -89,8 +89,7 @@ static void team_refresh_port_linkup(struct team_port *port) struct team_option_inst { /* One for each option instance */ struct list_head list; struct team_option *option; - struct team_port *port; /* != NULL if per-port */ - u32 array_index; + struct team_option_inst_info info; bool changed; bool removed; }; @@ -130,6 +129,7 @@ static int __team_option_inst_add(struct team *team, struct team_option *option, struct team_option_inst *opt_inst; unsigned int array_size; unsigned int i; + int err; array_size = option->array_size; if (!array_size) @@ -140,11 +140,17 @@ static int __team_option_inst_add(struct team *team, struct team_option *option, if (!opt_inst) return -ENOMEM; opt_inst->option = option; - opt_inst->port = port; - opt_inst->array_index = i; + opt_inst->info.port = port; + opt_inst->info.array_index = i; opt_inst->changed = true; opt_inst->removed = false; list_add_tail(&opt_inst->list, &team->option_inst_list); + if (option->init) { + err = option->init(team, &opt_inst->info); + if (err) + return err; + } + } return 0; } @@ -193,7 +199,7 @@ static void __team_option_inst_del_port(struct team *team, list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) { if (opt_inst->option->per_port && - opt_inst->port == port) + opt_inst->info.port == port) __team_option_inst_del(opt_inst); } } @@ -224,7 +230,7 @@ static void __team_option_inst_mark_removed_port(struct team *team, struct team_option_inst *opt_inst; list_for_each_entry(opt_inst, &team->option_inst_list, list) { - if (opt_inst->port == port) { + if (opt_inst->info.port == port) { opt_inst->changed = true; opt_inst->removed = true; } @@ -958,39 +964,47 @@ static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx) static int team_port_en_option_get(struct team *team, struct team_gsetter_ctx *ctx) { - ctx->data.bool_val = team_port_enabled(ctx->port); + struct team_port *port = ctx->info->port; + + ctx->data.bool_val = team_port_enabled(port); return 0; } static int team_port_en_option_set(struct team *team, struct team_gsetter_ctx *ctx) { + struct team_port *port = ctx->info->port; + if (ctx->data.bool_val) - team_port_enable(team, ctx->port); + team_port_enable(team, port); else - team_port_disable(team, ctx->port); + team_port_disable(team, port); return 0; } static int team_user_linkup_option_get(struct team *team, struct team_gsetter_ctx *ctx) { - ctx->data.bool_val = ctx->port->user.linkup; + struct team_port *port = ctx->info->port; + + ctx->data.bool_val = port->user.linkup; return 0; } static int team_user_linkup_option_set(struct team *team, struct team_gsetter_ctx *ctx) { - ctx->port->user.linkup = ctx->data.bool_val; - team_refresh_port_linkup(ctx->port); + struct team_port *port = ctx->info->port; + + port->user.linkup = ctx->data.bool_val; + team_refresh_port_linkup(port); return 0; } static int team_user_linkup_en_option_get(struct team *team, struct team_gsetter_ctx *ctx) { - struct team_port *port = ctx->port; + struct team_port *port = ctx->info->port; ctx->data.bool_val = port->user.linkup_enabled; return 0; @@ -999,10 +1013,10 @@ static int team_user_linkup_en_option_get(struct team *team, static int team_user_linkup_en_option_set(struct team *team, struct team_gsetter_ctx *ctx) { - struct team_port *port = ctx->port; + struct team_port *port = ctx->info->port; port->user.linkup_enabled = ctx->data.bool_val; - team_refresh_port_linkup(ctx->port); + team_refresh_port_linkup(port); return 0; } @@ -1557,6 +1571,7 @@ static int team_nl_fill_options_get(struct sk_buff *skb, list_for_each_entry(opt_inst, &team->option_inst_list, list) { struct nlattr *option_item; struct team_option *option = opt_inst->option; + struct team_option_inst_info *opt_inst_info; struct team_gsetter_ctx ctx; /* Include only changed options if fill all mode is not on */ @@ -1575,16 +1590,18 @@ static int team_nl_fill_options_get(struct sk_buff *skb, if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED)) goto nla_put_failure; - if (opt_inst->port && + + opt_inst_info = &opt_inst->info; + if (opt_inst_info->port && nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX, - opt_inst->port->dev->ifindex)) + opt_inst_info->port->dev->ifindex)) goto nla_put_failure; - ctx.port = opt_inst->port; if (opt_inst->option->array_size && nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX, - opt_inst->array_index)) + opt_inst_info->array_index)) goto nla_put_failure; - ctx.array_index = opt_inst->array_index; + ctx.info = opt_inst_info; + switch (option->type) { case TEAM_OPTION_TYPE_U32: if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32)) @@ -1746,19 +1763,20 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) list_for_each_entry(opt_inst, &team->option_inst_list, list) { struct team_option *option = opt_inst->option; struct team_gsetter_ctx ctx; + struct team_option_inst_info *opt_inst_info; int tmp_ifindex; - tmp_ifindex = opt_inst->port ? - opt_inst->port->dev->ifindex : 0; + opt_inst_info = &opt_inst->info; + tmp_ifindex = opt_inst_info->port ? + opt_inst_info->port->dev->ifindex : 0; if (option->type != opt_type || strcmp(option->name, opt_name) || tmp_ifindex != opt_port_ifindex || (option->array_size && !opt_is_array) || - opt_inst->array_index != opt_array_index) + opt_inst_info->array_index != opt_array_index) continue; opt_found = true; - ctx.port = opt_inst->port; - ctx.array_index = opt_inst->array_index; + ctx.info = opt_inst_info; switch (opt_type) { case TEAM_OPTION_TYPE_U32: ctx.data.u32_val = nla_get_u32(attr_data); diff --git a/include/linux/if_team.h b/include/linux/if_team.h index b1719e239a0b..30854cb0c855 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -83,6 +83,11 @@ enum team_option_type { TEAM_OPTION_TYPE_BOOL, }; +struct team_option_inst_info { + u32 array_index; + struct team_port *port; /* != NULL if per-port */ +}; + struct team_gsetter_ctx { union { u32 u32_val; @@ -93,8 +98,7 @@ struct team_gsetter_ctx { } bin_val; bool bool_val; } data; - u32 array_index; - struct team_port *port; + struct team_option_inst_info *info; }; struct team_option { @@ -103,6 +107,7 @@ struct team_option { bool per_port; unsigned int array_size; /* != 0 means the option is array */ enum team_option_type type; + int (*init)(struct team *team, struct team_option_inst_info *info); int (*getter)(struct team *team, struct team_gsetter_ctx *ctx); int (*setter)(struct team *team, struct team_gsetter_ctx *ctx); }; -- cgit v1.2.3-59-g8ed1b From 0f1aad2b7f01d88782fbf4ab08b13a7d92b9b6b2 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:11 +0000 Subject: team: allow async option changes This patch adds two exported functions. One allows to mark option instance as changed and the second processes change check and does transfer of changed options to userspace. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 18 ++++++++++++++++++ include/linux/if_team.h | 3 +++ 2 files changed, 21 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index cff8e253df72..7988ba099b94 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -82,6 +82,7 @@ static void team_refresh_port_linkup(struct team_port *port) port->state.linkup; } + /******************* * Options handling *******************/ @@ -387,6 +388,22 @@ static int team_option_set(struct team *team, return err; } +void team_option_inst_set_change(struct team_option_inst_info *opt_inst_info) +{ + struct team_option_inst *opt_inst; + + opt_inst = container_of(opt_inst_info, struct team_option_inst, info); + opt_inst->changed = true; +} +EXPORT_SYMBOL(team_option_inst_set_change); + +void team_options_change_check(struct team *team) +{ + __team_options_change_check(team); +} +EXPORT_SYMBOL(team_options_change_check); + + /**************** * Mode handling ****************/ @@ -2051,6 +2068,7 @@ static void team_port_change_check(struct team_port *port, bool linkup) mutex_unlock(&team->lock); } + /************************************ * Net device notifier event handler ************************************/ diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 30854cb0c855..2f2972535cc0 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -112,6 +112,9 @@ struct team_option { int (*setter)(struct team *team, struct team_gsetter_ctx *ctx); }; +extern void team_option_inst_set_change(struct team_option_inst_info *opt_inst_info); +extern void team_options_change_check(struct team *team); + struct team_mode { const char *kind; struct module *owner; -- cgit v1.2.3-59-g8ed1b From 4bccfd17e1f77593e99d5321c48c704a0a43ab68 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:16 +0000 Subject: team: add port_[enabled/disabled] mode callbacks Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 4 ++++ include/linux/if_team.h | 2 ++ 2 files changed, 6 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index eb18ac93095a..bc76f946e071 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -714,6 +714,8 @@ static void team_port_enable(struct team *team, port->index = team->en_port_count++; hlist_add_head_rcu(&port->hlist, team_port_index_hash(team, port->index)); + if (team->ops.port_enabled) + team->ops.port_enabled(team, port); } static void __reconstruct_port_hlist(struct team *team, int rm_index) @@ -737,6 +739,8 @@ static void team_port_disable(struct team *team, if (!team_port_enabled(port)) return; + if (team->ops.port_disabled) + team->ops.port_disabled(team, port); hlist_del_rcu(&port->hlist); __reconstruct_port_hlist(team, rm_index); team->en_port_count--; diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 2f2972535cc0..c1938869191f 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -74,6 +74,8 @@ struct team_mode_ops { int (*port_enter)(struct team *team, struct team_port *port); void (*port_leave)(struct team *team, struct team_port *port); void (*port_change_mac)(struct team *team, struct team_port *port); + void (*port_enabled)(struct team *team, struct team_port *port); + void (*port_disabled)(struct team *team, struct team_port *port); }; enum team_option_type { -- cgit v1.2.3-59-g8ed1b From af8b5fc31099abd7f3b297332c9e280ec0b30a71 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Tue, 19 Jun 2012 07:08:22 +0000 Subject: regulator: add new regulator driver for lp872x This driver supports TI/National LP8720, LP8725 PMIC. Signed-off-by: Milo(Woogyom) Kim Reviewed-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/lp872x.c | 957 +++++++++++++++++++++++++++++++++++++++ include/linux/regulator/lp872x.h | 90 ++++ 4 files changed, 1055 insertions(+) create mode 100644 drivers/regulator/lp872x.c create mode 100644 include/linux/regulator/lp872x.h (limited to 'include/linux') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 00ffe05d8026..870f7300b156 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -224,6 +224,13 @@ config REGULATOR_LP3972 Say Y here to support the voltage regulators and convertors on National Semiconductors LP3972 PMIC +config REGULATOR_LP872X + tristate "TI/National Semiconductor LP8720/LP8725 voltage regulators" + depends on I2C + select REGMAP_I2C + help + This driver supports LP8720/LP8725 PMIC + config REGULATOR_PCF50633 tristate "NXP PCF50633 regulator driver" depends on MFD_PCF50633 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index d8544539efec..e6f700748a3b 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o +obj-$(CONFIG_REGULATOR_LP872X) += lp872x.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c new file mode 100644 index 000000000000..d51d09852041 --- /dev/null +++ b/drivers/regulator/lp872x.c @@ -0,0 +1,957 @@ +/* + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * 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 +#include +#include +#include + +/* Registers : LP8720/8725 shared */ +#define LP872X_GENERAL_CFG 0x00 +#define LP872X_LDO1_VOUT 0x01 +#define LP872X_LDO2_VOUT 0x02 +#define LP872X_LDO3_VOUT 0x03 +#define LP872X_LDO4_VOUT 0x04 +#define LP872X_LDO5_VOUT 0x05 + +/* Registers : LP8720 */ +#define LP8720_BUCK_VOUT1 0x06 +#define LP8720_BUCK_VOUT2 0x07 +#define LP8720_ENABLE 0x08 + +/* Registers : LP8725 */ +#define LP8725_LILO1_VOUT 0x06 +#define LP8725_LILO2_VOUT 0x07 +#define LP8725_BUCK1_VOUT1 0x08 +#define LP8725_BUCK1_VOUT2 0x09 +#define LP8725_BUCK2_VOUT1 0x0A +#define LP8725_BUCK2_VOUT2 0x0B +#define LP8725_BUCK_CTRL 0x0C +#define LP8725_LDO_CTRL 0x0D + +/* Mask/shift : LP8720/LP8725 shared */ +#define LP872X_VOUT_M 0x1F +#define LP872X_START_DELAY_M 0xE0 +#define LP872X_START_DELAY_S 5 +#define LP872X_EN_LDO1_M BIT(0) +#define LP872X_EN_LDO2_M BIT(1) +#define LP872X_EN_LDO3_M BIT(2) +#define LP872X_EN_LDO4_M BIT(3) +#define LP872X_EN_LDO5_M BIT(4) + +/* Mask/shift : LP8720 */ +#define LP8720_TIMESTEP_S 0 /* Addr 00h */ +#define LP8720_TIMESTEP_M BIT(0) +#define LP8720_EXT_DVS_M BIT(2) +#define LP8720_BUCK_FPWM_S 5 /* Addr 07h */ +#define LP8720_BUCK_FPWM_M BIT(5) +#define LP8720_EN_BUCK_M BIT(5) /* Addr 08h */ +#define LP8720_DVS_SEL_M BIT(7) + +/* Mask/shift : LP8725 */ +#define LP8725_TIMESTEP_M 0xC0 /* Addr 00h */ +#define LP8725_TIMESTEP_S 6 +#define LP8725_BUCK1_EN_M BIT(0) +#define LP8725_DVS1_M BIT(2) +#define LP8725_DVS2_M BIT(3) +#define LP8725_BUCK2_EN_M BIT(4) +#define LP8725_BUCK_CL_M 0xC0 /* Addr 09h, 0Bh */ +#define LP8725_BUCK_CL_S 6 +#define LP8725_BUCK1_FPWM_S 1 /* Addr 0Ch */ +#define LP8725_BUCK1_FPWM_M BIT(1) +#define LP8725_BUCK2_FPWM_S 5 +#define LP8725_BUCK2_FPWM_M BIT(5) +#define LP8725_EN_LILO1_M BIT(5) /* Addr 0Dh */ +#define LP8725_EN_LILO2_M BIT(6) + +/* PWM mode */ +#define LP872X_FORCE_PWM 1 +#define LP872X_AUTO_PWM 0 + +#define LP8720_NUM_REGULATORS 6 +#define LP8725_NUM_REGULATORS 9 +#define EXTERN_DVS_USED 0 +#define MAX_DELAY 6 + +/* dump registers in regmap-debugfs */ +#define MAX_REGISTERS 0x0F + +enum lp872x_id { + LP8720, + LP8725, +}; + +struct lp872x { + struct regmap *regmap; + struct device *dev; + enum lp872x_id chipid; + struct lp872x_platform_data *pdata; + struct regulator_dev **regulators; + int num_regulators; + enum lp872x_dvs_state dvs_pin; + int dvs_gpio; +}; + +/* LP8720/LP8725 shared voltage table for LDOs */ +static const unsigned int lp872x_ldo_vtbl[] = { + 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, 1550000, + 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, 2000000, + 2100000, 2200000, 2300000, 2400000, 2500000, 2600000, 2650000, 2700000, + 2750000, 2800000, 2850000, 2900000, 2950000, 3000000, 3100000, 3300000, +}; + +/* LP8720 LDO4 voltage table */ +static const unsigned int lp8720_ldo4_vtbl[] = { + 800000, 850000, 900000, 1000000, 1100000, 1200000, 1250000, 1300000, + 1350000, 1400000, 1450000, 1500000, 1550000, 1600000, 1650000, 1700000, + 1750000, 1800000, 1850000, 1900000, 2000000, 2100000, 2200000, 2300000, + 2400000, 2500000, 2600000, 2650000, 2700000, 2750000, 2800000, 2850000, +}; + +/* LP8725 LILO(Low Input Low Output) voltage table */ +static const unsigned int lp8725_lilo_vtbl[] = { + 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, 1150000, + 1200000, 1250000, 1300000, 1350000, 1400000, 1500000, 1600000, 1700000, + 1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000, + 2600000, 2700000, 2800000, 2850000, 2900000, 3000000, 3100000, 3300000, +}; + +/* LP8720 BUCK voltage table */ +#define EXT_R 0 /* external resistor divider */ +static const unsigned int lp8720_buck_vtbl[] = { + EXT_R, 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, + 1150000, 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, + 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, + 1950000, 2000000, 2050000, 2100000, 2150000, 2200000, 2250000, 2300000, +}; + +/* LP8725 BUCK voltage table */ +static const unsigned int lp8725_buck_vtbl[] = { + 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, 1150000, + 1200000, 1250000, 1300000, 1350000, 1400000, 1500000, 1600000, 1700000, + 1750000, 1800000, 1850000, 1900000, 2000000, 2100000, 2200000, 2300000, + 2400000, 2500000, 2600000, 2700000, 2800000, 2850000, 2900000, 3000000, +}; + +/* LP8725 BUCK current limit */ +static const unsigned int lp8725_buck_uA[] = { + 460000, 780000, 1050000, 1370000, +}; + +static int lp872x_read_byte(struct lp872x *lp, u8 addr, u8 *data) +{ + int ret; + unsigned int val; + + ret = regmap_read(lp->regmap, addr, &val); + if (ret < 0) { + dev_err(lp->dev, "failed to read 0x%.2x\n", addr); + return ret; + } + + *data = (u8)val; + return 0; +} + +static inline int lp872x_write_byte(struct lp872x *lp, u8 addr, u8 data) +{ + return regmap_write(lp->regmap, addr, data); +} + +static inline int lp872x_update_bits(struct lp872x *lp, u8 addr, + unsigned int mask, u8 data) +{ + return regmap_update_bits(lp->regmap, addr, mask, data); +} + +static int _rdev_to_offset(struct regulator_dev *rdev) +{ + enum lp872x_regulator_id id = rdev_get_id(rdev); + + switch (id) { + case LP8720_ID_LDO1 ... LP8720_ID_BUCK: + return id; + case LP8725_ID_LDO1 ... LP8725_ID_BUCK2: + return id - LP8725_ID_BASE; + default: + return -EINVAL; + } +} + +static int lp872x_get_timestep_usec(struct lp872x *lp) +{ + enum lp872x_id chip = lp->chipid; + u8 val, mask, shift; + int *time_usec, size, ret; + int lp8720_time_usec[] = { 25, 50 }; + int lp8725_time_usec[] = { 32, 64, 128, 256 }; + + switch (chip) { + case LP8720: + mask = LP8720_TIMESTEP_M; + shift = LP8720_TIMESTEP_S; + time_usec = &lp8720_time_usec[0]; + size = ARRAY_SIZE(lp8720_time_usec); + break; + case LP8725: + mask = LP8725_TIMESTEP_M; + shift = LP8725_TIMESTEP_S; + time_usec = &lp8725_time_usec[0]; + size = ARRAY_SIZE(lp8725_time_usec); + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val); + if (ret) + return -EINVAL; + + val = (val & mask) >> shift; + if (val >= size) + return -EINVAL; + + return *(time_usec + val); +} + +static int lp872x_regulator_enable_time(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id regulator = rdev_get_id(rdev); + int time_step_us = lp872x_get_timestep_usec(lp); + int ret, offset; + u8 addr, val; + + if (time_step_us < 0) + return -EINVAL; + + switch (regulator) { + case LP8720_ID_LDO1 ... LP8720_ID_LDO5: + case LP8725_ID_LDO1 ... LP8725_ID_LILO2: + offset = _rdev_to_offset(rdev); + if (offset < 0) + return -EINVAL; + + addr = LP872X_LDO1_VOUT + offset; + break; + case LP8720_ID_BUCK: + addr = LP8720_BUCK_VOUT1; + break; + case LP8725_ID_BUCK1: + addr = LP8725_BUCK1_VOUT1; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK2_VOUT1; + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + val = (val & LP872X_START_DELAY_M) >> LP872X_START_DELAY_S; + + return val > MAX_DELAY ? 0 : val * time_step_us; +} + +static void lp872x_set_dvs(struct lp872x *lp, int gpio) +{ + enum lp872x_dvs_sel dvs_sel = lp->pdata->dvs->vsel; + enum lp872x_dvs_state state; + + state = dvs_sel == SEL_V1 ? DVS_HIGH : DVS_LOW; + gpio_set_value(gpio, state); + lp->dvs_pin = state; +} + +static u8 lp872x_select_buck_vout_addr(struct lp872x *lp, + enum lp872x_regulator_id buck) +{ + u8 val, addr; + + if (lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val)) + return 0; + + switch (buck) { + case LP8720_ID_BUCK: + if (val & LP8720_EXT_DVS_M) { + addr = (lp->dvs_pin == DVS_HIGH) ? + LP8720_BUCK_VOUT1 : LP8720_BUCK_VOUT2; + } else { + if (lp872x_read_byte(lp, LP8720_ENABLE, &val)) + return 0; + + addr = val & LP8720_DVS_SEL_M ? + LP8720_BUCK_VOUT1 : LP8720_BUCK_VOUT2; + } + break; + case LP8725_ID_BUCK1: + if (val & LP8725_DVS1_M) + addr = LP8725_BUCK1_VOUT1; + else + addr = (lp->dvs_pin == DVS_HIGH) ? + LP8725_BUCK1_VOUT1 : LP8725_BUCK1_VOUT2; + break; + case LP8725_ID_BUCK2: + addr = val & LP8725_DVS2_M ? + LP8725_BUCK2_VOUT1 : LP8725_BUCK2_VOUT2; + break; + default: + return 0; + } + + return addr; +} + +static bool lp872x_is_valid_buck_addr(u8 addr) +{ + switch (addr) { + case LP8720_BUCK_VOUT1: + case LP8720_BUCK_VOUT2: + case LP8725_BUCK1_VOUT1: + case LP8725_BUCK1_VOUT2: + case LP8725_BUCK2_VOUT1: + case LP8725_BUCK2_VOUT2: + return true; + default: + return false; + } +} + +static int lp872x_buck_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, mask = LP872X_VOUT_M; + struct lp872x_dvs *dvs = lp->pdata->dvs; + + if (dvs && gpio_is_valid(dvs->gpio)) + lp872x_set_dvs(lp, dvs->gpio); + + addr = lp872x_select_buck_vout_addr(lp, buck); + if (!lp872x_is_valid_buck_addr(addr)) + return -EINVAL; + + return lp872x_update_bits(lp, addr, mask, selector); +} + +static int lp872x_buck_get_voltage_sel(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, val; + int ret; + + addr = lp872x_select_buck_vout_addr(lp, buck); + if (!lp872x_is_valid_buck_addr(addr)) + return -EINVAL; + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + return val & LP872X_VOUT_M; +} + +static int lp8725_buck_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + int i, max = ARRAY_SIZE(lp8725_buck_uA); + u8 addr, val; + + switch (buck) { + case LP8725_ID_BUCK1: + addr = LP8725_BUCK1_VOUT2; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK2_VOUT2; + break; + default: + return -EINVAL; + } + + for (i = 0 ; i < max ; i++) + if (lp8725_buck_uA[i] >= min_uA && + lp8725_buck_uA[i] <= max_uA) + break; + + if (i == max) + return -EINVAL; + + val = i << LP8725_BUCK_CL_S; + + return lp872x_update_bits(lp, addr, LP8725_BUCK_CL_M, val); +} + +static int lp8725_buck_get_current_limit(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, val; + int ret; + + switch (buck) { + case LP8725_ID_BUCK1: + addr = LP8725_BUCK1_VOUT2; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK2_VOUT2; + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + val = (val & LP8725_BUCK_CL_M) >> LP8725_BUCK_CL_S; + + return (val < ARRAY_SIZE(lp8725_buck_uA)) ? + lp8725_buck_uA[val] : -EINVAL; +} + +static int lp872x_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, mask, shift, val; + + switch (buck) { + case LP8720_ID_BUCK: + addr = LP8720_BUCK_VOUT2; + mask = LP8720_BUCK_FPWM_M; + shift = LP8720_BUCK_FPWM_S; + break; + case LP8725_ID_BUCK1: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK1_FPWM_M; + shift = LP8725_BUCK1_FPWM_S; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK2_FPWM_M; + shift = LP8725_BUCK2_FPWM_S; + break; + default: + return -EINVAL; + } + + if (mode == REGULATOR_MODE_FAST) + val = LP872X_FORCE_PWM << shift; + else if (mode == REGULATOR_MODE_NORMAL) + val = LP872X_AUTO_PWM << shift; + else + return -EINVAL; + + return lp872x_update_bits(lp, addr, mask, val); +} + +static unsigned int lp872x_buck_get_mode(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, mask, val; + int ret; + + switch (buck) { + case LP8720_ID_BUCK: + addr = LP8720_BUCK_VOUT2; + mask = LP8720_BUCK_FPWM_M; + break; + case LP8725_ID_BUCK1: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK1_FPWM_M; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK2_FPWM_M; + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + return val & mask ? REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops lp872x_ldo_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp872x_regulator_enable_time, +}; + +static struct regulator_ops lp8720_buck_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = lp872x_buck_set_voltage_sel, + .get_voltage_sel = lp872x_buck_get_voltage_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp872x_regulator_enable_time, + .set_mode = lp872x_buck_set_mode, + .get_mode = lp872x_buck_get_mode, +}; + +static struct regulator_ops lp8725_buck_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = lp872x_buck_set_voltage_sel, + .get_voltage_sel = lp872x_buck_get_voltage_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp872x_regulator_enable_time, + .set_mode = lp872x_buck_set_mode, + .get_mode = lp872x_buck_get_mode, + .set_current_limit = lp8725_buck_set_current_limit, + .get_current_limit = lp8725_buck_get_current_limit, +}; + +static struct regulator_desc lp8720_regulator_desc[] = { + { + .name = "ldo1", + .id = LP8720_ID_LDO1, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO1_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO1_M, + }, + { + .name = "ldo2", + .id = LP8720_ID_LDO2, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO2_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO2_M, + }, + { + .name = "ldo3", + .id = LP8720_ID_LDO3, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO3_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO3_M, + }, + { + .name = "ldo4", + .id = LP8720_ID_LDO4, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp8720_ldo4_vtbl), + .volt_table = lp8720_ldo4_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO4_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO4_M, + }, + { + .name = "ldo5", + .id = LP8720_ID_LDO5, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO5_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO5_M, + }, + { + .name = "buck", + .id = LP8720_ID_BUCK, + .ops = &lp8720_buck_ops, + .n_voltages = ARRAY_SIZE(lp8720_buck_vtbl), + .volt_table = lp8720_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP8720_EN_BUCK_M, + }, +}; + +static struct regulator_desc lp8725_regulator_desc[] = { + { + .name = "ldo1", + .id = LP8725_ID_LDO1, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO1_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO1_M, + }, + { + .name = "ldo2", + .id = LP8725_ID_LDO2, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO2_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO2_M, + }, + { + .name = "ldo3", + .id = LP8725_ID_LDO3, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO3_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO3_M, + }, + { + .name = "ldo4", + .id = LP8725_ID_LDO4, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO4_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO4_M, + }, + { + .name = "ldo5", + .id = LP8725_ID_LDO5, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO5_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO5_M, + }, + { + .name = "lilo1", + .id = LP8725_ID_LILO1, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp8725_lilo_vtbl), + .volt_table = lp8725_lilo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8725_LILO1_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP8725_EN_LILO1_M, + }, + { + .name = "lilo2", + .id = LP8725_ID_LILO2, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp8725_lilo_vtbl), + .volt_table = lp8725_lilo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8725_LILO2_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP8725_EN_LILO2_M, + }, + { + .name = "buck1", + .id = LP8725_ID_BUCK1, + .ops = &lp8725_buck_ops, + .n_voltages = ARRAY_SIZE(lp8725_buck_vtbl), + .volt_table = lp8725_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP872X_GENERAL_CFG, + .enable_mask = LP8725_BUCK1_EN_M, + }, + { + .name = "buck2", + .id = LP8725_ID_BUCK2, + .ops = &lp8725_buck_ops, + .n_voltages = ARRAY_SIZE(lp8725_buck_vtbl), + .volt_table = lp8725_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP872X_GENERAL_CFG, + .enable_mask = LP8725_BUCK2_EN_M, + }, +}; + +static int lp872x_check_dvs_validity(struct lp872x *lp) +{ + struct lp872x_dvs *dvs = lp->pdata->dvs; + u8 val = 0; + int ret; + + ret = lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val); + if (ret) + return ret; + + ret = 0; + if (lp->chipid == LP8720) { + if (val & LP8720_EXT_DVS_M) + ret = dvs ? 0 : -EINVAL; + } else { + if ((val & LP8725_DVS1_M) == EXTERN_DVS_USED) + ret = dvs ? 0 : -EINVAL; + } + + return ret; +} + +static int lp872x_init_dvs(struct lp872x *lp) +{ + int ret, gpio; + struct lp872x_dvs *dvs = lp->pdata->dvs; + enum lp872x_dvs_state pinstate; + + ret = lp872x_check_dvs_validity(lp); + if (ret) { + dev_warn(lp->dev, "invalid dvs data: %d\n", ret); + return ret; + } + + gpio = dvs->gpio; + if (!gpio_is_valid(gpio)) { + dev_err(lp->dev, "invalid gpio: %d\n", gpio); + return -EINVAL; + } + + pinstate = dvs->init_state; + ret = devm_gpio_request_one(lp->dev, gpio, pinstate, "LP872X DVS"); + if (ret) { + dev_err(lp->dev, "gpio request err: %d\n", ret); + return ret; + } + + lp->dvs_pin = pinstate; + lp->dvs_gpio = gpio; + + return 0; +} + +static int lp872x_config(struct lp872x *lp) +{ + struct lp872x_platform_data *pdata = lp->pdata; + int ret; + + if (!pdata) { + dev_warn(lp->dev, "no platform data\n"); + return 0; + } + + if (!pdata->update_config) + return 0; + + ret = lp872x_write_byte(lp, LP872X_GENERAL_CFG, pdata->general_config); + if (ret) + return ret; + + return lp872x_init_dvs(lp); +} + +static struct regulator_init_data +*lp872x_find_regulator_init_data(int idx, struct lp872x *lp) +{ + int i, base, id, max_regulators; + + switch (lp->chipid) { + case LP8720: + base = LP8720_ID_BASE; + max_regulators = LP8720_NUM_REGULATORS; + break; + case LP8725: + base = LP8725_ID_BASE; + max_regulators = LP8725_NUM_REGULATORS; + break; + default: + return NULL; + } + + id = base + idx; + for (i = 0 ; i < max_regulators ; i++) + if (lp->pdata->regulator_data[i].id == id) + break; + + return (i == max_regulators) ? NULL : + lp->pdata->regulator_data[i].init_data; +} + +static int lp872x_regulator_register(struct lp872x *lp) +{ + struct regulator_desc *desc; + struct regulator_config cfg = { }; + struct regulator_dev *rdev; + int i, ret; + + for (i = 0 ; i < lp->num_regulators ; i++) { + desc = (lp->chipid == LP8720) ? &lp8720_regulator_desc[i] : + &lp8725_regulator_desc[i]; + + cfg.dev = lp->dev; + cfg.init_data = lp872x_find_regulator_init_data(i, lp); + cfg.driver_data = lp; + cfg.regmap = lp->regmap; + + rdev = regulator_register(desc, &cfg); + if (IS_ERR(rdev)) { + dev_err(lp->dev, "regulator register err"); + ret = PTR_ERR(rdev); + goto err; + } + + *(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 = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX_REGISTERS, +}; + +static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) +{ + struct lp872x *lp; + struct lp872x_platform_data *pdata = cl->dev.platform_data; + int ret, size, num_regulators; + const int lp872x_num_regulators[] = { + [LP8720] = LP8720_NUM_REGULATORS, + [LP8725] = LP8725_NUM_REGULATORS, + }; + + lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL); + if (!lp) + goto err_mem; + + num_regulators = lp872x_num_regulators[id->driver_data]; + size = sizeof(struct regulator_dev *) * num_regulators; + + lp->regulators = devm_kzalloc(&cl->dev, size, GFP_KERNEL); + if (!lp->regulators) + goto err_mem; + + lp->regmap = devm_regmap_init_i2c(cl, &lp872x_regmap_config); + if (IS_ERR(lp->regmap)) { + ret = PTR_ERR(lp->regmap); + dev_err(&cl->dev, "regmap init i2c err: %d\n", ret); + goto err_dev; + } + + lp->dev = &cl->dev; + lp->pdata = pdata; + lp->chipid = id->driver_data; + lp->num_regulators = num_regulators; + i2c_set_clientdata(cl, lp); + + ret = lp872x_config(lp); + if (ret) + goto err_dev; + + return lp872x_regulator_register(lp); + +err_mem: + return -ENOMEM; +err_dev: + return ret; +} + +static int __devexit lp872x_remove(struct i2c_client *cl) +{ + struct lp872x *lp = i2c_get_clientdata(cl); + + lp872x_regulator_unregister(lp); + return 0; +} + +static const struct i2c_device_id lp872x_ids[] = { + {"lp8720", LP8720}, + {"lp8725", LP8725}, + { } +}; +MODULE_DEVICE_TABLE(i2c, lp872x_ids); + +static struct i2c_driver lp872x_driver = { + .driver = { + .name = "lp872x", + .owner = THIS_MODULE, + }, + .probe = lp872x_probe, + .remove = __devexit_p(lp872x_remove), + .id_table = lp872x_ids, +}; + +module_i2c_driver(lp872x_driver); + +MODULE_DESCRIPTION("TI/National Semiconductor LP872x PMU Regulator Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/regulator/lp872x.h b/include/linux/regulator/lp872x.h new file mode 100644 index 000000000000..132e05c46661 --- /dev/null +++ b/include/linux/regulator/lp872x.h @@ -0,0 +1,90 @@ +/* + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * 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 __LP872X_REGULATOR_H__ +#define __LP872X_REGULATOR_H__ + +#include +#include +#include + +#define LP872X_MAX_REGULATORS 9 + +enum lp872x_regulator_id { + LP8720_ID_BASE, + LP8720_ID_LDO1 = LP8720_ID_BASE, + LP8720_ID_LDO2, + LP8720_ID_LDO3, + LP8720_ID_LDO4, + LP8720_ID_LDO5, + LP8720_ID_BUCK, + + LP8725_ID_BASE, + LP8725_ID_LDO1 = LP8725_ID_BASE, + LP8725_ID_LDO2, + LP8725_ID_LDO3, + LP8725_ID_LDO4, + LP8725_ID_LDO5, + LP8725_ID_LILO1, + LP8725_ID_LILO2, + LP8725_ID_BUCK1, + LP8725_ID_BUCK2, + + LP872X_ID_MAX, +}; + +enum lp872x_dvs_state { + DVS_LOW = GPIOF_OUT_INIT_LOW, + DVS_HIGH = GPIOF_OUT_INIT_HIGH, +}; + +enum lp872x_dvs_sel { + SEL_V1, + SEL_V2, +}; + +/** + * lp872x_dvs + * @gpio : gpio pin number for dvs control + * @vsel : dvs selector for buck v1 or buck v2 register + * @init_state : initial dvs pin state + */ +struct lp872x_dvs { + int gpio; + enum lp872x_dvs_sel vsel; + enum lp872x_dvs_state init_state; +}; + +/** + * lp872x_regdata + * @id : regulator id + * @init_data : init data for each regulator + */ +struct lp872x_regulator_data { + enum lp872x_regulator_id id; + struct regulator_init_data *init_data; +}; + +/** + * lp872x_platform_data + * @general_config : the value of LP872X_GENERAL_CFG register + * @update_config : if LP872X_GENERAL_CFG register is updated, set true + * @regulator_data : platform regulator id and init data + * @dvs : dvs data for buck voltage control + */ +struct lp872x_platform_data { + u8 general_config; + bool update_config; + struct lp872x_regulator_data regulator_data[LP872X_MAX_REGULATORS]; + struct lp872x_dvs *dvs; +}; + +#endif -- cgit v1.2.3-59-g8ed1b From dfad84aeab5f71b33a12e6803a809f698bdef5a2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 19 Jun 2012 17:43:56 +0200 Subject: regulator: support multiple dummy fixed regulators Currently regulator_register_fixed() uses a constant name to register a fixed dummy regulator. This is sufficient in principle, since there is no reason to register multiple such regulators. The user can simply supply all consumers in one array and use it to initialise such a regulator. However, in some cases it can be convenient to register multiple such regulators. This is also a prerequisite for the upcoming patch, that will add a voltage parameter to this function. The original function is provided as a wrapper macro. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mark Brown --- drivers/regulator/fixed-helper.c | 14 +++++++++++--- include/linux/regulator/fixed.h | 7 +++++-- 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/regulator/fixed-helper.c b/drivers/regulator/fixed-helper.c index cacd33c9d042..3aa268dfdb1d 100644 --- a/drivers/regulator/fixed-helper.c +++ b/drivers/regulator/fixed-helper.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -13,16 +14,18 @@ static void regulator_fixed_release(struct device *dev) { struct fixed_regulator_data *data = container_of(dev, struct fixed_regulator_data, pdev.dev); + kfree(data->cfg.supply_name); kfree(data); } /** - * regulator_register_fixed - register a no-op fixed regulator + * regulator_register_fixed_name - register a no-op fixed regulator * @id: platform device id + * @name: name to be used for the regulator * @supplies: consumers for this regulator * @num_supplies: number of consumers */ -struct platform_device *regulator_register_fixed(int id, +struct platform_device *regulator_register_always_on(int id, const char *name, struct regulator_consumer_supply *supplies, int num_supplies) { struct fixed_regulator_data *data; @@ -31,7 +34,12 @@ struct platform_device *regulator_register_fixed(int id, if (!data) return NULL; - data->cfg.supply_name = "fixed-dummy"; + data->cfg.supply_name = kstrdup(name, GFP_KERNEL); + if (!data->cfg.supply_name) { + kfree(data); + return NULL; + } + data->cfg.microvolts = 0; data->cfg.gpio = -EINVAL; data->cfg.enabled_at_boot = 1; diff --git a/include/linux/regulator/fixed.h b/include/linux/regulator/fixed.h index f83f7440b488..6b9325b5e371 100644 --- a/include/linux/regulator/fixed.h +++ b/include/linux/regulator/fixed.h @@ -58,14 +58,17 @@ struct fixed_voltage_config { struct regulator_consumer_supply; #if IS_ENABLED(CONFIG_REGULATOR) -struct platform_device *regulator_register_fixed(int id, +struct platform_device *regulator_register_always_on(int id, const char *name, struct regulator_consumer_supply *supplies, int num_supplies); #else -static inline struct platform_device *regulator_register_fixed(int id, +static inline struct platform_device *regulator_register_always_on(int id, const char *name, struct regulator_consumer_supply *supplies, int num_supplies) { return NULL; } #endif +#define regulator_register_fixed(id, s, ns) regulator_register_always_on(id, \ + "fixed-dummy", s, ns) + #endif -- cgit v1.2.3-59-g8ed1b From 15719ccc274981b19ad8fe9ac20c94249de8a257 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 19 Jun 2012 17:44:39 +0200 Subject: regulator: extend the fixed dummy voltage regulator to accept voltage Trivially extend the regulator_register_always_on() helper function to be even more useful by adding a voltage parameter to it. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mark Brown --- drivers/regulator/fixed-helper.c | 5 +++-- include/linux/regulator/fixed.h | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/regulator/fixed-helper.c b/drivers/regulator/fixed-helper.c index 3aa268dfdb1d..f9d027992aae 100644 --- a/drivers/regulator/fixed-helper.c +++ b/drivers/regulator/fixed-helper.c @@ -24,9 +24,10 @@ static void regulator_fixed_release(struct device *dev) * @name: name to be used for the regulator * @supplies: consumers for this regulator * @num_supplies: number of consumers + * @uv: voltage in microvolts */ struct platform_device *regulator_register_always_on(int id, const char *name, - struct regulator_consumer_supply *supplies, int num_supplies) + struct regulator_consumer_supply *supplies, int num_supplies, int uv) { struct fixed_regulator_data *data; @@ -40,7 +41,7 @@ struct platform_device *regulator_register_always_on(int id, const char *name, return NULL; } - data->cfg.microvolts = 0; + data->cfg.microvolts = uv; data->cfg.gpio = -EINVAL; data->cfg.enabled_at_boot = 1; data->cfg.init_data = &data->init_data; diff --git a/include/linux/regulator/fixed.h b/include/linux/regulator/fixed.h index 6b9325b5e371..680f24e08af2 100644 --- a/include/linux/regulator/fixed.h +++ b/include/linux/regulator/fixed.h @@ -59,16 +59,16 @@ struct regulator_consumer_supply; #if IS_ENABLED(CONFIG_REGULATOR) struct platform_device *regulator_register_always_on(int id, const char *name, - struct regulator_consumer_supply *supplies, int num_supplies); + struct regulator_consumer_supply *supplies, int num_supplies, int uv); #else static inline struct platform_device *regulator_register_always_on(int id, const char *name, - struct regulator_consumer_supply *supplies, int num_supplies) + struct regulator_consumer_supply *supplies, int num_supplies, int uv) { return NULL; } #endif #define regulator_register_fixed(id, s, ns) regulator_register_always_on(id, \ - "fixed-dummy", s, ns) + "fixed-dummy", s, ns, 0) #endif -- cgit v1.2.3-59-g8ed1b From 3e7b706ca5151849e4ca91f7f3d8d6d8d8f7b667 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Wed, 14 Mar 2012 15:34:17 -0700 Subject: mfd: twl: remove pdata->irq_base/_end, no more users After converstion to SPARSE_IRQ, the driver doesn't use the pdata->irq_base/irq_end fields anymore. The last users have been cleanup up, and now these fields can be removed. Cc: Benoit Cousson Acked-by: Samuel Ortiz Signed-off-by: Kevin Hilman --- include/linux/i2c/twl.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 3993477103a5..555382660bc4 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -683,7 +683,6 @@ struct twl4030_audio_data { }; struct twl4030_platform_data { - unsigned irq_base, irq_end; struct twl4030_clock_init_data *clock; struct twl4030_bci_platform_data *bci; struct twl4030_gpio_platform_data *gpio; -- cgit v1.2.3-59-g8ed1b From 33eb3311f3ad4a14f2e55d36fdb0d3ec54712231 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 19 Jun 2012 16:20:24 -0300 Subject: sound: Remove unused include/linux/ac97_codec.h header This file has been superseded by include/sound/ac97_codec.h, and has currently no users. Cc: Ralf Baechle Cc: Jaroslav Kysela Cc: Clemens Ladisch Signed-off-by: Ezequiel Garcia Signed-off-by: Takashi Iwai --- include/linux/ac97_codec.h | 362 --------------------------------------------- 1 file changed, 362 deletions(-) delete mode 100644 include/linux/ac97_codec.h (limited to 'include/linux') diff --git a/include/linux/ac97_codec.h b/include/linux/ac97_codec.h deleted file mode 100644 index 0260c3e79fdd..000000000000 --- a/include/linux/ac97_codec.h +++ /dev/null @@ -1,362 +0,0 @@ -#ifndef _AC97_CODEC_H_ -#define _AC97_CODEC_H_ - -#include -#include - -/* AC97 1.0 */ -#define AC97_RESET 0x0000 // -#define AC97_MASTER_VOL_STEREO 0x0002 // Line Out -#define AC97_HEADPHONE_VOL 0x0004 // -#define AC97_MASTER_VOL_MONO 0x0006 // TAD Output -#define AC97_MASTER_TONE 0x0008 // -#define AC97_PCBEEP_VOL 0x000a // none -#define AC97_PHONE_VOL 0x000c // TAD Input (mono) -#define AC97_MIC_VOL 0x000e // MIC Input (mono) -#define AC97_LINEIN_VOL 0x0010 // Line Input (stereo) -#define AC97_CD_VOL 0x0012 // CD Input (stereo) -#define AC97_VIDEO_VOL 0x0014 // none -#define AC97_AUX_VOL 0x0016 // Aux Input (stereo) -#define AC97_PCMOUT_VOL 0x0018 // Wave Output (stereo) -#define AC97_RECORD_SELECT 0x001a // -#define AC97_RECORD_GAIN 0x001c -#define AC97_RECORD_GAIN_MIC 0x001e -#define AC97_GENERAL_PURPOSE 0x0020 -#define AC97_3D_CONTROL 0x0022 -#define AC97_MODEM_RATE 0x0024 -#define AC97_POWER_CONTROL 0x0026 - -/* AC'97 2.0 */ -#define AC97_EXTENDED_ID 0x0028 /* Extended Audio ID */ -#define AC97_EXTENDED_STATUS 0x002A /* Extended Audio Status */ -#define AC97_PCM_FRONT_DAC_RATE 0x002C /* PCM Front DAC Rate */ -#define AC97_PCM_SURR_DAC_RATE 0x002E /* PCM Surround DAC Rate */ -#define AC97_PCM_LFE_DAC_RATE 0x0030 /* PCM LFE DAC Rate */ -#define AC97_PCM_LR_ADC_RATE 0x0032 /* PCM LR ADC Rate */ -#define AC97_PCM_MIC_ADC_RATE 0x0034 /* PCM MIC ADC Rate */ -#define AC97_CENTER_LFE_MASTER 0x0036 /* Center + LFE Master Volume */ -#define AC97_SURROUND_MASTER 0x0038 /* Surround (Rear) Master Volume */ -#define AC97_RESERVED_3A 0x003A /* Reserved in AC '97 < 2.2 */ - -/* AC'97 2.2 */ -#define AC97_SPDIF_CONTROL 0x003A /* S/PDIF Control */ - -/* range 0x3c-0x58 - MODEM */ -#define AC97_EXTENDED_MODEM_ID 0x003C -#define AC97_EXTEND_MODEM_STAT 0x003E -#define AC97_LINE1_RATE 0x0040 -#define AC97_LINE2_RATE 0x0042 -#define AC97_HANDSET_RATE 0x0044 -#define AC97_LINE1_LEVEL 0x0046 -#define AC97_LINE2_LEVEL 0x0048 -#define AC97_HANDSET_LEVEL 0x004A -#define AC97_GPIO_CONFIG 0x004C -#define AC97_GPIO_POLARITY 0x004E -#define AC97_GPIO_STICKY 0x0050 -#define AC97_GPIO_WAKE_UP 0x0052 -#define AC97_GPIO_STATUS 0x0054 -#define AC97_MISC_MODEM_STAT 0x0056 -#define AC97_RESERVED_58 0x0058 - -/* registers 0x005a - 0x007a are vendor reserved */ - -#define AC97_VENDOR_ID1 0x007c -#define AC97_VENDOR_ID2 0x007e - -/* volume control bit defines */ -#define AC97_MUTE 0x8000 -#define AC97_MICBOOST 0x0040 -#define AC97_LEFTVOL 0x3f00 -#define AC97_RIGHTVOL 0x003f - -/* record mux defines */ -#define AC97_RECMUX_MIC 0x0000 -#define AC97_RECMUX_CD 0x0101 -#define AC97_RECMUX_VIDEO 0x0202 -#define AC97_RECMUX_AUX 0x0303 -#define AC97_RECMUX_LINE 0x0404 -#define AC97_RECMUX_STEREO_MIX 0x0505 -#define AC97_RECMUX_MONO_MIX 0x0606 -#define AC97_RECMUX_PHONE 0x0707 - -/* general purpose register bit defines */ -#define AC97_GP_LPBK 0x0080 /* Loopback mode */ -#define AC97_GP_MS 0x0100 /* Mic Select 0=Mic1, 1=Mic2 */ -#define AC97_GP_MIX 0x0200 /* Mono output select 0=Mix, 1=Mic */ -#define AC97_GP_RLBK 0x0400 /* Remote Loopback - Modem line codec */ -#define AC97_GP_LLBK 0x0800 /* Local Loopback - Modem Line codec */ -#define AC97_GP_LD 0x1000 /* Loudness 1=on */ -#define AC97_GP_3D 0x2000 /* 3D Enhancement 1=on */ -#define AC97_GP_ST 0x4000 /* Stereo Enhancement 1=on */ -#define AC97_GP_POP 0x8000 /* Pcm Out Path, 0=pre 3D, 1=post 3D */ - -/* extended audio status and control bit defines */ -#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */ -#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */ -#define AC97_EA_SPDIF 0x0004 /* S/PDIF Enable bit */ -#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */ -#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */ -#define AC97_EA_SDAC 0x0040 /* PCM Surround DACs are ready (Read only) */ -#define AC97_EA_LDAC 0x0080 /* PCM LFE DAC is ready (Read only) */ -#define AC97_EA_MDAC 0x0100 /* MIC ADC is ready (Read only) */ -#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */ -#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */ -#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */ -#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */ -#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */ -#define AC97_EA_SLOT_MASK 0xffcf /* Mask for slot assignment bits */ -#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */ -#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */ -#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */ -#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */ - -/* S/PDIF control bit defines */ -#define AC97_SC_PRO 0x0001 /* Professional status */ -#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */ -#define AC97_SC_COPY 0x0004 /* Copyright status */ -#define AC97_SC_PRE 0x0008 /* Preemphasis status */ -#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */ -#define AC97_SC_L 0x0800 /* Generation Level status */ -#define AC97_SC_SPSR_MASK 0xcfff /* S/PDIF Sample Rate bits */ -#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */ -#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */ -#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */ -#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */ -#define AC97_SC_V 0x8000 /* Validity status */ - -/* powerdown control and status bit defines */ - -/* status */ -#define AC97_PWR_MDM 0x0010 /* Modem section ready */ -#define AC97_PWR_REF 0x0008 /* Vref nominal */ -#define AC97_PWR_ANL 0x0004 /* Analog section ready */ -#define AC97_PWR_DAC 0x0002 /* DAC section ready */ -#define AC97_PWR_ADC 0x0001 /* ADC section ready */ - -/* control */ -#define AC97_PWR_PR0 0x0100 /* ADC and Mux powerdown */ -#define AC97_PWR_PR1 0x0200 /* DAC powerdown */ -#define AC97_PWR_PR2 0x0400 /* Output mixer powerdown (Vref on) */ -#define AC97_PWR_PR3 0x0800 /* Output mixer powerdown (Vref off) */ -#define AC97_PWR_PR4 0x1000 /* AC-link powerdown */ -#define AC97_PWR_PR5 0x2000 /* Internal Clk disable */ -#define AC97_PWR_PR6 0x4000 /* HP amp powerdown */ -#define AC97_PWR_PR7 0x8000 /* Modem off - if supported */ - -/* extended audio ID register bit defines */ -#define AC97_EXTID_VRA 0x0001 -#define AC97_EXTID_DRA 0x0002 -#define AC97_EXTID_SPDIF 0x0004 -#define AC97_EXTID_VRM 0x0008 -#define AC97_EXTID_DSA0 0x0010 -#define AC97_EXTID_DSA1 0x0020 -#define AC97_EXTID_CDAC 0x0040 -#define AC97_EXTID_SDAC 0x0080 -#define AC97_EXTID_LDAC 0x0100 -#define AC97_EXTID_AMAP 0x0200 -#define AC97_EXTID_REV0 0x0400 -#define AC97_EXTID_REV1 0x0800 -#define AC97_EXTID_ID0 0x4000 -#define AC97_EXTID_ID1 0x8000 - -/* extended status register bit defines */ -#define AC97_EXTSTAT_VRA 0x0001 -#define AC97_EXTSTAT_DRA 0x0002 -#define AC97_EXTSTAT_SPDIF 0x0004 -#define AC97_EXTSTAT_VRM 0x0008 -#define AC97_EXTSTAT_SPSA0 0x0010 -#define AC97_EXTSTAT_SPSA1 0x0020 -#define AC97_EXTSTAT_CDAC 0x0040 -#define AC97_EXTSTAT_SDAC 0x0080 -#define AC97_EXTSTAT_LDAC 0x0100 -#define AC97_EXTSTAT_MADC 0x0200 -#define AC97_EXTSTAT_SPCV 0x0400 -#define AC97_EXTSTAT_PRI 0x0800 -#define AC97_EXTSTAT_PRJ 0x1000 -#define AC97_EXTSTAT_PRK 0x2000 -#define AC97_EXTSTAT_PRL 0x4000 - -/* extended audio ID register bit defines */ -#define AC97_EXTID_VRA 0x0001 -#define AC97_EXTID_DRA 0x0002 -#define AC97_EXTID_SPDIF 0x0004 -#define AC97_EXTID_VRM 0x0008 -#define AC97_EXTID_DSA0 0x0010 -#define AC97_EXTID_DSA1 0x0020 -#define AC97_EXTID_CDAC 0x0040 -#define AC97_EXTID_SDAC 0x0080 -#define AC97_EXTID_LDAC 0x0100 -#define AC97_EXTID_AMAP 0x0200 -#define AC97_EXTID_REV0 0x0400 -#define AC97_EXTID_REV1 0x0800 -#define AC97_EXTID_ID0 0x4000 -#define AC97_EXTID_ID1 0x8000 - -/* extended status register bit defines */ -#define AC97_EXTSTAT_VRA 0x0001 -#define AC97_EXTSTAT_DRA 0x0002 -#define AC97_EXTSTAT_SPDIF 0x0004 -#define AC97_EXTSTAT_VRM 0x0008 -#define AC97_EXTSTAT_SPSA0 0x0010 -#define AC97_EXTSTAT_SPSA1 0x0020 -#define AC97_EXTSTAT_CDAC 0x0040 -#define AC97_EXTSTAT_SDAC 0x0080 -#define AC97_EXTSTAT_LDAC 0x0100 -#define AC97_EXTSTAT_MADC 0x0200 -#define AC97_EXTSTAT_SPCV 0x0400 -#define AC97_EXTSTAT_PRI 0x0800 -#define AC97_EXTSTAT_PRJ 0x1000 -#define AC97_EXTSTAT_PRK 0x2000 -#define AC97_EXTSTAT_PRL 0x4000 - -/* useful power states */ -#define AC97_PWR_D0 0x0000 /* everything on */ -#define AC97_PWR_D1 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR4 -#define AC97_PWR_D2 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 -#define AC97_PWR_D3 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 -#define AC97_PWR_ANLOFF AC97_PWR_PR2|AC97_PWR_PR3 /* analog section off */ - -/* Total number of defined registers. */ -#define AC97_REG_CNT 64 - - -/* OSS interface to the ac97s.. */ -#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|SOUND_MASK_PCM|\ - SOUND_MASK_LINE|SOUND_MASK_CD|\ - SOUND_MASK_ALTPCM|SOUND_MASK_IGAIN|\ - SOUND_MASK_LINE1|SOUND_MASK_VIDEO) - -#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \ - SOUND_MASK_BASS|SOUND_MASK_TREBLE|\ - SOUND_MASK_SPEAKER|SOUND_MASK_MIC|\ - SOUND_MASK_PHONEIN|SOUND_MASK_PHONEOUT) - -#define AC97_RECORD_MASK (SOUND_MASK_MIC|\ - SOUND_MASK_CD|SOUND_MASK_IGAIN|SOUND_MASK_VIDEO|\ - SOUND_MASK_LINE1| SOUND_MASK_LINE|\ - SOUND_MASK_PHONEIN) - -/* original check is not good enough in case FOO is greater than - * SOUND_MIXER_NRDEVICES because the supported_mixers has exactly - * SOUND_MIXER_NRDEVICES elements. - * before matching the given mixer against the bitmask in supported_mixers we - * check if mixer number exceeds maximum allowed size which is as mentioned - * above SOUND_MIXER_NRDEVICES */ -#define supported_mixer(CODEC,FOO) ((FOO >= 0) && \ - (FOO < SOUND_MIXER_NRDEVICES) && \ - (CODEC)->supported_mixers & (1< Date: Wed, 20 Jun 2012 17:29:04 +0900 Subject: sh: pfc: Split out gpio chip support. This implements a bit of rework for the PFC code, making the core itself slightly more pluggable and moving out the gpio chip handling completely. The API is preserved in such a way that platforms that depend on it for early configuration are still able to do so, while making it possible to migrate to alternate interfaces going forward. This is the first step of chainsawing necessary to support the pinctrl API, with the eventual goal being able to decouple pin function state from the gpio API while retaining gpio chip tie-in for gpio pin functions only, relying on the pinctrl/pinmux API for non-gpio function demux. Signed-off-by: Paul Mundt --- drivers/sh/Makefile | 2 +- drivers/sh/pfc-gpio.c | 309 ++++++++++++++++++++++++++++++++++++++++ drivers/sh/pfc.c | 376 ++++++++++++++----------------------------------- include/linux/sh_pfc.h | 37 ++++- 4 files changed, 447 insertions(+), 277 deletions(-) create mode 100644 drivers/sh/pfc-gpio.c (limited to 'include/linux') diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index 7139ad2f2086..be5b2934f067 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -6,5 +6,5 @@ obj-y := intc/ obj-$(CONFIG_HAVE_CLK) += clk/ obj-$(CONFIG_MAPLE) += maple/ obj-$(CONFIG_SUPERHYWAY) += superhyway/ -obj-$(CONFIG_GENERIC_GPIO) += pfc.o +obj-$(CONFIG_GENERIC_GPIO) += pfc.o pfc-gpio.o obj-y += pm_runtime.o diff --git a/drivers/sh/pfc-gpio.c b/drivers/sh/pfc-gpio.c new file mode 100644 index 000000000000..d74e5a96024b --- /dev/null +++ b/drivers/sh/pfc-gpio.c @@ -0,0 +1,309 @@ +/* + * SuperH Pin Function Controller GPIO driver. + * + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2009 - 2012 Paul Mundt + * + * 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. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +struct sh_pfc_chip { + struct sh_pfc *pfc; + struct gpio_chip gpio_chip; +}; + +static struct sh_pfc_chip *gpio_to_pfc_chip(struct gpio_chip *gc) +{ + return container_of(gc, struct sh_pfc_chip, gpio_chip); +} + +static struct sh_pfc *gpio_to_pfc(struct gpio_chip *gc) +{ + return gpio_to_pfc_chip(gc)->pfc; +} + +static int sh_gpio_request(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + struct pinmux_data_reg *dummy; + unsigned long flags; + int i, ret, pinmux_type; + + ret = -EINVAL; + + if (!pfc) + goto err_out; + + spin_lock_irqsave(&pfc->lock, flags); + + if ((pfc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) + goto err_unlock; + + /* setup pin function here if no data is associated with pin */ + + if (sh_pfc_get_data_reg(pfc, offset, &dummy, &i) != 0) + pinmux_type = PINMUX_TYPE_FUNCTION; + else + pinmux_type = PINMUX_TYPE_GPIO; + + if (pinmux_type == PINMUX_TYPE_FUNCTION) { + if (sh_pfc_config_gpio(pfc, offset, + pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err_unlock; + + if (sh_pfc_config_gpio(pfc, offset, + pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + } + + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= pinmux_type; + + ret = 0; + err_unlock: + spin_unlock_irqrestore(&pfc->lock, flags); + err_out: + return ret; +} + +static void sh_gpio_free(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned long flags; + int pinmux_type; + + if (!pfc) + return; + + spin_lock_irqsave(&pfc->lock, flags); + + pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; + sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= PINMUX_TYPE_NONE; + + spin_unlock_irqrestore(&pfc->lock, flags); +} + +static int sh_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned long flags; + int ret; + + spin_lock_irqsave(&pfc->lock, flags); + ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_INPUT); + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) + BUG(); + else + sh_pfc_write_bit(dr, bit, value); +} + +static int sh_gpio_direction_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned long flags; + int ret; + + sh_gpio_set_value(pfc, offset, value); + + spin_lock_irqsave(&pfc->lock, flags); + ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_OUTPUT); + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) + return -EINVAL; + + return sh_pfc_read_bit(dr, bit); +} + +static int sh_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + return sh_gpio_get_value(gpio_to_pfc(gc), offset); +} + +static void sh_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +{ + sh_gpio_set_value(gpio_to_pfc(gc), offset, value); +} + +static int sh_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + pinmux_enum_t enum_id; + pinmux_enum_t *enum_ids; + int i, k, pos; + + pos = 0; + enum_id = 0; + while (1) { + pos = sh_pfc_gpio_to_enum(pfc, offset, pos, &enum_id); + if (pos <= 0 || !enum_id) + break; + + for (i = 0; i < pfc->gpio_irq_size; i++) { + enum_ids = pfc->gpio_irq[i].enum_ids; + for (k = 0; enum_ids[k]; k++) { + if (enum_ids[k] == enum_id) + return pfc->gpio_irq[i].irq; + } + } + } + + return -ENOSYS; +} + +static void sh_pfc_gpio_setup(struct sh_pfc_chip *chip) +{ + struct sh_pfc *pfc = chip->pfc; + struct gpio_chip *gc = &chip->gpio_chip; + + gc->request = sh_gpio_request; + gc->free = sh_gpio_free; + gc->direction_input = sh_gpio_direction_input; + gc->get = sh_gpio_get; + gc->direction_output = sh_gpio_direction_output; + gc->set = sh_gpio_set; + gc->to_irq = sh_gpio_to_irq; + + WARN_ON(pfc->first_gpio != 0); /* needs testing */ + + gc->label = pfc->name; + gc->owner = THIS_MODULE; + gc->base = pfc->first_gpio; + gc->ngpio = (pfc->last_gpio - pfc->first_gpio) + 1; +} + +int sh_pfc_register_gpiochip(struct sh_pfc *pfc) +{ + struct sh_pfc_chip *chip; + int ret; + + chip = kzalloc(sizeof(struct sh_pfc_chip), GFP_KERNEL); + if (unlikely(!chip)) + return -ENOMEM; + + chip->pfc = pfc; + + sh_pfc_gpio_setup(chip); + + ret = gpiochip_add(&chip->gpio_chip); + if (unlikely(ret < 0)) + kfree(chip); + + pr_info("%s handling gpio %d -> %d\n", + pfc->name, pfc->first_gpio, pfc->last_gpio); + + return ret; +} +EXPORT_SYMBOL_GPL(sh_pfc_register_gpiochip); + +static int sh_pfc_gpio_match(struct gpio_chip *gc, void *data) +{ + return !!strstr(gc->label, data); +} + +static int __devinit sh_pfc_gpio_probe(struct platform_device *pdev) +{ + struct sh_pfc_chip *chip; + struct gpio_chip *gc; + + gc = gpiochip_find("_pfc", sh_pfc_gpio_match); + if (unlikely(!gc)) { + pr_err("Cant find gpio chip\n"); + return -ENODEV; + } + + chip = gpio_to_pfc_chip(gc); + platform_set_drvdata(pdev, chip); + + pr_info("attaching to GPIO chip %s\n", chip->pfc->name); + + return 0; +} + +static int __devexit sh_pfc_gpio_remove(struct platform_device *pdev) +{ + struct sh_pfc_chip *chip = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&chip->gpio_chip); + if (unlikely(ret < 0)) + return ret; + + kfree(chip); + return 0; +} + +static struct platform_driver sh_pfc_gpio_driver = { + .probe = sh_pfc_gpio_probe, + .remove = __devexit_p(sh_pfc_gpio_remove), + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, +}; + +static struct platform_device sh_pfc_gpio_device = { + .name = KBUILD_MODNAME, + .id = -1, +}; + +static int __init sh_pfc_gpio_init(void) +{ + int rc; + + rc = platform_driver_register(&sh_pfc_gpio_driver); + if (likely(!rc)) { + rc = platform_device_register(&sh_pfc_gpio_device); + if (unlikely(rc)) + platform_driver_unregister(&sh_pfc_gpio_driver); + } + + return rc; +} + +static void __exit sh_pfc_gpio_exit(void) +{ + platform_device_unregister(&sh_pfc_gpio_device); + platform_driver_unregister(&sh_pfc_gpio_driver); +} + +module_init(sh_pfc_gpio_init); +module_exit(sh_pfc_gpio_exit); + +MODULE_AUTHOR("Magnus Damm, Paul Mundt"); +MODULE_DESCRIPTION("GPIO driver for SuperH pin function controller"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:pfc-gpio"); diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c index 522c6c46d1be..8a9ae09603d6 100644 --- a/drivers/sh/pfc.c +++ b/drivers/sh/pfc.c @@ -1,7 +1,8 @@ /* - * Pinmuxed GPIO support for SuperH. + * SuperH Pin Function Controller support. * * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2009 - 2012 Paul Mundt * * 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 @@ -11,70 +12,74 @@ #include #include -#include +#include #include -#include #include #include -#include #include -#include #include #include -static void pfc_iounmap(struct pinmux_info *pip) +static struct sh_pfc *sh_pfc __read_mostly; + +static inline bool sh_pfc_initialized(void) +{ + return !!sh_pfc; +} + +static void pfc_iounmap(struct sh_pfc *pfc) { int k; - for (k = 0; k < pip->num_resources; k++) - if (pip->window[k].virt) - iounmap(pip->window[k].virt); + for (k = 0; k < pfc->num_resources; k++) + if (pfc->window[k].virt) + iounmap(pfc->window[k].virt); - kfree(pip->window); - pip->window = NULL; + kfree(pfc->window); + pfc->window = NULL; } -static int pfc_ioremap(struct pinmux_info *pip) +static int pfc_ioremap(struct sh_pfc *pfc) { struct resource *res; int k; - if (!pip->num_resources) + if (!pfc->num_resources) return 0; - pip->window = kzalloc(pip->num_resources * sizeof(*pip->window), + pfc->window = kzalloc(pfc->num_resources * sizeof(*pfc->window), GFP_NOWAIT); - if (!pip->window) + if (!pfc->window) goto err1; - for (k = 0; k < pip->num_resources; k++) { - res = pip->resource + k; + for (k = 0; k < pfc->num_resources; k++) { + res = pfc->resource + k; WARN_ON(resource_type(res) != IORESOURCE_MEM); - pip->window[k].phys = res->start; - pip->window[k].size = resource_size(res); - pip->window[k].virt = ioremap_nocache(res->start, + pfc->window[k].phys = res->start; + pfc->window[k].size = resource_size(res); + pfc->window[k].virt = ioremap_nocache(res->start, resource_size(res)); - if (!pip->window[k].virt) + if (!pfc->window[k].virt) goto err2; } return 0; err2: - pfc_iounmap(pip); + pfc_iounmap(pfc); err1: return -1; } -static void __iomem *pfc_phys_to_virt(struct pinmux_info *pip, +static void __iomem *pfc_phys_to_virt(struct sh_pfc *pfc, unsigned long address) { struct pfc_window *window; int k; /* scan through physical windows and convert address */ - for (k = 0; k < pip->num_resources; k++) { - window = pip->window + k; + for (k = 0; k < pfc->num_resources; k++) { + window = pfc->window + k; if (address < window->phys) continue; @@ -135,8 +140,7 @@ static void gpio_write_raw_reg(void __iomem *mapped_reg, BUG(); } -static int gpio_read_bit(struct pinmux_data_reg *dr, - unsigned long in_pos) +int sh_pfc_read_bit(struct pinmux_data_reg *dr, unsigned long in_pos) { unsigned long pos; @@ -147,9 +151,10 @@ static int gpio_read_bit(struct pinmux_data_reg *dr, return (gpio_read_raw_reg(dr->mapped_reg, dr->reg_width) >> pos) & 1; } +EXPORT_SYMBOL_GPL(sh_pfc_read_bit); -static void gpio_write_bit(struct pinmux_data_reg *dr, - unsigned long in_pos, unsigned long value) +void sh_pfc_write_bit(struct pinmux_data_reg *dr, unsigned long in_pos, + unsigned long value) { unsigned long pos; @@ -166,8 +171,9 @@ static void gpio_write_bit(struct pinmux_data_reg *dr, gpio_write_raw_reg(dr->mapped_reg, dr->reg_width, dr->reg_shadow); } +EXPORT_SYMBOL_GPL(sh_pfc_write_bit); -static void config_reg_helper(struct pinmux_info *gpioc, +static void config_reg_helper(struct sh_pfc *pfc, struct pinmux_cfg_reg *crp, unsigned long in_pos, void __iomem **mapped_regp, @@ -176,7 +182,7 @@ static void config_reg_helper(struct pinmux_info *gpioc, { int k; - *mapped_regp = pfc_phys_to_virt(gpioc, crp->reg); + *mapped_regp = pfc_phys_to_virt(pfc, crp->reg); if (crp->field_width) { *maskp = (1 << crp->field_width) - 1; @@ -189,14 +195,14 @@ static void config_reg_helper(struct pinmux_info *gpioc, } } -static int read_config_reg(struct pinmux_info *gpioc, +static int read_config_reg(struct sh_pfc *pfc, struct pinmux_cfg_reg *crp, unsigned long field) { void __iomem *mapped_reg; unsigned long mask, pos; - config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos); + config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); pr_debug("read_reg: addr = %lx, field = %ld, " "r_width = %ld, f_width = %ld\n", @@ -205,14 +211,14 @@ static int read_config_reg(struct pinmux_info *gpioc, return (gpio_read_raw_reg(mapped_reg, crp->reg_width) >> pos) & mask; } -static void write_config_reg(struct pinmux_info *gpioc, +static void write_config_reg(struct sh_pfc *pfc, struct pinmux_cfg_reg *crp, unsigned long field, unsigned long value) { void __iomem *mapped_reg; unsigned long mask, pos, data; - config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos); + config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); pr_debug("write_reg addr = %lx, value = %ld, field = %ld, " "r_width = %ld, f_width = %ld\n", @@ -225,30 +231,30 @@ static void write_config_reg(struct pinmux_info *gpioc, data &= mask; data |= value; - if (gpioc->unlock_reg) - gpio_write_raw_reg(pfc_phys_to_virt(gpioc, gpioc->unlock_reg), + if (pfc->unlock_reg) + gpio_write_raw_reg(pfc_phys_to_virt(pfc, pfc->unlock_reg), 32, ~data); gpio_write_raw_reg(mapped_reg, crp->reg_width, data); } -static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) +static int setup_data_reg(struct sh_pfc *pfc, unsigned gpio) { - struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; + struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; struct pinmux_data_reg *data_reg; int k, n; - if (!enum_in_range(gpiop->enum_id, &gpioc->data)) + if (!enum_in_range(gpiop->enum_id, &pfc->data)) return -1; k = 0; while (1) { - data_reg = gpioc->data_regs + k; + data_reg = pfc->data_regs + k; if (!data_reg->reg_width) break; - data_reg->mapped_reg = pfc_phys_to_virt(gpioc, data_reg->reg); + data_reg->mapped_reg = pfc_phys_to_virt(pfc, data_reg->reg); for (n = 0; n < data_reg->reg_width; n++) { if (data_reg->enum_ids[n] == gpiop->enum_id) { @@ -267,17 +273,17 @@ static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) return -1; } -static void setup_data_regs(struct pinmux_info *gpioc) +static void setup_data_regs(struct sh_pfc *pfc) { struct pinmux_data_reg *drp; int k; - for (k = gpioc->first_gpio; k <= gpioc->last_gpio; k++) - setup_data_reg(gpioc, k); + for (k = pfc->first_gpio; k <= pfc->last_gpio; k++) + setup_data_reg(pfc, k); k = 0; while (1) { - drp = gpioc->data_regs + k; + drp = pfc->data_regs + k; if (!drp->reg_width) break; @@ -288,23 +294,24 @@ static void setup_data_regs(struct pinmux_info *gpioc) } } -static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio, +int sh_pfc_get_data_reg(struct sh_pfc *pfc, unsigned gpio, struct pinmux_data_reg **drp, int *bitp) { - struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; + struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; int k, n; - if (!enum_in_range(gpiop->enum_id, &gpioc->data)) + if (!enum_in_range(gpiop->enum_id, &pfc->data)) return -1; k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT; n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT; - *drp = gpioc->data_regs + k; + *drp = pfc->data_regs + k; *bitp = n; return 0; } +EXPORT_SYMBOL_GPL(sh_pfc_get_data_reg); -static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, +static int get_config_reg(struct sh_pfc *pfc, pinmux_enum_t enum_id, struct pinmux_cfg_reg **crp, int *fieldp, int *valuep, unsigned long **cntp) @@ -315,7 +322,7 @@ static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, k = 0; while (1) { - config_reg = gpioc->cfg_regs + k; + config_reg = pfc->cfg_regs + k; r_width = config_reg->reg_width; f_width = config_reg->field_width; @@ -350,15 +357,15 @@ static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, return -1; } -static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, - int pos, pinmux_enum_t *enum_idp) +int sh_pfc_gpio_to_enum(struct sh_pfc *pfc, unsigned gpio, int pos, + pinmux_enum_t *enum_idp) { - pinmux_enum_t enum_id = gpioc->gpios[gpio].enum_id; - pinmux_enum_t *data = gpioc->gpio_data; + pinmux_enum_t enum_id = pfc->gpios[gpio].enum_id; + pinmux_enum_t *data = pfc->gpio_data; int k; - if (!enum_in_range(enum_id, &gpioc->data)) { - if (!enum_in_range(enum_id, &gpioc->mark)) { + if (!enum_in_range(enum_id, &pfc->data)) { + if (!enum_in_range(enum_id, &pfc->mark)) { pr_err("non data/mark enum_id for gpio %d\n", gpio); return -1; } @@ -369,7 +376,7 @@ static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, return pos + 1; } - for (k = 0; k < gpioc->gpio_data_size; k++) { + for (k = 0; k < pfc->gpio_data_size; k++) { if (data[k] == enum_id) { *enum_idp = data[k + 1]; return k + 1; @@ -379,11 +386,10 @@ static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio); return -1; } +EXPORT_SYMBOL_GPL(sh_pfc_gpio_to_enum); -enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE }; - -static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, - int pinmux_type, int cfg_mode) +int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type, + int cfg_mode) { struct pinmux_cfg_reg *cr = NULL; pinmux_enum_t enum_id; @@ -398,19 +404,19 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, break; case PINMUX_TYPE_OUTPUT: - range = &gpioc->output; + range = &pfc->output; break; case PINMUX_TYPE_INPUT: - range = &gpioc->input; + range = &pfc->input; break; case PINMUX_TYPE_INPUT_PULLUP: - range = &gpioc->input_pu; + range = &pfc->input_pu; break; case PINMUX_TYPE_INPUT_PULLDOWN: - range = &gpioc->input_pd; + range = &pfc->input_pd; break; default: @@ -422,7 +428,7 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, field = 0; value = 0; while (1) { - pos = get_gpio_enum_id(gpioc, gpio, pos, &enum_id); + pos = sh_pfc_gpio_to_enum(pfc, gpio, pos, &enum_id); if (pos <= 0) goto out_err; @@ -430,7 +436,7 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, break; /* first check if this is a function enum */ - in_range = enum_in_range(enum_id, &gpioc->function); + in_range = enum_in_range(enum_id, &pfc->function); if (!in_range) { /* not a function enum */ if (range) { @@ -467,19 +473,19 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, if (!in_range) continue; - if (get_config_reg(gpioc, enum_id, &cr, + if (get_config_reg(pfc, enum_id, &cr, &field, &value, &cntp) != 0) goto out_err; switch (cfg_mode) { case GPIO_CFG_DRYRUN: if (!*cntp || - (read_config_reg(gpioc, cr, field) != value)) + (read_config_reg(pfc, cr, field) != value)) continue; break; case GPIO_CFG_REQ: - write_config_reg(gpioc, cr, field, value); + write_config_reg(pfc, cr, field, value); *cntp = *cntp + 1; break; @@ -493,89 +499,18 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, out_err: return -1; } +EXPORT_SYMBOL_GPL(sh_pfc_config_gpio); -static DEFINE_SPINLOCK(gpio_lock); - -static struct pinmux_info *chip_to_pinmux(struct gpio_chip *chip) -{ - return container_of(chip, struct pinmux_info, chip); -} - -static int sh_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - struct pinmux_info *gpioc = chip_to_pinmux(chip); - struct pinmux_data_reg *dummy; - unsigned long flags; - int i, ret, pinmux_type; - - ret = -EINVAL; - - if (!gpioc) - goto err_out; - - spin_lock_irqsave(&gpio_lock, flags); - - if ((gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) - goto err_unlock; - - /* setup pin function here if no data is associated with pin */ - - if (get_data_reg(gpioc, offset, &dummy, &i) != 0) - pinmux_type = PINMUX_TYPE_FUNCTION; - else - pinmux_type = PINMUX_TYPE_GPIO; - - if (pinmux_type == PINMUX_TYPE_FUNCTION) { - if (pinmux_config_gpio(gpioc, offset, - pinmux_type, - GPIO_CFG_DRYRUN) != 0) - goto err_unlock; - - if (pinmux_config_gpio(gpioc, offset, - pinmux_type, - GPIO_CFG_REQ) != 0) - BUG(); - } - - gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - gpioc->gpios[offset].flags |= pinmux_type; - - ret = 0; - err_unlock: - spin_unlock_irqrestore(&gpio_lock, flags); - err_out: - return ret; -} - -static void sh_gpio_free(struct gpio_chip *chip, unsigned offset) -{ - struct pinmux_info *gpioc = chip_to_pinmux(chip); - unsigned long flags; - int pinmux_type; - - if (!gpioc) - return; - - spin_lock_irqsave(&gpio_lock, flags); - - pinmux_type = gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE; - pinmux_config_gpio(gpioc, offset, pinmux_type, GPIO_CFG_FREE); - gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - gpioc->gpios[offset].flags |= PINMUX_TYPE_NONE; - - spin_unlock_irqrestore(&gpio_lock, flags); -} - -static int pinmux_direction(struct pinmux_info *gpioc, - unsigned gpio, int new_pinmux_type) +int sh_pfc_set_direction(struct sh_pfc *pfc, unsigned gpio, + int new_pinmux_type) { int pinmux_type; int ret = -EINVAL; - if (!gpioc) + if (!pfc) goto err_out; - pinmux_type = gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE; + pinmux_type = pfc->gpios[gpio].flags & PINMUX_FLAG_TYPE; switch (pinmux_type) { case PINMUX_TYPE_GPIO: @@ -584,156 +519,55 @@ static int pinmux_direction(struct pinmux_info *gpioc, case PINMUX_TYPE_INPUT: case PINMUX_TYPE_INPUT_PULLUP: case PINMUX_TYPE_INPUT_PULLDOWN: - pinmux_config_gpio(gpioc, gpio, pinmux_type, GPIO_CFG_FREE); + sh_pfc_config_gpio(pfc, gpio, pinmux_type, GPIO_CFG_FREE); break; default: goto err_out; } - if (pinmux_config_gpio(gpioc, gpio, + if (sh_pfc_config_gpio(pfc, gpio, new_pinmux_type, GPIO_CFG_DRYRUN) != 0) goto err_out; - if (pinmux_config_gpio(gpioc, gpio, + if (sh_pfc_config_gpio(pfc, gpio, new_pinmux_type, GPIO_CFG_REQ) != 0) BUG(); - gpioc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; - gpioc->gpios[gpio].flags |= new_pinmux_type; + pfc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[gpio].flags |= new_pinmux_type; ret = 0; err_out: return ret; } +EXPORT_SYMBOL_GPL(sh_pfc_set_direction); -static int sh_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +int register_sh_pfc(struct sh_pfc *pfc) { - struct pinmux_info *gpioc = chip_to_pinmux(chip); - unsigned long flags; + int (*initroutine)(struct sh_pfc *) = NULL; int ret; - spin_lock_irqsave(&gpio_lock, flags); - ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_INPUT); - spin_unlock_irqrestore(&gpio_lock, flags); - - return ret; -} - -static void sh_gpio_set_value(struct pinmux_info *gpioc, - unsigned gpio, int value) -{ - struct pinmux_data_reg *dr = NULL; - int bit = 0; - - if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) - BUG(); - else - gpio_write_bit(dr, bit, value); -} + if (sh_pfc) + return -EBUSY; -static int sh_gpio_direction_output(struct gpio_chip *chip, unsigned offset, - int value) -{ - struct pinmux_info *gpioc = chip_to_pinmux(chip); - unsigned long flags; - int ret; - - sh_gpio_set_value(gpioc, offset, value); - spin_lock_irqsave(&gpio_lock, flags); - ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_OUTPUT); - spin_unlock_irqrestore(&gpio_lock, flags); - - return ret; -} - -static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio) -{ - struct pinmux_data_reg *dr = NULL; - int bit = 0; - - if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) - return -EINVAL; - - return gpio_read_bit(dr, bit); -} - -static int sh_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - return sh_gpio_get_value(chip_to_pinmux(chip), offset); -} - -static void sh_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - sh_gpio_set_value(chip_to_pinmux(chip), offset, value); -} - -static int sh_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct pinmux_info *gpioc = chip_to_pinmux(chip); - pinmux_enum_t enum_id; - pinmux_enum_t *enum_ids; - int i, k, pos; - - pos = 0; - enum_id = 0; - while (1) { - pos = get_gpio_enum_id(gpioc, offset, pos, &enum_id); - if (pos <= 0 || !enum_id) - break; - - for (i = 0; i < gpioc->gpio_irq_size; i++) { - enum_ids = gpioc->gpio_irq[i].enum_ids; - for (k = 0; enum_ids[k]; k++) { - if (enum_ids[k] == enum_id) - return gpioc->gpio_irq[i].irq; - } - } - } - - return -ENOSYS; -} - -int register_pinmux(struct pinmux_info *pip) -{ - struct gpio_chip *chip = &pip->chip; - int ret; - - pr_info("%s handling gpio %d -> %d\n", - pip->name, pip->first_gpio, pip->last_gpio); - - ret = pfc_ioremap(pip); - if (ret < 0) + ret = pfc_ioremap(pfc); + if (unlikely(ret < 0)) return ret; - setup_data_regs(pip); - - chip->request = sh_gpio_request; - chip->free = sh_gpio_free; - chip->direction_input = sh_gpio_direction_input; - chip->get = sh_gpio_get; - chip->direction_output = sh_gpio_direction_output; - chip->set = sh_gpio_set; - chip->to_irq = sh_gpio_to_irq; - - WARN_ON(pip->first_gpio != 0); /* needs testing */ + spin_lock_init(&pfc->lock); - chip->label = pip->name; - chip->owner = THIS_MODULE; - chip->base = pip->first_gpio; - chip->ngpio = (pip->last_gpio - pip->first_gpio) + 1; + setup_data_regs(pfc); - ret = gpiochip_add(chip); - if (ret < 0) - pfc_iounmap(pip); + sh_pfc = pfc; + pr_info("%s support registered\n", pfc->name); - return ret; -} + initroutine = symbol_request(sh_pfc_register_gpiochip); + if (initroutine) { + (*initroutine)(pfc); + symbol_put_addr(initroutine); + } -int unregister_pinmux(struct pinmux_info *pip) -{ - pr_info("%s deregistering\n", pip->name); - pfc_iounmap(pip); - return gpiochip_remove(&pip->chip); + return 0; } diff --git a/include/linux/sh_pfc.h b/include/linux/sh_pfc.h index 5c15aed9c4b2..95dad340794a 100644 --- a/include/linux/sh_pfc.h +++ b/include/linux/sh_pfc.h @@ -89,7 +89,7 @@ struct pfc_window { unsigned long size; }; -struct pinmux_info { +struct sh_pfc { char *name; pinmux_enum_t reserved_id; struct pinmux_range data; @@ -112,17 +112,44 @@ struct pinmux_info { struct pinmux_irq *gpio_irq; unsigned int gpio_irq_size; + spinlock_t lock; + struct resource *resource; unsigned int num_resources; struct pfc_window *window; unsigned long unlock_reg; - - struct gpio_chip chip; }; -int register_pinmux(struct pinmux_info *pip); -int unregister_pinmux(struct pinmux_info *pip); +/* XXX compat for now */ +#define pinmux_info sh_pfc + +/* drivers/sh/pfc-gpio.c */ +int sh_pfc_register_gpiochip(struct sh_pfc *pfc); + +/* drivers/sh/pfc.c */ +int register_sh_pfc(struct sh_pfc *pfc); + +int sh_pfc_read_bit(struct pinmux_data_reg *dr, unsigned long in_pos); +void sh_pfc_write_bit(struct pinmux_data_reg *dr, unsigned long in_pos, + unsigned long value); +int sh_pfc_get_data_reg(struct sh_pfc *pfc, unsigned gpio, + struct pinmux_data_reg **drp, int *bitp); +int sh_pfc_gpio_to_enum(struct sh_pfc *pfc, unsigned gpio, int pos, + pinmux_enum_t *enum_idp); +int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type, + int cfg_mode); +int sh_pfc_set_direction(struct sh_pfc *pfc, unsigned gpio, + int new_pinmux_type); + +/* xxx */ +static inline int register_pinmux(struct pinmux_info *pip) +{ + struct sh_pfc *pfc = pip; + return register_sh_pfc(pfc); +} + +enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE }; /* helper macro for port */ #define PORT_1(fn, pfx, sfx) fn(pfx, sfx) -- cgit v1.2.3-59-g8ed1b From 3052cc2c92f11875d111d5b7b9b3ad535b3128b9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Jun 2012 20:11:40 +0200 Subject: dmaengine: Add wrapper for device_tx_status callback This patch adds a small inline wrapper for the devivce_tx_status callback of a dma device. This makes the source code of users of this function a bit more compact and a bit more legible. E.g.: -status = chan->device->device_tx_status(chan, cookie, &state) +status = dmaengine_tx_status(chan, cookie, &state) Signed-off-by: Lars-Peter Clausen Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index ccec62f8e501..9c02a4508b25 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -674,6 +674,12 @@ static inline int dmaengine_resume(struct dma_chan *chan) return dmaengine_device_control(chan, DMA_RESUME, 0); } +static inline enum dma_status dmaengine_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *state) +{ + return chan->device->device_tx_status(chan, cookie, state); +} + static inline dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc) { return desc->tx_submit(desc); -- cgit v1.2.3-59-g8ed1b From 9f65b2b60c42c1e2d885acede4443b53f141c987 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 20 Jun 2012 22:29:14 +0900 Subject: sh: pfc: Kill off unused pinmux bias flags. WANT_PULLUP/DOWN were never interfaced with anything, so just kill them off. Signed-off-by: Paul Mundt --- include/linux/sh_pfc.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sh_pfc.h b/include/linux/sh_pfc.h index 95dad340794a..c06a47313a25 100644 --- a/include/linux/sh_pfc.h +++ b/include/linux/sh_pfc.h @@ -25,8 +25,6 @@ typedef unsigned short pinmux_flag_t; #define PINMUX_TYPE_INPUT_PULLDOWN 6 #define PINMUX_FLAG_TYPE (0x7) -#define PINMUX_FLAG_WANT_PULLUP (1 << 3) -#define PINMUX_FLAG_WANT_PULLDOWN (1 << 4) #define PINMUX_FLAG_DBIT_SHIFT 5 #define PINMUX_FLAG_DBIT (0x1f << PINMUX_FLAG_DBIT_SHIFT) -- cgit v1.2.3-59-g8ed1b From c32c44cb58d212513243744878423abd207bc8a8 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Jun 2012 20:11:40 +0200 Subject: dmaengine: Add wrapper for device_tx_status callback This patch adds a small inline wrapper for the devivce_tx_status callback of a dma device. This makes the source code of users of this function a bit more compact and a bit more legible. E.g.: -status = chan->device->device_tx_status(chan, cookie, &state) +status = dmaengine_tx_status(chan, cookie, &state) Signed-off-by: Lars-Peter Clausen Acked-by Vinod Koul Signed-off-by: Mark Brown --- include/linux/dmaengine.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 56377df39124..cc0756a35ae3 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -670,6 +670,12 @@ static inline int dmaengine_resume(struct dma_chan *chan) return dmaengine_device_control(chan, DMA_RESUME, 0); } +static inline enum dma_status dmaengine_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *state) +{ + return chan->device->device_tx_status(chan, cookie, state); +} + static inline dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc) { return desc->tx_submit(desc); -- cgit v1.2.3-59-g8ed1b From 06d5631f56460917af3d9417ef63811cf0cad9ce Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 21 Jun 2012 00:03:41 +0900 Subject: sh: pfc: Verify pin type encoding size at build time. The encoding is tightly packed, and future changes (such as pinconf-generic support) can easily lead to a situation where we violate the encoding constraints and trample data bit/reg bits. This plugs in some sanity checks by way of a BUILD_BUG_ON() to blow up if we fail to fit. Signed-off-by: Paul Mundt --- drivers/sh/pfc.c | 5 +++++ include/linux/sh_pfc.h | 23 ++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c index 8a9ae09603d6..ce4579ebd602 100644 --- a/drivers/sh/pfc.c +++ b/drivers/sh/pfc.c @@ -549,6 +549,11 @@ int register_sh_pfc(struct sh_pfc *pfc) int (*initroutine)(struct sh_pfc *) = NULL; int ret; + /* + * Ensure that the type encoding fits + */ + BUILD_BUG_ON(PINMUX_FLAG_TYPE > ((1 << PINMUX_FLAG_DBIT_SHIFT) - 1)); + if (sh_pfc) return -EBUSY; diff --git a/include/linux/sh_pfc.h b/include/linux/sh_pfc.h index c06a47313a25..ed1d8234f6ae 100644 --- a/include/linux/sh_pfc.h +++ b/include/linux/sh_pfc.h @@ -16,15 +16,18 @@ typedef unsigned short pinmux_enum_t; typedef unsigned short pinmux_flag_t; -#define PINMUX_TYPE_NONE 0 -#define PINMUX_TYPE_FUNCTION 1 -#define PINMUX_TYPE_GPIO 2 -#define PINMUX_TYPE_OUTPUT 3 -#define PINMUX_TYPE_INPUT 4 -#define PINMUX_TYPE_INPUT_PULLUP 5 -#define PINMUX_TYPE_INPUT_PULLDOWN 6 +enum { + PINMUX_TYPE_NONE, -#define PINMUX_FLAG_TYPE (0x7) + PINMUX_TYPE_FUNCTION, + PINMUX_TYPE_GPIO, + PINMUX_TYPE_OUTPUT, + PINMUX_TYPE_INPUT, + PINMUX_TYPE_INPUT_PULLUP, + PINMUX_TYPE_INPUT_PULLDOWN, + + PINMUX_FLAG_TYPE, /* must be last */ +}; #define PINMUX_FLAG_DBIT_SHIFT 5 #define PINMUX_FLAG_DBIT (0x1f << PINMUX_FLAG_DBIT_SHIFT) @@ -36,7 +39,9 @@ struct pinmux_gpio { pinmux_flag_t flags; }; -#define PINMUX_GPIO(gpio, data_or_mark) [gpio] = { data_or_mark } +#define PINMUX_GPIO(gpio, data_or_mark) \ + [gpio] = { .enum_id = data_or_mark, .flags = PINMUX_TYPE_NONE } + #define PINMUX_DATA(data_or_mark, ids...) data_or_mark, ids, 0 struct pinmux_cfg_reg { -- cgit v1.2.3-59-g8ed1b From 5a05fae5ca7cd5279567747fc34d60413b504cd6 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 20 Jun 2012 20:50:31 +0200 Subject: netfilter: nfq_ct_hook needs __rcu and __read_mostly This removes some sparse warnings. Reported-by: Fengguang Wu Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 2 +- net/netfilter/core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index dca19e61b30a..38b96a54f9a5 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -404,7 +404,7 @@ struct nfq_ct_hook { void (*seq_adjust)(struct sk_buff *skb, struct nf_conn *ct, u32 ctinfo, int off); }; -extern struct nfq_ct_hook *nfq_ct_hook; +extern struct nfq_ct_hook __rcu *nfq_ct_hook; #else static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} #endif diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 7eef8453b909..4cd10ed2d6e6 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -265,7 +265,7 @@ void nf_conntrack_destroy(struct nf_conntrack *nfct) } EXPORT_SYMBOL(nf_conntrack_destroy); -struct nfq_ct_hook *nfq_ct_hook; +struct nfq_ct_hook __rcu *nfq_ct_hook __read_mostly; EXPORT_SYMBOL_GPL(nfq_ct_hook); #endif /* CONFIG_NF_CONNTRACK */ -- cgit v1.2.3-59-g8ed1b From c2ebea2097f84f0973c58b8467c1a2236bbb307a Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Wed, 20 Jun 2012 17:59:01 -0700 Subject: ieee80211: more OUI type definitions for WLAN_OUI_MICROSOFT WMM and WPS Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 6e0601189db9..318fc1f705b1 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1633,6 +1633,8 @@ enum ieee80211_sa_query_action { #define WLAN_OUI_TYPE_WFA_P2P 9 #define WLAN_OUI_MICROSOFT 0x0050f2 #define WLAN_OUI_TYPE_MICROSOFT_WPA 1 +#define WLAN_OUI_TYPE_MICROSOFT_WMM 2 +#define WLAN_OUI_TYPE_MICROSOFT_WPS 4 /* * WMM/802.11e Tspec Element -- cgit v1.2.3-59-g8ed1b From d584a61a931e6cbfef0dd811c4ae0250ec5987f4 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 20 Jun 2012 20:52:31 +0200 Subject: netfilter: nfnetlink_queue: fix compilation with CONFIG_NF_NAT=m and CONFIG_NF_CT_NETLINK=y LD init/built-in.o net/built-in.o:(.data+0x4408): undefined reference to `nf_nat_tcp_seq_adjust' make: *** [vmlinux] Error 1 This patch adds a new pointer hook (nfq_ct_nat_hook) similar to other existing in Netfilter to solve our complicated configuration dependencies. Reported-by: Valdis Kletnieks Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 6 +++++- net/ipv4/netfilter/nf_nat_core.c | 6 ++++++ net/netfilter/core.c | 3 +++ net/netfilter/nf_conntrack_netlink.c | 3 --- net/netfilter/nfnetlink_queue_ct.c | 8 ++++---- 5 files changed, 18 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 38b96a54f9a5..c613cf0d7884 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -401,10 +401,14 @@ struct nfq_ct_hook { size_t (*build_size)(const struct nf_conn *ct); int (*build)(struct sk_buff *skb, struct nf_conn *ct); int (*parse)(const struct nlattr *attr, struct nf_conn *ct); +}; +extern struct nfq_ct_hook __rcu *nfq_ct_hook; + +struct nfq_ct_nat_hook { void (*seq_adjust)(struct sk_buff *skb, struct nf_conn *ct, u32 ctinfo, int off); }; -extern struct nfq_ct_hook __rcu *nfq_ct_hook; +extern struct nfq_ct_nat_hook __rcu *nfq_ct_nat_hook; #else static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} #endif diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c index abb52adf5acd..44b082fd48ab 100644 --- a/net/ipv4/netfilter/nf_nat_core.c +++ b/net/ipv4/netfilter/nf_nat_core.c @@ -691,6 +691,10 @@ static struct nf_ct_helper_expectfn follow_master_nat = { .expectfn = nf_nat_follow_master, }; +static struct nfq_ct_nat_hook nfq_ct_nat = { + .seq_adjust = nf_nat_tcp_seq_adjust, +}; + static int __init nf_nat_init(void) { size_t i; @@ -731,6 +735,7 @@ static int __init nf_nat_init(void) nfnetlink_parse_nat_setup); BUG_ON(nf_ct_nat_offset != NULL); RCU_INIT_POINTER(nf_ct_nat_offset, nf_nat_get_offset); + RCU_INIT_POINTER(nfq_ct_nat_hook, &nfq_ct_nat); return 0; cleanup_extend: @@ -747,6 +752,7 @@ static void __exit nf_nat_cleanup(void) RCU_INIT_POINTER(nf_nat_seq_adjust_hook, NULL); RCU_INIT_POINTER(nfnetlink_parse_nat_setup_hook, NULL); RCU_INIT_POINTER(nf_ct_nat_offset, NULL); + RCU_INIT_POINTER(nfq_ct_nat_hook, NULL); synchronize_net(); } diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 4cd10ed2d6e6..0bc6b60db4df 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -268,6 +268,9 @@ EXPORT_SYMBOL(nf_conntrack_destroy); struct nfq_ct_hook __rcu *nfq_ct_hook __read_mostly; EXPORT_SYMBOL_GPL(nfq_ct_hook); +struct nfq_ct_nat_hook __rcu *nfq_ct_nat_hook __read_mostly; +EXPORT_SYMBOL_GPL(nfq_ct_nat_hook); + #endif /* CONFIG_NF_CONNTRACK */ #ifdef CONFIG_PROC_FS diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 31d1d8f3a6ce..8bb47339b770 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1757,9 +1757,6 @@ static struct nfq_ct_hook ctnetlink_nfqueue_hook = { .build_size = ctnetlink_nfqueue_build_size, .build = ctnetlink_nfqueue_build, .parse = ctnetlink_nfqueue_parse, -#ifdef CONFIG_NF_NAT_NEEDED - .seq_adjust = nf_nat_tcp_seq_adjust, -#endif }; #endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */ diff --git a/net/netfilter/nfnetlink_queue_ct.c b/net/netfilter/nfnetlink_queue_ct.c index 68ef550066f5..01247b730e66 100644 --- a/net/netfilter/nfnetlink_queue_ct.c +++ b/net/netfilter/nfnetlink_queue_ct.c @@ -86,12 +86,12 @@ nla_put_failure: void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, int diff) { - struct nfq_ct_hook *nfq_ct; + struct nfq_ct_nat_hook *nfq_nat_ct; - nfq_ct = rcu_dereference(nfq_ct_hook); - if (nfq_ct == NULL) + nfq_nat_ct = rcu_dereference(nfq_ct_nat_hook); + if (nfq_nat_ct == NULL) return; if ((ct->status & IPS_NAT_MASK) && diff) - nfq_ct->seq_adjust(skb, ct, ctinfo, diff); + nfq_nat_ct->seq_adjust(skb, ct, ctinfo, diff); } -- cgit v1.2.3-59-g8ed1b From f4b57a3b4352f72e461e362cb25917e28bdba80f Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 22 Jun 2012 14:55:16 +0800 Subject: PCI/ACPI: provide MMCONFIG address for PCI host bridges This patch provide MMCONFIG address for PCI host bridges, which will be used to support host bridge hotplug. It gets MMCONFIG address by evaluating _CBA method if available. Reviewed-by: Yinghai Lu Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas --- drivers/acpi/pci_root.c | 2 ++ drivers/pci/pci-acpi.c | 14 ++++++++++++++ include/acpi/acnames.h | 1 + include/acpi/acpi_bus.h | 1 + include/linux/pci-acpi.h | 1 + 5 files changed, 19 insertions(+) (limited to 'include/linux') diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 7aff6312ce7c..ec54014c321c 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -505,6 +505,8 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS); device->driver_data = root; + root->mcfg_addr = acpi_pci_root_get_mcfg_addr(device->handle); + /* * All supported architectures that use ACPI have support for * PCI domains, so we indicate this in _OSC support capabilities. diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 61e2fefeedab..87f4c504eafb 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -162,6 +162,20 @@ acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) return remove_pm_notifier(dev, pci_acpi_wake_dev); } +phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle) +{ + acpi_status status = AE_NOT_EXIST; + unsigned long long mcfg_addr; + + if (handle) + status = acpi_evaluate_integer(handle, METHOD_NAME__CBA, + NULL, &mcfg_addr); + if (ACPI_FAILURE(status)) + return 0; + + return (phys_addr_t)mcfg_addr; +} + /* * _SxD returns the D-state with the highest power * (lowest D-state number) supported in the S-state "x". diff --git a/include/acpi/acnames.h b/include/acpi/acnames.h index 38f508816e4a..b177f97f53b6 100644 --- a/include/acpi/acnames.h +++ b/include/acpi/acnames.h @@ -62,6 +62,7 @@ #define METHOD_NAME__AEI "_AEI" #define METHOD_NAME__PRW "_PRW" #define METHOD_NAME__SRS "_SRS" +#define METHOD_NAME__CBA "_CBA" /* Method names - these methods must appear at the namespace root */ diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 9e6e1c6eb60a..457974073994 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -401,6 +401,7 @@ struct acpi_pci_root { u32 osc_support_set; /* _OSC state of support bits */ u32 osc_control_set; /* _OSC state of control bits */ + phys_addr_t mcfg_addr; }; /* helper */ diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 44623500f419..248fba2af98a 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -17,6 +17,7 @@ extern acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev); extern acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev, struct pci_dev *pci_dev); extern acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev); +extern phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle); static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) { -- cgit v1.2.3-59-g8ed1b From 397038519f2c2ac68c125c0cd766e91041c52b30 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 22 Jun 2012 14:55:21 +0800 Subject: ACPI: mark acpi_sfi_table_parse() as __init Mark function acpi_sfi_table_parse() as __init to avoid warning messages: WARNING: vmlinux.o(.text+0x4cd2d2): Section mismatch in reference from the function acpi_sfi_table_parse.clone.0() to the function Function acpi_sfi_table_parse() calls acpi_table_parse() and pci_parse_mcfg(), which are both marked as __init. Currently acpi_sfi_table_parse() is only used by MMCONFIG to scan MCFG table at boot time only, so it's safe to mark acpi_sfi_table_parse() as __init. Reviewed-by: Yinghai Lu Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas --- include/linux/sfi_acpi.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sfi_acpi.h b/include/linux/sfi_acpi.h index c4a5a8cd4469..631af63af42d 100644 --- a/include/linux/sfi_acpi.h +++ b/include/linux/sfi_acpi.h @@ -66,7 +66,7 @@ extern int sfi_acpi_table_parse(char *signature, char *oem_id, char *oem_table_id, int (*handler)(struct acpi_table_header *)); -static inline int acpi_sfi_table_parse(char *signature, +static inline int __init acpi_sfi_table_parse(char *signature, int (*handler)(struct acpi_table_header *)) { if (!acpi_table_parse(signature, handler)) @@ -83,7 +83,7 @@ static inline int sfi_acpi_table_parse(char *signature, char *oem_id, return -1; } -static inline int acpi_sfi_table_parse(char *signature, +static inline int __init acpi_sfi_table_parse(char *signature, int (*handler)(struct acpi_table_header *)) { return acpi_table_parse(signature, handler); -- cgit v1.2.3-59-g8ed1b From 6648bd7e0e62c0c8c03b15e00c9e7015e232feff Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 21 Jun 2012 13:58:31 +0000 Subject: ipv4: Add sysctl knob to control early socket demux This change is meant to add a control for disabling early socket demux. The main motivation behind this patch is to provide an option to disable the feature as it adds an additional cost to routing that reduces overall throughput by up to 5%. For example one of my systems went from 12.1Mpps to 11.6 after the early socket demux was added. It looks like the reason for the regression is that we are now having to perform two lookups, first the one for an established socket, and then the one for the routing table. By adding this patch and toggling the value for ip_early_demux to 0 I am able to get back to the 12.1Mpps I was previously seeing. [ Move local variables in ip_rcv_finish() down into the basic block in which they are actually used. -DaveM ] Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- include/linux/sysctl.h | 1 + include/net/ip.h | 3 +++ kernel/sysctl_binary.c | 2 ++ net/ipv4/ip_input.c | 22 +++++++++++++--------- net/ipv4/sysctl_net_ipv4.c | 7 +++++++ 5 files changed, 26 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index c34b4c82b0dc..20825e5f433f 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -425,6 +425,7 @@ enum NET_TCP_ALLOWED_CONG_CONTROL=123, NET_TCP_MAX_SSTHRESH=124, NET_TCP_FRTO_RESPONSE=125, + NET_IPV4_EARLY_DEMUX=126, }; enum { diff --git a/include/net/ip.h b/include/net/ip.h index 83e0619f59d0..50841bd6f10e 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -210,6 +210,9 @@ extern int inet_peer_threshold; extern int inet_peer_minttl; extern int inet_peer_maxttl; +/* From ip_input.c */ +extern int sysctl_ip_early_demux; + /* From ip_output.c */ extern int sysctl_ip_dynaddr; diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c index a650694883a1..6a3cf8253aae 100644 --- a/kernel/sysctl_binary.c +++ b/kernel/sysctl_binary.c @@ -415,6 +415,8 @@ static const struct bin_table bin_net_ipv4_table[] = { { CTL_INT, NET_IPV4_IPFRAG_SECRET_INTERVAL, "ipfrag_secret_interval" }, /* NET_IPV4_IPFRAG_MAX_DIST "ipfrag_max_dist" no longer used */ + { CTL_INT, NET_IPV4_EARLY_DEMUX, "ip_early_demux" }, + { CTL_INT, 2088 /* NET_IPQ_QMAX */, "ip_queue_maxlen" }, /* NET_TCP_DEFAULT_WIN_SCALE unused */ diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 93b092c9a394..bca25179cdb9 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -313,6 +313,8 @@ drop: return true; } +int sysctl_ip_early_demux __read_mostly = 1; + static int ip_rcv_finish(struct sk_buff *skb) { const struct iphdr *iph = ip_hdr(skb); @@ -323,16 +325,18 @@ static int ip_rcv_finish(struct sk_buff *skb) * how the packet travels inside Linux networking. */ if (skb_dst(skb) == NULL) { - const struct net_protocol *ipprot; - int protocol = iph->protocol; - int err; + int err = -ENOENT; - rcu_read_lock(); - ipprot = rcu_dereference(inet_protos[protocol]); - err = -ENOENT; - if (ipprot && ipprot->early_demux) - err = ipprot->early_demux(skb); - rcu_read_unlock(); + if (sysctl_ip_early_demux) { + const struct net_protocol *ipprot; + int protocol = iph->protocol; + + rcu_read_lock(); + ipprot = rcu_dereference(inet_protos[protocol]); + if (ipprot && ipprot->early_demux) + err = ipprot->early_demux(skb); + rcu_read_unlock(); + } if (err) { err = ip_route_input_noref(skb, iph->daddr, iph->saddr, diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index ef32956ed655..12aa0c5867c4 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -300,6 +300,13 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "ip_early_demux", + .data = &sysctl_ip_early_demux, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { .procname = "ip_dynaddr", .data = &sysctl_ip_dynaddr, -- cgit v1.2.3-59-g8ed1b From dfbce08c19cba2ba4faaf8c0dd6d7678f46c78dd Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 22 Jun 2012 23:02:22 -0700 Subject: ipv4: Don't add deprecated new binary sysctl value. Reported-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/sysctl.h | 1 - kernel/sysctl_binary.c | 2 -- 2 files changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 20825e5f433f..c34b4c82b0dc 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -425,7 +425,6 @@ enum NET_TCP_ALLOWED_CONG_CONTROL=123, NET_TCP_MAX_SSTHRESH=124, NET_TCP_FRTO_RESPONSE=125, - NET_IPV4_EARLY_DEMUX=126, }; enum { diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c index 6a3cf8253aae..a650694883a1 100644 --- a/kernel/sysctl_binary.c +++ b/kernel/sysctl_binary.c @@ -415,8 +415,6 @@ static const struct bin_table bin_net_ipv4_table[] = { { CTL_INT, NET_IPV4_IPFRAG_SECRET_INTERVAL, "ipfrag_secret_interval" }, /* NET_IPV4_IPFRAG_MAX_DIST "ipfrag_max_dist" no longer used */ - { CTL_INT, NET_IPV4_EARLY_DEMUX, "ip_early_demux" }, - { CTL_INT, 2088 /* NET_IPQ_QMAX */, "ip_queue_maxlen" }, /* NET_TCP_DEFAULT_WIN_SCALE unused */ -- cgit v1.2.3-59-g8ed1b From 448bd857d48e69b33ef323739dc6d8ca20d4cda7 Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Sat, 23 Jun 2012 10:23:51 +0800 Subject: PCI/PM: add PCIe runtime D3cold support This patch adds runtime D3cold support and corresponding ACPI platform support. This patch only enables runtime D3cold support; it does not enable D3cold support during system suspend/hibernate. D3cold is the deepest power saving state for a PCIe device, where its main power is removed. While it is in D3cold, you can't access the device at all, not even its configuration space (which is still accessible in D3hot). Therefore the PCI PM registers can not be used to transition into/out of the D3cold state; that must be done by platform logic such as ACPI _PR3. To support wakeup from D3cold, a system may provide auxiliary power, which allows a device to request wakeup using a Beacon or the sideband WAKE# signal. WAKE# is usually connected to platform logic such as ACPI GPE. This is quite different from other power saving states, where devices request wakeup via a PME message on the PCIe link. Some devices, such as those in plug-in slots, have no direct platform logic. For example, there is usually no ACPI _PR3 for them. D3cold support for these devices can be done via the PCIe Downstream Port leading to the device. When the PCIe port is powered on/off, the device is powered on/off too. Wakeup events from the device will be notified to the corresponding PCIe port. For more information about PCIe D3cold and corresponding ACPI support, please refer to: - PCI Express Base Specification Revision 2.0 - Advanced Configuration and Power Interface Specification Revision 5.0 [bhelgaas: changelog] Reviewed-by: Rafael J. Wysocki Originally-by: Zheng Yan Signed-off-by: Huang Ying Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-acpi.c | 23 +++++++-- drivers/pci/pci-driver.c | 10 +++- drivers/pci/pci-sysfs.c | 29 +++++++++++ drivers/pci/pci.c | 114 +++++++++++++++++++++++++++++++++++++---- drivers/pci/pci.h | 1 + drivers/pci/pcie/portdrv_pci.c | 44 ++++++++++++++-- include/linux/pci.h | 16 ++++-- 7 files changed, 215 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index a9efebc586b4..e1658afef873 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -48,6 +48,12 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context) if (event != ACPI_NOTIFY_DEVICE_WAKE || !pci_dev) return; + if (pci_dev->current_state == PCI_D3cold) { + pci_wakeup_event(pci_dev); + pm_runtime_resume(&pci_dev->dev); + return; + } + if (!pci_dev->pm_cap || !pci_dev->pme_support || pci_check_pme_status(pci_dev)) { if (pci_dev->pme_poll) @@ -187,10 +193,13 @@ acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev) { - int acpi_state; + int acpi_state, d_max; - acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL, - ACPI_STATE_D3); + if (pdev->no_d3cold) + d_max = ACPI_STATE_D3_HOT; + else + d_max = ACPI_STATE_D3_COLD; + acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL, d_max); if (acpi_state < 0) return PCI_POWER_ERROR; @@ -297,7 +306,13 @@ static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable) static int acpi_pci_run_wake(struct pci_dev *dev, bool enable) { - if (dev->pme_interrupt) + /* + * Per PCI Express Base Specification Revision 2.0 section + * 5.3.3.2 Link Wakeup, platform support is needed for D3cold + * waking up to power on the main link even if there is PME + * support for D3cold + */ + if (dev->pme_interrupt && !dev->runtime_d3cold) return 0; if (!acpi_pm_device_run_wake(&dev->dev, enable)) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index bf0cee629b60..ca2e4c79a588 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1019,10 +1019,13 @@ static int pci_pm_runtime_suspend(struct device *dev) if (!pm || !pm->runtime_suspend) return -ENOSYS; + pci_dev->no_d3cold = false; error = pm->runtime_suspend(dev); suspend_report_result(pm->runtime_suspend, error); if (error) return error; + if (!pci_dev->d3cold_allowed) + pci_dev->no_d3cold = true; pci_fixup_device(pci_fixup_suspend, pci_dev); @@ -1044,6 +1047,7 @@ static int pci_pm_runtime_suspend(struct device *dev) static int pci_pm_runtime_resume(struct device *dev) { + int rc; struct pci_dev *pci_dev = to_pci_dev(dev); const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; @@ -1054,7 +1058,11 @@ static int pci_pm_runtime_resume(struct device *dev) __pci_enable_wake(pci_dev, PCI_D0, true, false); pci_fixup_device(pci_fixup_resume, pci_dev); - return pm->runtime_resume(dev); + rc = pm->runtime_resume(dev); + + pci_dev->runtime_d3cold = false; + + return rc; } static int pci_pm_runtime_idle(struct device *dev) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 86c63fe45d11..1426db0c0607 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "pci.h" static int sysfs_initialized; /* = 0 */ @@ -378,6 +379,31 @@ dev_bus_rescan_store(struct device *dev, struct device_attribute *attr, #endif +#if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) +static ssize_t d3cold_allowed_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + unsigned long val; + + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; + + pdev->d3cold_allowed = !!val; + pm_runtime_resume(dev); + + return count; +} + +static ssize_t d3cold_allowed_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + return sprintf (buf, "%u\n", pdev->d3cold_allowed); +} +#endif + struct device_attribute pci_dev_attrs[] = { __ATTR_RO(resource), __ATTR_RO(vendor), @@ -401,6 +427,9 @@ struct device_attribute pci_dev_attrs[] = { #ifdef CONFIG_HOTPLUG __ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store), __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store), +#endif +#if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) + __ATTR(d3cold_allowed, 0644, d3cold_allowed_show, d3cold_allowed_store), #endif __ATTR_NULL, }; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 9eae64b17954..8effb9b23eec 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -622,7 +622,8 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) dev_info(&dev->dev, "Refused to change power state, " "currently in D%d\n", dev->current_state); - /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT + /* + * According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT * INTERFACE SPECIFICATION, REV. 1.2", a device transitioning * from D3hot to D0 _may_ perform an internal reset, thereby * going to "D0 Uninitialized" rather than "D0 Initialized". @@ -654,6 +655,16 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state) if (dev->pm_cap) { u16 pmcsr; + /* + * Configuration space is not accessible for device in + * D3cold, so just keep or set D3cold for safety + */ + if (dev->current_state == PCI_D3cold) + return; + if (state == PCI_D3cold) { + dev->current_state = PCI_D3cold; + return; + } pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); } else { @@ -694,8 +705,50 @@ static int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state) */ static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state) { - if (state == PCI_D0) + if (state == PCI_D0) { pci_platform_power_transition(dev, PCI_D0); + /* + * Mandatory power management transition delays, see + * PCI Express Base Specification Revision 2.0 Section + * 6.6.1: Conventional Reset. Do not delay for + * devices powered on/off by corresponding bridge, + * because have already delayed for the bridge. + */ + if (dev->runtime_d3cold) { + msleep(dev->d3cold_delay); + /* + * When powering on a bridge from D3cold, the + * whole hierarchy may be powered on into + * D0uninitialized state, resume them to give + * them a chance to suspend again + */ + pci_wakeup_bus(dev->subordinate); + } + } +} + +/** + * __pci_dev_set_current_state - Set current state of a PCI device + * @dev: Device to handle + * @data: pointer to state to be set + */ +static int __pci_dev_set_current_state(struct pci_dev *dev, void *data) +{ + pci_power_t state = *(pci_power_t *)data; + + dev->current_state = state; + return 0; +} + +/** + * __pci_bus_set_current_state - Walk given bus and set current state of devices + * @bus: Top bus of the subtree to walk. + * @state: state to be set + */ +static void __pci_bus_set_current_state(struct pci_bus *bus, pci_power_t state) +{ + if (bus) + pci_walk_bus(bus, __pci_dev_set_current_state, &state); } /** @@ -707,8 +760,15 @@ static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state) */ int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state) { - return state >= PCI_D0 ? - pci_platform_power_transition(dev, state) : -EINVAL; + int ret; + + if (state < PCI_D0) + return -EINVAL; + ret = pci_platform_power_transition(dev, state); + /* Power off the bridge may power off the whole hierarchy */ + if (!ret && state == PCI_D3cold) + __pci_bus_set_current_state(dev->subordinate, PCI_D3cold); + return ret; } EXPORT_SYMBOL_GPL(__pci_complete_power_transition); @@ -732,8 +792,8 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) int error; /* bound the state we're entering */ - if (state > PCI_D3hot) - state = PCI_D3hot; + if (state > PCI_D3cold) + state = PCI_D3cold; else if (state < PCI_D0) state = PCI_D0; else if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev)) @@ -748,10 +808,15 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) /* This device is quirked not to be put into D3, so don't put it in D3 */ - if (state == PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3)) + if (state >= PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3)) return 0; - error = pci_raw_set_power_state(dev, state); + /* + * To put device in D3cold, we put device into D3hot in native + * way, then put device into D3cold with platform ops + */ + error = pci_raw_set_power_state(dev, state > PCI_D3hot ? + PCI_D3hot : state); if (!__pci_complete_power_transition(dev, state)) error = 0; @@ -1497,6 +1562,28 @@ void pci_pme_wakeup_bus(struct pci_bus *bus) pci_walk_bus(bus, pci_pme_wakeup, (void *)true); } +/** + * pci_wakeup - Wake up a PCI device + * @dev: Device to handle. + * @ign: ignored parameter + */ +static int pci_wakeup(struct pci_dev *pci_dev, void *ign) +{ + pci_wakeup_event(pci_dev); + pm_request_resume(&pci_dev->dev); + return 0; +} + +/** + * pci_wakeup_bus - Walk given bus and wake up devices on it + * @bus: Top bus of the subtree to walk. + */ +void pci_wakeup_bus(struct pci_bus *bus) +{ + if (bus) + pci_walk_bus(bus, pci_wakeup, NULL); +} + /** * pci_pme_capable - check the capability of PCI device to generate PME# * @dev: PCI device to handle. @@ -1754,6 +1841,10 @@ int pci_prepare_to_sleep(struct pci_dev *dev) if (target_state == PCI_POWER_ERROR) return -EIO; + /* D3cold during system suspend/hibernate is not supported */ + if (target_state > PCI_D3hot) + target_state = PCI_D3hot; + pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev)); error = pci_set_power_state(dev, target_state); @@ -1791,12 +1882,16 @@ int pci_finish_runtime_suspend(struct pci_dev *dev) if (target_state == PCI_POWER_ERROR) return -EIO; + dev->runtime_d3cold = target_state == PCI_D3cold; + __pci_enable_wake(dev, target_state, true, pci_dev_run_wake(dev)); error = pci_set_power_state(dev, target_state); - if (error) + if (error) { __pci_enable_wake(dev, target_state, true, false); + dev->runtime_d3cold = false; + } return error; } @@ -1866,6 +1961,7 @@ void pci_pm_init(struct pci_dev *dev) dev->pm_cap = pm; dev->d3_delay = PCI_PM_D3_WAIT; + dev->d3cold_delay = PCI_PM_D3COLD_WAIT; dev->d1_support = false; dev->d2_support = false; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e4943479b234..5cd3dce7a245 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -70,6 +70,7 @@ extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); extern void pci_disable_enabled_device(struct pci_dev *dev); extern int pci_finish_runtime_suspend(struct pci_dev *dev); extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); +extern void pci_wakeup_bus(struct pci_bus *bus); extern void pci_pm_init(struct pci_dev *dev); extern void platform_pci_wakeup_init(struct pci_dev *dev); extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 7c576b9aa01d..3a7eefcb270a 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -101,12 +101,48 @@ static int pcie_port_resume_noirq(struct device *dev) } #ifdef CONFIG_PM_RUNTIME -static int pcie_port_runtime_pm(struct device *dev) +struct d3cold_info { + bool no_d3cold; + unsigned int d3cold_delay; +}; + +static int pci_dev_d3cold_info(struct pci_dev *pdev, void *data) +{ + struct d3cold_info *info = data; + + info->d3cold_delay = max_t(unsigned int, pdev->d3cold_delay, + info->d3cold_delay); + if (pdev->no_d3cold) + info->no_d3cold = true; + return 0; +} + +static int pcie_port_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct d3cold_info d3cold_info = { + .no_d3cold = false, + .d3cold_delay = PCI_PM_D3_WAIT, + }; + + /* + * If any subordinate device disable D3cold, we should not put + * the port into D3cold. The D3cold delay of port should be + * the max of that of all subordinate devices. + */ + pci_walk_bus(pdev->subordinate, pci_dev_d3cold_info, &d3cold_info); + pdev->no_d3cold = d3cold_info.no_d3cold; + pdev->d3cold_delay = d3cold_info.d3cold_delay; + return 0; +} + +static int pcie_port_runtime_resume(struct device *dev) { return 0; } #else -#define pcie_port_runtime_pm NULL +#define pcie_port_runtime_suspend NULL +#define pcie_port_runtime_resume NULL #endif static const struct dev_pm_ops pcie_portdrv_pm_ops = { @@ -117,8 +153,8 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { .poweroff = pcie_port_device_suspend, .restore = pcie_port_device_resume, .resume_noirq = pcie_port_resume_noirq, - .runtime_suspend = pcie_port_runtime_pm, - .runtime_resume = pcie_port_runtime_pm, + .runtime_suspend = pcie_port_runtime_suspend, + .runtime_resume = pcie_port_runtime_resume, }; #define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops) diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..002cfd3e33ca 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -132,9 +132,10 @@ static inline const char *pci_power_name(pci_power_t state) return pci_power_names[1 + (int) state]; } -#define PCI_PM_D2_DELAY 200 -#define PCI_PM_D3_WAIT 10 -#define PCI_PM_BUS_WAIT 50 +#define PCI_PM_D2_DELAY 200 +#define PCI_PM_D3_WAIT 10 +#define PCI_PM_D3COLD_WAIT 100 +#define PCI_PM_BUS_WAIT 50 /** The pci_channel state describes connectivity between the CPU and * the pci device. If some PCI bus between here and the pci device @@ -278,11 +279,18 @@ struct pci_dev { unsigned int pme_poll:1; /* Poll device's PME status bit */ unsigned int d1_support:1; /* Low power state D1 is supported */ unsigned int d2_support:1; /* Low power state D2 is supported */ - unsigned int no_d1d2:1; /* Only allow D0 and D3 */ + unsigned int no_d1d2:1; /* D1 and D2 are forbidden */ + unsigned int no_d3cold:1; /* D3cold is forbidden */ + unsigned int d3cold_allowed:1; /* D3cold is allowed by user */ unsigned int mmio_always_on:1; /* disallow turning off io/mem decoding during bar sizing */ unsigned int wakeup_prepared:1; + unsigned int runtime_d3cold:1; /* whether go through runtime + D3cold, not set for devices + powered on/off by the + corresponding bridge */ unsigned int d3_delay; /* D3->D0 transition time in ms */ + unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */ #ifdef CONFIG_PCIEASPM struct pcie_link_state *link_state; /* ASPM link state. */ -- cgit v1.2.3-59-g8ed1b From 74416e1e07660798379ce10a210bf4fd35b84f9f Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 30 May 2012 14:18:41 -0600 Subject: driver core: Add iommu_group tracking to struct device IOMMU groups allow IOMMU drivers to represent DMA visibility and isolation of devices. Multiple devices may be grouped together for the purposes of DMA. Placing a pointer on struct device enable easy access for things like streaming DMA programming and drivers like VFIO. Signed-off-by: Alex Williamson Acked-by: Greg Kroah-Hartman Acked-by: Benjamin Herrenschmidt Signed-off-by: Joerg Roedel --- include/linux/device.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/device.h b/include/linux/device.h index 161d96241b1b..d0e4d99405ae 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -36,6 +36,7 @@ struct subsys_private; struct bus_type; struct device_node; struct iommu_ops; +struct iommu_group; struct bus_attribute { struct attribute attr; @@ -687,6 +688,7 @@ struct device { const struct attribute_group **groups; /* optional groups */ void (*release)(struct device *dev); + struct iommu_group *iommu_group; }; /* Get the wakeup routines, which depend on struct device */ -- cgit v1.2.3-59-g8ed1b From d72e31c9374627068df29da8085ca18c92ae35d3 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 30 May 2012 14:18:53 -0600 Subject: iommu: IOMMU Groups IOMMU device groups are currently a rather vague associative notion with assembly required by the user or user level driver provider to do anything useful. This patch intends to grow the IOMMU group concept into something a bit more consumable. To do this, we first create an object representing the group, struct iommu_group. This structure is allocated (iommu_group_alloc) and filled (iommu_group_add_device) by the iommu driver. The iommu driver is free to add devices to the group using it's own set of policies. This allows inclusion of devices based on physical hardware or topology limitations of the platform, as well as soft requirements, such as multi-function trust levels or peer-to-peer protection of the interconnects. Each device may only belong to a single iommu group, which is linked from struct device.iommu_group. IOMMU groups are maintained using kobject reference counting, allowing for automatic removal of empty, unreferenced groups. It is the responsibility of the iommu driver to remove devices from the group (iommu_group_remove_device). IOMMU groups also include a userspace representation in sysfs under /sys/kernel/iommu_groups. When allocated, each group is given a dynamically assign ID (int). The ID is managed by the core IOMMU group code to support multiple heterogeneous iommu drivers, which could potentially collide in group naming/numbering. This also keeps group IDs to small, easily managed values. A directory is created under /sys/kernel/iommu_groups for each group. A further subdirectory named "devices" contains links to each device within the group. The iommu_group file in the device's sysfs directory, which formerly contained a group number when read, is now a link to the iommu group. Example: $ ls -l /sys/kernel/iommu_groups/26/devices/ total 0 lrwxrwxrwx. 1 root root 0 Apr 17 12:57 0000:00:1e.0 -> ../../../../devices/pci0000:00/0000:00:1e.0 lrwxrwxrwx. 1 root root 0 Apr 17 12:57 0000:06:0d.0 -> ../../../../devices/pci0000:00/0000:00:1e.0/0000:06:0d.0 lrwxrwxrwx. 1 root root 0 Apr 17 12:57 0000:06:0d.1 -> ../../../../devices/pci0000:00/0000:00:1e.0/0000:06:0d.1 $ ls -l /sys/kernel/iommu_groups/26/devices/*/iommu_group [truncating perms/owner/timestamp] /sys/kernel/iommu_groups/26/devices/0000:00:1e.0/iommu_group -> ../../../kernel/iommu_groups/26 /sys/kernel/iommu_groups/26/devices/0000:06:0d.0/iommu_group -> ../../../../kernel/iommu_groups/26 /sys/kernel/iommu_groups/26/devices/0000:06:0d.1/iommu_group -> ../../../../kernel/iommu_groups/26 Groups also include several exported functions for use by user level driver providers, for example VFIO. These include: iommu_group_get(): Acquires a reference to a group from a device iommu_group_put(): Releases reference iommu_group_for_each_dev(): Iterates over group devices using callback iommu_group_[un]register_notifier(): Allows notification of device add and remove operations relevant to the group iommu_group_id(): Return the group number This patch also extends the IOMMU API to allow attaching groups to domains. This is currently a simple wrapper for iterating through devices within a group, but it's expected that the IOMMU API may eventually make groups a more integral part of domains. Groups intentionally do not try to manage group ownership. A user level driver provider must independently acquire ownership for each device within a group before making use of the group as a whole. This may change in the future if group usage becomes more pervasive across both DMA and IOMMU ops. Groups intentionally do not provide a mechanism for driver locking or otherwise manipulating driver matching/probing of devices within the group. Such interfaces are generic to devices and beyond the scope of IOMMU groups. If implemented, user level providers have ready access via iommu_group_for_each_dev and group notifiers. iommu_device_group() is removed here as it has no users. The replacement is: group = iommu_group_get(dev); id = iommu_group_id(group); iommu_group_put(group); AMD-Vi & Intel VT-d support re-added in following patches. Signed-off-by: Alex Williamson Acked-by: Benjamin Herrenschmidt Signed-off-by: Joerg Roedel --- .../ABI/testing/sysfs-kernel-iommu_groups | 14 + drivers/iommu/amd_iommu.c | 21 - drivers/iommu/intel-iommu.c | 49 -- drivers/iommu/iommu.c | 578 +++++++++++++++++++-- include/linux/iommu.h | 104 +++- 5 files changed, 663 insertions(+), 103 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-kernel-iommu_groups (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-kernel-iommu_groups b/Documentation/ABI/testing/sysfs-kernel-iommu_groups new file mode 100644 index 000000000000..9b31556cfdda --- /dev/null +++ b/Documentation/ABI/testing/sysfs-kernel-iommu_groups @@ -0,0 +1,14 @@ +What: /sys/kernel/iommu_groups/ +Date: May 2012 +KernelVersion: v3.5 +Contact: Alex Williamson +Description: /sys/kernel/iommu_groups/ contains a number of sub- + directories, each representing an IOMMU group. The + name of the sub-directory matches the iommu_group_id() + for the group, which is an integer value. Within each + subdirectory is another directory named "devices" with + links to the sysfs devices contained in this group. + The group directory also optionally contains a "name" + file if the IOMMU driver has chosen to register a more + common name for the group. +Users: diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index a2e418cba0ff..55283d6291c8 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3227,26 +3227,6 @@ static int amd_iommu_domain_has_cap(struct iommu_domain *domain, return 0; } -static int amd_iommu_device_group(struct device *dev, unsigned int *groupid) -{ - struct iommu_dev_data *dev_data = dev->archdata.iommu; - struct pci_dev *pdev = to_pci_dev(dev); - u16 devid; - - if (!dev_data) - return -ENODEV; - - if (pdev->is_virtfn || !iommu_group_mf) - devid = dev_data->devid; - else - devid = calc_devid(pdev->bus->number, - PCI_DEVFN(PCI_SLOT(pdev->devfn), 0)); - - *groupid = amd_iommu_alias_table[devid]; - - return 0; -} - static struct iommu_ops amd_iommu_ops = { .domain_init = amd_iommu_domain_init, .domain_destroy = amd_iommu_domain_destroy, @@ -3256,7 +3236,6 @@ static struct iommu_ops amd_iommu_ops = { .unmap = amd_iommu_unmap, .iova_to_phys = amd_iommu_iova_to_phys, .domain_has_cap = amd_iommu_domain_has_cap, - .device_group = amd_iommu_device_group, .pgsize_bitmap = AMD_IOMMU_PGSIZES, }; diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index b12af2ff8c54..c62f2df25221 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -4090,54 +4090,6 @@ static int intel_iommu_domain_has_cap(struct iommu_domain *domain, return 0; } -/* - * Group numbers are arbitrary. Device with the same group number - * indicate the iommu cannot differentiate between them. To avoid - * tracking used groups we just use the seg|bus|devfn of the lowest - * level we're able to differentiate devices - */ -static int intel_iommu_device_group(struct device *dev, unsigned int *groupid) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct pci_dev *bridge; - union { - struct { - u8 devfn; - u8 bus; - u16 segment; - } pci; - u32 group; - } id; - - if (iommu_no_mapping(dev)) - return -ENODEV; - - id.pci.segment = pci_domain_nr(pdev->bus); - id.pci.bus = pdev->bus->number; - id.pci.devfn = pdev->devfn; - - if (!device_to_iommu(id.pci.segment, id.pci.bus, id.pci.devfn)) - return -ENODEV; - - bridge = pci_find_upstream_pcie_bridge(pdev); - if (bridge) { - if (pci_is_pcie(bridge)) { - id.pci.bus = bridge->subordinate->number; - id.pci.devfn = 0; - } else { - id.pci.bus = bridge->bus->number; - id.pci.devfn = bridge->devfn; - } - } - - if (!pdev->is_virtfn && iommu_group_mf) - id.pci.devfn = PCI_DEVFN(PCI_SLOT(id.pci.devfn), 0); - - *groupid = id.group; - - return 0; -} - static struct iommu_ops intel_iommu_ops = { .domain_init = intel_iommu_domain_init, .domain_destroy = intel_iommu_domain_destroy, @@ -4147,7 +4099,6 @@ static struct iommu_ops intel_iommu_ops = { .unmap = intel_iommu_unmap, .iova_to_phys = intel_iommu_iova_to_phys, .domain_has_cap = intel_iommu_domain_has_cap, - .device_group = intel_iommu_device_group, .pgsize_bitmap = INTEL_IOMMU_PGSIZES, }; diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 8b9ded88e6f5..0e928acd7dcf 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -26,60 +26,535 @@ #include #include #include +#include +#include +#include + +static struct kset *iommu_group_kset; +static struct ida iommu_group_ida; +static struct mutex iommu_group_mutex; + +struct iommu_group { + struct kobject kobj; + struct kobject *devices_kobj; + struct list_head devices; + struct mutex mutex; + struct blocking_notifier_head notifier; + void *iommu_data; + void (*iommu_data_release)(void *iommu_data); + char *name; + int id; +}; + +struct iommu_device { + struct list_head list; + struct device *dev; + char *name; +}; + +struct iommu_group_attribute { + struct attribute attr; + ssize_t (*show)(struct iommu_group *group, char *buf); + ssize_t (*store)(struct iommu_group *group, + const char *buf, size_t count); +}; + +#define IOMMU_GROUP_ATTR(_name, _mode, _show, _store) \ +struct iommu_group_attribute iommu_group_attr_##_name = \ + __ATTR(_name, _mode, _show, _store) -static ssize_t show_iommu_group(struct device *dev, - struct device_attribute *attr, char *buf) +#define to_iommu_group_attr(_attr) \ + container_of(_attr, struct iommu_group_attribute, attr) +#define to_iommu_group(_kobj) \ + container_of(_kobj, struct iommu_group, kobj) + +static ssize_t iommu_group_attr_show(struct kobject *kobj, + struct attribute *__attr, char *buf) { - unsigned int groupid; + struct iommu_group_attribute *attr = to_iommu_group_attr(__attr); + struct iommu_group *group = to_iommu_group(kobj); + ssize_t ret = -EIO; - if (iommu_device_group(dev, &groupid)) - return 0; + if (attr->show) + ret = attr->show(group, buf); + return ret; +} + +static ssize_t iommu_group_attr_store(struct kobject *kobj, + struct attribute *__attr, + const char *buf, size_t count) +{ + struct iommu_group_attribute *attr = to_iommu_group_attr(__attr); + struct iommu_group *group = to_iommu_group(kobj); + ssize_t ret = -EIO; - return sprintf(buf, "%u", groupid); + if (attr->store) + ret = attr->store(group, buf, count); + return ret; } -static DEVICE_ATTR(iommu_group, S_IRUGO, show_iommu_group, NULL); -static int add_iommu_group(struct device *dev, void *data) +static const struct sysfs_ops iommu_group_sysfs_ops = { + .show = iommu_group_attr_show, + .store = iommu_group_attr_store, +}; + +static int iommu_group_create_file(struct iommu_group *group, + struct iommu_group_attribute *attr) +{ + return sysfs_create_file(&group->kobj, &attr->attr); +} + +static void iommu_group_remove_file(struct iommu_group *group, + struct iommu_group_attribute *attr) +{ + sysfs_remove_file(&group->kobj, &attr->attr); +} + +static ssize_t iommu_group_show_name(struct iommu_group *group, char *buf) +{ + return sprintf(buf, "%s\n", group->name); +} + +static IOMMU_GROUP_ATTR(name, S_IRUGO, iommu_group_show_name, NULL); + +static void iommu_group_release(struct kobject *kobj) +{ + struct iommu_group *group = to_iommu_group(kobj); + + if (group->iommu_data_release) + group->iommu_data_release(group->iommu_data); + + mutex_lock(&iommu_group_mutex); + ida_remove(&iommu_group_ida, group->id); + mutex_unlock(&iommu_group_mutex); + + kfree(group->name); + kfree(group); +} + +static struct kobj_type iommu_group_ktype = { + .sysfs_ops = &iommu_group_sysfs_ops, + .release = iommu_group_release, +}; + +/** + * iommu_group_alloc - Allocate a new group + * @name: Optional name to associate with group, visible in sysfs + * + * This function is called by an iommu driver to allocate a new iommu + * group. The iommu group represents the minimum granularity of the iommu. + * Upon successful return, the caller holds a reference to the supplied + * group in order to hold the group until devices are added. Use + * iommu_group_put() to release this extra reference count, allowing the + * group to be automatically reclaimed once it has no devices or external + * references. + */ +struct iommu_group *iommu_group_alloc(void) { - unsigned int groupid; + struct iommu_group *group; + int ret; + + group = kzalloc(sizeof(*group), GFP_KERNEL); + if (!group) + return ERR_PTR(-ENOMEM); + + group->kobj.kset = iommu_group_kset; + mutex_init(&group->mutex); + INIT_LIST_HEAD(&group->devices); + BLOCKING_INIT_NOTIFIER_HEAD(&group->notifier); + + mutex_lock(&iommu_group_mutex); + +again: + if (unlikely(0 == ida_pre_get(&iommu_group_ida, GFP_KERNEL))) { + kfree(group); + mutex_unlock(&iommu_group_mutex); + return ERR_PTR(-ENOMEM); + } + + if (-EAGAIN == ida_get_new(&iommu_group_ida, &group->id)) + goto again; + + mutex_unlock(&iommu_group_mutex); - if (iommu_device_group(dev, &groupid) == 0) - return device_create_file(dev, &dev_attr_iommu_group); + ret = kobject_init_and_add(&group->kobj, &iommu_group_ktype, + NULL, "%d", group->id); + if (ret) { + mutex_lock(&iommu_group_mutex); + ida_remove(&iommu_group_ida, group->id); + mutex_unlock(&iommu_group_mutex); + kfree(group); + return ERR_PTR(ret); + } + + group->devices_kobj = kobject_create_and_add("devices", &group->kobj); + if (!group->devices_kobj) { + kobject_put(&group->kobj); /* triggers .release & free */ + return ERR_PTR(-ENOMEM); + } + + /* + * The devices_kobj holds a reference on the group kobject, so + * as long as that exists so will the group. We can therefore + * use the devices_kobj for reference counting. + */ + kobject_put(&group->kobj); + + return group; +} +EXPORT_SYMBOL_GPL(iommu_group_alloc); + +/** + * iommu_group_get_iommudata - retrieve iommu_data registered for a group + * @group: the group + * + * iommu drivers can store data in the group for use when doing iommu + * operations. This function provides a way to retrieve it. Caller + * should hold a group reference. + */ +void *iommu_group_get_iommudata(struct iommu_group *group) +{ + return group->iommu_data; +} +EXPORT_SYMBOL_GPL(iommu_group_get_iommudata); + +/** + * iommu_group_set_iommudata - set iommu_data for a group + * @group: the group + * @iommu_data: new data + * @release: release function for iommu_data + * + * iommu drivers can store data in the group for use when doing iommu + * operations. This function provides a way to set the data after + * the group has been allocated. Caller should hold a group reference. + */ +void iommu_group_set_iommudata(struct iommu_group *group, void *iommu_data, + void (*release)(void *iommu_data)) +{ + group->iommu_data = iommu_data; + group->iommu_data_release = release; +} +EXPORT_SYMBOL_GPL(iommu_group_set_iommudata); + +/** + * iommu_group_set_name - set name for a group + * @group: the group + * @name: name + * + * Allow iommu driver to set a name for a group. When set it will + * appear in a name attribute file under the group in sysfs. + */ +int iommu_group_set_name(struct iommu_group *group, const char *name) +{ + int ret; + + if (group->name) { + iommu_group_remove_file(group, &iommu_group_attr_name); + kfree(group->name); + group->name = NULL; + if (!name) + return 0; + } + + group->name = kstrdup(name, GFP_KERNEL); + if (!group->name) + return -ENOMEM; + + ret = iommu_group_create_file(group, &iommu_group_attr_name); + if (ret) { + kfree(group->name); + group->name = NULL; + return ret; + } return 0; } +EXPORT_SYMBOL_GPL(iommu_group_set_name); -static int remove_iommu_group(struct device *dev) +/** + * iommu_group_add_device - add a device to an iommu group + * @group: the group into which to add the device (reference should be held) + * @dev: the device + * + * This function is called by an iommu driver to add a device into a + * group. Adding a device increments the group reference count. + */ +int iommu_group_add_device(struct iommu_group *group, struct device *dev) { - unsigned int groupid; + int ret, i = 0; + struct iommu_device *device; + + device = kzalloc(sizeof(*device), GFP_KERNEL); + if (!device) + return -ENOMEM; + + device->dev = dev; - if (iommu_device_group(dev, &groupid) == 0) - device_remove_file(dev, &dev_attr_iommu_group); + ret = sysfs_create_link(&dev->kobj, &group->kobj, "iommu_group"); + if (ret) { + kfree(device); + return ret; + } + + device->name = kasprintf(GFP_KERNEL, "%s", kobject_name(&dev->kobj)); +rename: + if (!device->name) { + sysfs_remove_link(&dev->kobj, "iommu_group"); + kfree(device); + return -ENOMEM; + } + ret = sysfs_create_link_nowarn(group->devices_kobj, + &dev->kobj, device->name); + if (ret) { + kfree(device->name); + if (ret == -EEXIST && i >= 0) { + /* + * Account for the slim chance of collision + * and append an instance to the name. + */ + device->name = kasprintf(GFP_KERNEL, "%s.%d", + kobject_name(&dev->kobj), i++); + goto rename; + } + + sysfs_remove_link(&dev->kobj, "iommu_group"); + kfree(device); + return ret; + } + + kobject_get(group->devices_kobj); + + dev->iommu_group = group; + + mutex_lock(&group->mutex); + list_add_tail(&device->list, &group->devices); + mutex_unlock(&group->mutex); + + /* Notify any listeners about change to group. */ + blocking_notifier_call_chain(&group->notifier, + IOMMU_GROUP_NOTIFY_ADD_DEVICE, dev); return 0; } +EXPORT_SYMBOL_GPL(iommu_group_add_device); -static int iommu_device_notifier(struct notifier_block *nb, - unsigned long action, void *data) +/** + * iommu_group_remove_device - remove a device from it's current group + * @dev: device to be removed + * + * This function is called by an iommu driver to remove the device from + * it's current group. This decrements the iommu group reference count. + */ +void iommu_group_remove_device(struct device *dev) +{ + struct iommu_group *group = dev->iommu_group; + struct iommu_device *tmp_device, *device = NULL; + + /* Pre-notify listeners that a device is being removed. */ + blocking_notifier_call_chain(&group->notifier, + IOMMU_GROUP_NOTIFY_DEL_DEVICE, dev); + + mutex_lock(&group->mutex); + list_for_each_entry(tmp_device, &group->devices, list) { + if (tmp_device->dev == dev) { + device = tmp_device; + list_del(&device->list); + break; + } + } + mutex_unlock(&group->mutex); + + if (!device) + return; + + sysfs_remove_link(group->devices_kobj, device->name); + sysfs_remove_link(&dev->kobj, "iommu_group"); + + kfree(device->name); + kfree(device); + dev->iommu_group = NULL; + kobject_put(group->devices_kobj); +} +EXPORT_SYMBOL_GPL(iommu_group_remove_device); + +/** + * iommu_group_for_each_dev - iterate over each device in the group + * @group: the group + * @data: caller opaque data to be passed to callback function + * @fn: caller supplied callback function + * + * This function is called by group users to iterate over group devices. + * Callers should hold a reference count to the group during callback. + * The group->mutex is held across callbacks, which will block calls to + * iommu_group_add/remove_device. + */ +int iommu_group_for_each_dev(struct iommu_group *group, void *data, + int (*fn)(struct device *, void *)) +{ + struct iommu_device *device; + int ret = 0; + + mutex_lock(&group->mutex); + list_for_each_entry(device, &group->devices, list) { + ret = fn(device->dev, data); + if (ret) + break; + } + mutex_unlock(&group->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(iommu_group_for_each_dev); + +/** + * iommu_group_get - Return the group for a device and increment reference + * @dev: get the group that this device belongs to + * + * This function is called by iommu drivers and users to get the group + * for the specified device. If found, the group is returned and the group + * reference in incremented, else NULL. + */ +struct iommu_group *iommu_group_get(struct device *dev) +{ + struct iommu_group *group = dev->iommu_group; + + if (group) + kobject_get(group->devices_kobj); + + return group; +} +EXPORT_SYMBOL_GPL(iommu_group_get); + +/** + * iommu_group_put - Decrement group reference + * @group: the group to use + * + * This function is called by iommu drivers and users to release the + * iommu group. Once the reference count is zero, the group is released. + */ +void iommu_group_put(struct iommu_group *group) +{ + if (group) + kobject_put(group->devices_kobj); +} +EXPORT_SYMBOL_GPL(iommu_group_put); + +/** + * iommu_group_register_notifier - Register a notifier for group changes + * @group: the group to watch + * @nb: notifier block to signal + * + * This function allows iommu group users to track changes in a group. + * See include/linux/iommu.h for actions sent via this notifier. Caller + * should hold a reference to the group throughout notifier registration. + */ +int iommu_group_register_notifier(struct iommu_group *group, + struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&group->notifier, nb); +} +EXPORT_SYMBOL_GPL(iommu_group_register_notifier); + +/** + * iommu_group_unregister_notifier - Unregister a notifier + * @group: the group to watch + * @nb: notifier block to signal + * + * Unregister a previously registered group notifier block. + */ +int iommu_group_unregister_notifier(struct iommu_group *group, + struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&group->notifier, nb); +} +EXPORT_SYMBOL_GPL(iommu_group_unregister_notifier); + +/** + * iommu_group_id - Return ID for a group + * @group: the group to ID + * + * Return the unique ID for the group matching the sysfs group number. + */ +int iommu_group_id(struct iommu_group *group) +{ + return group->id; +} +EXPORT_SYMBOL_GPL(iommu_group_id); + +static int add_iommu_group(struct device *dev, void *data) +{ + struct iommu_ops *ops = data; + + if (!ops->add_device) + return -ENODEV; + + WARN_ON(dev->iommu_group); + + ops->add_device(dev); + + return 0; +} + +static int iommu_bus_notifier(struct notifier_block *nb, + unsigned long action, void *data) { struct device *dev = data; + struct iommu_ops *ops = dev->bus->iommu_ops; + struct iommu_group *group; + unsigned long group_action = 0; + + /* + * ADD/DEL call into iommu driver ops if provided, which may + * result in ADD/DEL notifiers to group->notifier + */ + if (action == BUS_NOTIFY_ADD_DEVICE) { + if (ops->add_device) + return ops->add_device(dev); + } else if (action == BUS_NOTIFY_DEL_DEVICE) { + if (ops->remove_device && dev->iommu_group) { + ops->remove_device(dev); + return 0; + } + } - if (action == BUS_NOTIFY_ADD_DEVICE) - return add_iommu_group(dev, NULL); - else if (action == BUS_NOTIFY_DEL_DEVICE) - return remove_iommu_group(dev); + /* + * Remaining BUS_NOTIFYs get filtered and republished to the + * group, if anyone is listening + */ + group = iommu_group_get(dev); + if (!group) + return 0; + switch (action) { + case BUS_NOTIFY_BIND_DRIVER: + group_action = IOMMU_GROUP_NOTIFY_BIND_DRIVER; + break; + case BUS_NOTIFY_BOUND_DRIVER: + group_action = IOMMU_GROUP_NOTIFY_BOUND_DRIVER; + break; + case BUS_NOTIFY_UNBIND_DRIVER: + group_action = IOMMU_GROUP_NOTIFY_UNBIND_DRIVER; + break; + case BUS_NOTIFY_UNBOUND_DRIVER: + group_action = IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER; + break; + } + + if (group_action) + blocking_notifier_call_chain(&group->notifier, + group_action, dev); + + iommu_group_put(group); return 0; } -static struct notifier_block iommu_device_nb = { - .notifier_call = iommu_device_notifier, +static struct notifier_block iommu_bus_nb = { + .notifier_call = iommu_bus_notifier, }; static void iommu_bus_init(struct bus_type *bus, struct iommu_ops *ops) { - bus_register_notifier(bus, &iommu_device_nb); - bus_for_each_dev(bus, NULL, NULL, add_iommu_group); + bus_register_notifier(bus, &iommu_bus_nb); + bus_for_each_dev(bus, NULL, ops, add_iommu_group); } /** @@ -192,6 +667,45 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev) } EXPORT_SYMBOL_GPL(iommu_detach_device); +/* + * IOMMU groups are really the natrual working unit of the IOMMU, but + * the IOMMU API works on domains and devices. Bridge that gap by + * iterating over the devices in a group. Ideally we'd have a single + * device which represents the requestor ID of the group, but we also + * allow IOMMU drivers to create policy defined minimum sets, where + * the physical hardware may be able to distiguish members, but we + * wish to group them at a higher level (ex. untrusted multi-function + * PCI devices). Thus we attach each device. + */ +static int iommu_group_do_attach_device(struct device *dev, void *data) +{ + struct iommu_domain *domain = data; + + return iommu_attach_device(domain, dev); +} + +int iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group) +{ + return iommu_group_for_each_dev(group, domain, + iommu_group_do_attach_device); +} +EXPORT_SYMBOL_GPL(iommu_attach_group); + +static int iommu_group_do_detach_device(struct device *dev, void *data) +{ + struct iommu_domain *domain = data; + + iommu_detach_device(domain, dev); + + return 0; +} + +void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group) +{ + iommu_group_for_each_dev(group, domain, iommu_group_do_detach_device); +} +EXPORT_SYMBOL_GPL(iommu_detach_group); + phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, unsigned long iova) { @@ -336,11 +850,15 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) } EXPORT_SYMBOL_GPL(iommu_unmap); -int iommu_device_group(struct device *dev, unsigned int *groupid) +static int __init iommu_init(void) { - if (iommu_present(dev->bus) && dev->bus->iommu_ops->device_group) - return dev->bus->iommu_ops->device_group(dev, groupid); + iommu_group_kset = kset_create_and_add("iommu_groups", + NULL, kernel_kobj); + ida_init(&iommu_group_ida); + mutex_init(&iommu_group_mutex); - return -ENODEV; + BUG_ON(!iommu_group_kset); + + return 0; } -EXPORT_SYMBOL_GPL(iommu_device_group); +subsys_initcall(iommu_init); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 450293f6d68b..a71df92be992 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -26,6 +26,7 @@ #define IOMMU_CACHE (4) /* DMA cache coherency */ struct iommu_ops; +struct iommu_group; struct bus_type; struct device; struct iommu_domain; @@ -60,6 +61,8 @@ struct iommu_domain { * @iova_to_phys: translate iova to physical address * @domain_has_cap: domain capabilities query * @commit: commit iommu domain + * @add_device: add device to iommu grouping + * @remove_device: remove device from iommu grouping * @pgsize_bitmap: bitmap of supported page sizes */ struct iommu_ops { @@ -75,10 +78,18 @@ struct iommu_ops { unsigned long iova); int (*domain_has_cap)(struct iommu_domain *domain, unsigned long cap); - int (*device_group)(struct device *dev, unsigned int *groupid); + int (*add_device)(struct device *dev); + void (*remove_device)(struct device *dev); unsigned long pgsize_bitmap; }; +#define IOMMU_GROUP_NOTIFY_ADD_DEVICE 1 /* Device added */ +#define IOMMU_GROUP_NOTIFY_DEL_DEVICE 2 /* Pre Device removed */ +#define IOMMU_GROUP_NOTIFY_BIND_DRIVER 3 /* Pre Driver bind */ +#define IOMMU_GROUP_NOTIFY_BOUND_DRIVER 4 /* Post Driver bind */ +#define IOMMU_GROUP_NOTIFY_UNBIND_DRIVER 5 /* Pre Driver unbind */ +#define IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER 6 /* Post Driver unbind */ + extern int bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops); extern bool iommu_present(struct bus_type *bus); extern struct iommu_domain *iommu_domain_alloc(struct bus_type *bus); @@ -97,7 +108,29 @@ extern int iommu_domain_has_cap(struct iommu_domain *domain, unsigned long cap); extern void iommu_set_fault_handler(struct iommu_domain *domain, iommu_fault_handler_t handler, void *token); -extern int iommu_device_group(struct device *dev, unsigned int *groupid); + +extern int iommu_attach_group(struct iommu_domain *domain, + struct iommu_group *group); +extern void iommu_detach_group(struct iommu_domain *domain, + struct iommu_group *group); +extern struct iommu_group *iommu_group_alloc(void); +extern void *iommu_group_get_iommudata(struct iommu_group *group); +extern void iommu_group_set_iommudata(struct iommu_group *group, + void *iommu_data, + void (*release)(void *iommu_data)); +extern int iommu_group_set_name(struct iommu_group *group, const char *name); +extern int iommu_group_add_device(struct iommu_group *group, + struct device *dev); +extern void iommu_group_remove_device(struct device *dev); +extern int iommu_group_for_each_dev(struct iommu_group *group, void *data, + int (*fn)(struct device *, void *)); +extern struct iommu_group *iommu_group_get(struct device *dev); +extern void iommu_group_put(struct iommu_group *group); +extern int iommu_group_register_notifier(struct iommu_group *group, + struct notifier_block *nb); +extern int iommu_group_unregister_notifier(struct iommu_group *group, + struct notifier_block *nb); +extern int iommu_group_id(struct iommu_group *group); /** * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework @@ -142,6 +175,7 @@ static inline int report_iommu_fault(struct iommu_domain *domain, #else /* CONFIG_IOMMU_API */ struct iommu_ops {}; +struct iommu_group {}; static inline bool iommu_present(struct bus_type *bus) { @@ -197,11 +231,75 @@ static inline void iommu_set_fault_handler(struct iommu_domain *domain, { } -static inline int iommu_device_group(struct device *dev, unsigned int *groupid) +int iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group) +{ + return -ENODEV; +} + +void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group) +{ +} + +struct iommu_group *iommu_group_alloc(void) +{ + return ERR_PTR(-ENODEV); +} + +void *iommu_group_get_iommudata(struct iommu_group *group) +{ + return NULL; +} + +void iommu_group_set_iommudata(struct iommu_group *group, void *iommu_data, + void (*release)(void *iommu_data)) +{ +} + +int iommu_group_set_name(struct iommu_group *group, const char *name) +{ + return -ENODEV; +} + +int iommu_group_add_device(struct iommu_group *group, struct device *dev) +{ + return -ENODEV; +} + +void iommu_group_remove_device(struct device *dev) +{ +} + +int iommu_group_for_each_dev(struct iommu_group *group, void *data, + int (*fn)(struct device *, void *)) +{ + return -ENODEV; +} + +struct iommu_group *iommu_group_get(struct device *dev) +{ + return NULL; +} + +void iommu_group_put(struct iommu_group *group) +{ +} + +int iommu_group_register_notifier(struct iommu_group *group, + struct notifier_block *nb) { return -ENODEV; } +int iommu_group_unregister_notifier(struct iommu_group *group, + struct notifier_block *nb) +{ + return 0; +} + +int iommu_group_id(struct iommu_group *group) +{ + return -ENODEV; +} #endif /* CONFIG_IOMMU_API */ #endif /* __LINUX_IOMMU_H */ -- cgit v1.2.3-59-g8ed1b From 4e0ee78f2af96676c9dca898c13250f62c513058 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Mon, 25 Jun 2012 14:23:54 +0300 Subject: iommu: Add DMA window parser, of_get_dma_window() This code was based on: "arch/microblaze/kernel/prom_parse.c" "arch/powerpc/kernel/prom_parse.c" Can replace "of_parse_dma_window()" in the above. This supports different formats flexibly. "prefix" can be configured if any. "busno" and "index" are optionally specified. Set NULL and 0 if not used. Signed-off-by: Hiroshi DOYU Acked-by: Stephen Warren Signed-off-by: Joerg Roedel --- drivers/iommu/Kconfig | 4 +++ drivers/iommu/Makefile | 1 + drivers/iommu/of_iommu.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of_iommu.h | 21 +++++++++++ 4 files changed, 116 insertions(+) create mode 100644 drivers/iommu/of_iommu.c create mode 100644 include/linux/of_iommu.h (limited to 'include/linux') diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 340893727538..4826af62a9de 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -13,6 +13,10 @@ menuconfig IOMMU_SUPPORT if IOMMU_SUPPORT +config OF_IOMMU + def_bool y + depends on OF + # MSM IOMMU support config MSM_IOMMU bool "MSM IOMMU Support" diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 76e54ef796de..14a4d5fc94fa 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_IOMMU_API) += iommu.o +obj-$(CONFIG_OF_IOMMU) += of_iommu.o obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c new file mode 100644 index 000000000000..ee249bc959f8 --- /dev/null +++ b/drivers/iommu/of_iommu.c @@ -0,0 +1,90 @@ +/* + * OF helpers for IOMMU + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include + +/** + * of_get_dma_window - Parse *dma-window property and returns 0 if found. + * + * @dn: device node + * @prefix: prefix for property name if any + * @index: index to start to parse + * @busno: Returns busno if supported. Otherwise pass NULL + * @addr: Returns address that DMA starts + * @size: Returns the range that DMA can handle + * + * This supports different formats flexibly. "prefix" can be + * configured if any. "busno" and "index" are optionally + * specified. Set 0(or NULL) if not used. + */ +int of_get_dma_window(struct device_node *dn, const char *prefix, int index, + unsigned long *busno, dma_addr_t *addr, size_t *size) +{ + const __be32 *dma_window, *end; + int bytes, cur_index = 0; + char propname[NAME_MAX], addrname[NAME_MAX], sizename[NAME_MAX]; + + if (!dn || !addr || !size) + return -EINVAL; + + if (!prefix) + prefix = ""; + + snprintf(propname, sizeof(propname), "%sdma-window", prefix); + snprintf(addrname, sizeof(addrname), "%s#dma-address-cells", prefix); + snprintf(sizename, sizeof(sizename), "%s#dma-size-cells", prefix); + + dma_window = of_get_property(dn, propname, &bytes); + if (!dma_window) + return -ENODEV; + end = dma_window + bytes / sizeof(*dma_window); + + while (dma_window < end) { + u32 cells; + const void *prop; + + /* busno is one cell if supported */ + if (busno) + *busno = be32_to_cpup(dma_window++); + + prop = of_get_property(dn, addrname, NULL); + if (!prop) + prop = of_get_property(dn, "#address-cells", NULL); + + cells = prop ? be32_to_cpup(prop) : of_n_addr_cells(dn); + if (!cells) + return -EINVAL; + *addr = of_read_number(dma_window, cells); + dma_window += cells; + + prop = of_get_property(dn, sizename, NULL); + cells = prop ? be32_to_cpup(prop) : of_n_size_cells(dn); + if (!cells) + return -EINVAL; + *size = of_read_number(dma_window, cells); + dma_window += cells; + + if (cur_index++ == index) + break; + } + return 0; +} +EXPORT_SYMBOL_GPL(of_get_dma_window); diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h new file mode 100644 index 000000000000..51a560f34bca --- /dev/null +++ b/include/linux/of_iommu.h @@ -0,0 +1,21 @@ +#ifndef __OF_IOMMU_H +#define __OF_IOMMU_H + +#ifdef CONFIG_OF_IOMMU + +extern int of_get_dma_window(struct device_node *dn, const char *prefix, + int index, unsigned long *busno, dma_addr_t *addr, + size_t *size); + +#else + +static inline int of_get_dma_window(struct device_node *dn, const char *prefix, + int index, unsigned long *busno, dma_addr_t *addr, + size_t *size) +{ + return -EINVAL; +} + +#endif /* CONFIG_OF_IOMMU */ + +#endif /* __OF_IOMMU_H */ -- cgit v1.2.3-59-g8ed1b From 88e920b4505105b710f8d4a535ec02c4078f8e2e Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Thu, 21 Jun 2012 11:09:54 -0700 Subject: nl80211: specify RSSI threshold in scheduled scan Support configuring an RSSI threshold in dBm (s32) when requesting scheduled scan, below which a BSS won't be reported by the cfg80211 driver. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- include/linux/nl80211.h | 6 ++++++ include/net/cfg80211.h | 2 ++ net/wireless/nl80211.c | 9 ++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index b7c3b737ddde..c0fc5d277338 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1542,6 +1542,9 @@ enum nl80211_attrs { #define NL80211_MIN_REMAIN_ON_CHANNEL_TIME 10 +/* default RSSI threshold for scan results if none specified. */ +#define NL80211_SCAN_RSSI_THOLD_OFF -300 + /** * enum nl80211_iftype - (virtual) interface types * @@ -1974,6 +1977,8 @@ enum nl80211_reg_rule_attr { * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching, * only report BSS with matching SSID. + * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a + * BSS in scan results. Filtering is turned off if not specified. * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter * attribute number currently defined * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use @@ -1982,6 +1987,7 @@ enum nl80211_sched_scan_match_attr { __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID, NL80211_SCHED_SCAN_MATCH_ATTR_SSID, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, /* keep last */ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f0163a10b8ce..061c01957e54 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1027,6 +1027,7 @@ struct cfg80211_match_set { * @wiphy: the wiphy this was for * @dev: the interface * @channels: channels to scan + * @rssi_thold: don't report scan results below this threshold (in s32 dBm) */ struct cfg80211_sched_scan_request { struct cfg80211_ssid *ssids; @@ -1037,6 +1038,7 @@ struct cfg80211_sched_scan_request { size_t ie_len; struct cfg80211_match_set *match_sets; int n_match_sets; + s32 rssi_thold; /* internal */ struct wiphy *wiphy; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 888fadc4d63e..234ff3bbd104 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -340,6 +340,7 @@ static const struct nla_policy nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = { [NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY, .len = IEEE80211_MAX_SSID_LEN }, + [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 }, }; /* ifidx get helper */ @@ -4387,7 +4388,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH], tmp) { - struct nlattr *ssid; + struct nlattr *ssid, *rssi; nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, nla_data(attr), nla_len(attr), @@ -4403,6 +4404,12 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, request->match_sets[i].ssid.ssid_len = nla_len(ssid); } + rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI]; + if (rssi) + request->rssi_thold = nla_get_u32(rssi); + else + request->rssi_thold = + NL80211_SCAN_RSSI_THOLD_OFF; i++; } } -- cgit v1.2.3-59-g8ed1b From 2b6f2c3520124e8bad4bffa71f5b98e602b9cf03 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Mon, 25 Jun 2012 21:30:57 -0600 Subject: PCI: pull pcibios_setup() up into core Currently, all of the architectures implement their own pcibios_setup() routine. Most of the implementations do nothing so this patch introduces a generic (__weak) routine in the core that can be used by all architectures as a default. If necessary, it can be overridden by architecture-specific code. Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 12 ++++++++++++ include/linux/pci.h | 1 + 2 files changed, 13 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..c87d518acace 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2665,6 +2665,18 @@ static void __pci_set_master(struct pci_dev *dev, bool enable) dev->is_busmaster = enable; } +/** + * pcibios_setup - process "pci=" kernel boot arguments + * @str: string used to pass in "pci=" kernel boot arguments + * + * Process kernel boot arguments. This is the default implementation. + * Architecture specific implementations can override this as necessary. + */ +char * __weak __init pcibios_setup(char *str) +{ + return str; +} + /** * pcibios_set_master - enable PCI bus-mastering for device dev * @dev: the PCI device to enable diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..f91143e86f85 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -642,6 +642,7 @@ extern int no_pci_devices(void); void pcibios_fixup_bus(struct pci_bus *); int __must_check pcibios_enable_device(struct pci_dev *, int mask); +/* Architecture specific versions may override this (weak) */ char *pcibios_setup(char *str); /* Used only when drivers/pci/setup.c is used */ -- cgit v1.2.3-59-g8ed1b From 32bad7e30f113a8a5cebe4704bf6519ab4383e1b Mon Sep 17 00:00:00 2001 From: "alex.bluesman.smirnov@gmail.com" Date: Mon, 25 Jun 2012 23:24:48 +0000 Subject: mac802154: add wpan device-class support Every real 802.15.4 transceiver, which works with software MAC layer, can be classified as a wpan device in this stack. So the wpan device implementation provides missing link in datapath between the device drivers and the Linux network queue. According to the IEEE 802.15.4 standard each packet can be one of the following types: - beacon - MAC layer command - ACK - data This patch adds support for the data packet-type only, but this is enough to perform data transmission and receiving over radio. Signed-off-by: Alexander Smirnov Signed-off-by: David S. Miller --- include/linux/nl802154.h | 14 +- include/net/mac802154.h | 8 + net/mac802154/Makefile | 2 +- net/mac802154/ieee802154_dev.c | 4 + net/mac802154/mac802154.h | 4 + net/mac802154/mac_cmd.c | 4 + net/mac802154/rx.c | 1 + net/mac802154/wpan.c | 559 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 583 insertions(+), 13 deletions(-) create mode 100644 net/mac802154/wpan.c (limited to 'include/linux') diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h index 5a3db3aa5f17..fd4f2d1cdf6c 100644 --- a/include/linux/nl802154.h +++ b/include/linux/nl802154.h @@ -130,18 +130,8 @@ enum { enum { __IEEE802154_DEV_INVALID = -1, - /* TODO: - * Nowadays three device types supported by this stack at linux-zigbee - * project: WPAN = 0, MONITOR = 1 and SMAC = 2. - * - * Since this stack implementation exists many years, it's definitely - * bad idea to change the assigned values due to they are already used - * by third-party userspace software like: iz-tools, wireshark... - * - * Currently only monitor device is added and initialized by '1' for - * compatibility. - */ - IEEE802154_DEV_MONITOR = 1, + IEEE802154_DEV_WPAN, + IEEE802154_DEV_MONITOR, __IEEE802154_DEV_MAX, }; diff --git a/include/net/mac802154.h b/include/net/mac802154.h index c9f8ab5cc687..d0d11df9cba1 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -21,6 +21,14 @@ #include +/* General MAC frame format: + * 2 bytes: Frame Control + * 1 byte: Sequence Number + * 20 bytes: Addressing fields + * 14 bytes: Auxiliary Security Header + */ +#define MAC802154_FRAME_HARD_HEADER_LEN (2 + 1 + 20 + 14) + /* The following flags are used to indicate changed address settings from * the stack to the hardware. */ diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile index ec1bd3fc1273..57cf5d1a2e4a 100644 --- a/net/mac802154/Makefile +++ b/net/mac802154/Makefile @@ -1,2 +1,2 @@ obj-$(CONFIG_MAC802154) += mac802154.o -mac802154-objs := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o monitor.o +mac802154-objs := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o monitor.o wpan.o diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c index e3edfb0661b0..e748aed290aa 100644 --- a/net/mac802154/ieee802154_dev.c +++ b/net/mac802154/ieee802154_dev.c @@ -140,6 +140,10 @@ mac802154_add_iface(struct wpan_phy *phy, const char *name, int type) dev = alloc_netdev(sizeof(struct mac802154_sub_if_data), name, mac802154_monitor_setup); break; + case IEEE802154_DEV_WPAN: + dev = alloc_netdev(sizeof(struct mac802154_sub_if_data), + name, mac802154_wpan_setup); + break; default: dev = NULL; err = -EINVAL; diff --git a/net/mac802154/mac802154.h b/net/mac802154/mac802154.h index 789d9c948aec..c0efcf19a171 100644 --- a/net/mac802154/mac802154.h +++ b/net/mac802154/mac802154.h @@ -93,6 +93,7 @@ struct mac802154_sub_if_data { #define MAC802154_CHAN_NONE (~(u8)0) /* No channel is assigned */ extern struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced; +extern struct ieee802154_mlme_ops mac802154_mlme_wpan; int mac802154_slave_open(struct net_device *dev); int mac802154_slave_close(struct net_device *dev); @@ -100,6 +101,9 @@ int mac802154_slave_close(struct net_device *dev); void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb); void mac802154_monitor_setup(struct net_device *dev); +void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb); +void mac802154_wpan_setup(struct net_device *dev); + netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, u8 page, u8 chan); diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c index 7a5d0e052cd7..db8341957bd2 100644 --- a/net/mac802154/mac_cmd.c +++ b/net/mac802154/mac_cmd.c @@ -43,3 +43,7 @@ struct wpan_phy *mac802154_get_phy(const struct net_device *dev) struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced = { .get_phy = mac802154_get_phy, }; + +struct ieee802154_mlme_ops mac802154_mlme_wpan = { + .get_phy = mac802154_get_phy, +}; diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index 4a7d76d4f8bc..38548ec2098f 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -77,6 +77,7 @@ mac802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb, u8 lqi) } mac802154_monitors_rx(priv, skb); + mac802154_wpans_rx(priv, skb); out: dev_kfree_skb(skb); return; diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c new file mode 100644 index 000000000000..f30f6d4beea1 --- /dev/null +++ b/net/mac802154/wpan.c @@ -0,0 +1,559 @@ +/* + * Copyright 2007-2012 Siemens AG + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Written by: + * Dmitry Eremin-Solenikov + * Sergey Lapin + * Maxim Gorbachyov + * Alexander Smirnov + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "mac802154.h" + +static inline int mac802154_fetch_skb_u8(struct sk_buff *skb, u8 *val) +{ + if (unlikely(!pskb_may_pull(skb, 1))) + return -EINVAL; + + *val = skb->data[0]; + skb_pull(skb, 1); + + return 0; +} + +static inline int mac802154_fetch_skb_u16(struct sk_buff *skb, u16 *val) +{ + if (unlikely(!pskb_may_pull(skb, 2))) + return -EINVAL; + + *val = skb->data[0] | (skb->data[1] << 8); + skb_pull(skb, 2); + + return 0; +} + +static inline void mac802154_haddr_copy_swap(u8 *dest, const u8 *src) +{ + int i; + for (i = 0; i < IEEE802154_ADDR_LEN; i++) + dest[IEEE802154_ADDR_LEN - i - 1] = src[i]; +} + +static int +mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct sockaddr_ieee802154 *sa = + (struct sockaddr_ieee802154 *)&ifr->ifr_addr; + int err = -ENOIOCTLCMD; + + spin_lock_bh(&priv->mib_lock); + + switch (cmd) { + case SIOCGIFADDR: + if (priv->pan_id == IEEE802154_PANID_BROADCAST || + priv->short_addr == IEEE802154_ADDR_BROADCAST) { + err = -EADDRNOTAVAIL; + break; + } + + sa->family = AF_IEEE802154; + sa->addr.addr_type = IEEE802154_ADDR_SHORT; + sa->addr.pan_id = priv->pan_id; + sa->addr.short_addr = priv->short_addr; + + err = 0; + break; + case SIOCSIFADDR: + dev_warn(&dev->dev, + "Using DEBUGing ioctl SIOCSIFADDR isn't recommened!\n"); + if (sa->family != AF_IEEE802154 || + sa->addr.addr_type != IEEE802154_ADDR_SHORT || + sa->addr.pan_id == IEEE802154_PANID_BROADCAST || + sa->addr.short_addr == IEEE802154_ADDR_BROADCAST || + sa->addr.short_addr == IEEE802154_ADDR_UNDEF) { + err = -EINVAL; + break; + } + + priv->pan_id = sa->addr.pan_id; + priv->short_addr = sa->addr.short_addr; + + err = 0; + break; + } + + spin_unlock_bh(&priv->mib_lock); + return err; +} + +static int mac802154_wpan_mac_addr(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + + if (netif_running(dev)) + return -EBUSY; + + /* FIXME: validate addr */ + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + mac802154_dev_set_ieee_addr(dev); + return 0; +} + +static int mac802154_header_create(struct sk_buff *skb, + struct net_device *dev, + unsigned short type, + const void *_daddr, + const void *_saddr, + unsigned len) +{ + const struct ieee802154_addr *saddr = _saddr; + const struct ieee802154_addr *daddr = _daddr; + struct ieee802154_addr dev_addr; + struct mac802154_sub_if_data *priv = netdev_priv(dev); + int pos = 2; + u8 *head; + u16 fc; + + if (!daddr) + return -EINVAL; + + head = kzalloc(MAC802154_FRAME_HARD_HEADER_LEN, GFP_KERNEL); + if (head == NULL) + return -ENOMEM; + + head[pos++] = mac_cb(skb)->seq; /* DSN/BSN */ + fc = mac_cb_type(skb); + + if (!saddr) { + spin_lock_bh(&priv->mib_lock); + + if (priv->short_addr == IEEE802154_ADDR_BROADCAST || + priv->short_addr == IEEE802154_ADDR_UNDEF || + priv->pan_id == IEEE802154_PANID_BROADCAST) { + dev_addr.addr_type = IEEE802154_ADDR_LONG; + memcpy(dev_addr.hwaddr, dev->dev_addr, + IEEE802154_ADDR_LEN); + } else { + dev_addr.addr_type = IEEE802154_ADDR_SHORT; + dev_addr.short_addr = priv->short_addr; + } + + dev_addr.pan_id = priv->pan_id; + saddr = &dev_addr; + + spin_unlock_bh(&priv->mib_lock); + } + + if (daddr->addr_type != IEEE802154_ADDR_NONE) { + fc |= (daddr->addr_type << IEEE802154_FC_DAMODE_SHIFT); + + head[pos++] = daddr->pan_id & 0xff; + head[pos++] = daddr->pan_id >> 8; + + if (daddr->addr_type == IEEE802154_ADDR_SHORT) { + head[pos++] = daddr->short_addr & 0xff; + head[pos++] = daddr->short_addr >> 8; + } else { + mac802154_haddr_copy_swap(head + pos, daddr->hwaddr); + pos += IEEE802154_ADDR_LEN; + } + } + + if (saddr->addr_type != IEEE802154_ADDR_NONE) { + fc |= (saddr->addr_type << IEEE802154_FC_SAMODE_SHIFT); + + if ((saddr->pan_id == daddr->pan_id) && + (saddr->pan_id != IEEE802154_PANID_BROADCAST)) { + /* PANID compression/intra PAN */ + fc |= IEEE802154_FC_INTRA_PAN; + } else { + head[pos++] = saddr->pan_id & 0xff; + head[pos++] = saddr->pan_id >> 8; + } + + if (saddr->addr_type == IEEE802154_ADDR_SHORT) { + head[pos++] = saddr->short_addr & 0xff; + head[pos++] = saddr->short_addr >> 8; + } else { + mac802154_haddr_copy_swap(head + pos, saddr->hwaddr); + pos += IEEE802154_ADDR_LEN; + } + } + + head[0] = fc; + head[1] = fc >> 8; + + memcpy(skb_push(skb, pos), head, pos); + kfree(head); + + return pos; +} + +static int +mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr) +{ + const u8 *hdr = skb_mac_header(skb); + const u8 *tail = skb_tail_pointer(skb); + struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr; + u16 fc; + int da_type; + + if (hdr + 3 > tail) + goto malformed; + + fc = hdr[0] | (hdr[1] << 8); + + hdr += 3; + + da_type = IEEE802154_FC_DAMODE(fc); + addr->addr_type = IEEE802154_FC_SAMODE(fc); + + switch (da_type) { + case IEEE802154_ADDR_NONE: + if (fc & IEEE802154_FC_INTRA_PAN) + goto malformed; + break; + case IEEE802154_ADDR_LONG: + if (fc & IEEE802154_FC_INTRA_PAN) { + if (hdr + 2 > tail) + goto malformed; + addr->pan_id = hdr[0] | (hdr[1] << 8); + hdr += 2; + } + + if (hdr + IEEE802154_ADDR_LEN > tail) + goto malformed; + + hdr += IEEE802154_ADDR_LEN; + break; + case IEEE802154_ADDR_SHORT: + if (fc & IEEE802154_FC_INTRA_PAN) { + if (hdr + 2 > tail) + goto malformed; + addr->pan_id = hdr[0] | (hdr[1] << 8); + hdr += 2; + } + + if (hdr + 2 > tail) + goto malformed; + + hdr += 2; + break; + default: + goto malformed; + + } + + switch (addr->addr_type) { + case IEEE802154_ADDR_NONE: + break; + case IEEE802154_ADDR_LONG: + if (!(fc & IEEE802154_FC_INTRA_PAN)) { + if (hdr + 2 > tail) + goto malformed; + addr->pan_id = hdr[0] | (hdr[1] << 8); + hdr += 2; + } + + if (hdr + IEEE802154_ADDR_LEN > tail) + goto malformed; + + mac802154_haddr_copy_swap(addr->hwaddr, hdr); + hdr += IEEE802154_ADDR_LEN; + break; + case IEEE802154_ADDR_SHORT: + if (!(fc & IEEE802154_FC_INTRA_PAN)) { + if (hdr + 2 > tail) + goto malformed; + addr->pan_id = hdr[0] | (hdr[1] << 8); + hdr += 2; + } + + if (hdr + 2 > tail) + goto malformed; + + addr->short_addr = hdr[0] | (hdr[1] << 8); + hdr += 2; + break; + default: + goto malformed; + } + + return sizeof(struct ieee802154_addr); + +malformed: + pr_debug("malformed packet\n"); + return 0; +} + +static netdev_tx_t +mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct mac802154_sub_if_data *priv; + u8 chan, page; + + priv = netdev_priv(dev); + + spin_lock_bh(&priv->mib_lock); + chan = priv->chan; + page = priv->page; + spin_unlock_bh(&priv->mib_lock); + + if (chan == MAC802154_CHAN_NONE || + page >= WPAN_NUM_PAGES || + chan >= WPAN_NUM_CHANNELS) + return NETDEV_TX_OK; + + skb->skb_iif = dev->ifindex; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + return mac802154_tx(priv->hw, skb, page, chan); +} + +static struct header_ops mac802154_header_ops = { + .create = mac802154_header_create, + .parse = mac802154_header_parse, +}; + +static const struct net_device_ops mac802154_wpan_ops = { + .ndo_open = mac802154_slave_open, + .ndo_stop = mac802154_slave_close, + .ndo_start_xmit = mac802154_wpan_xmit, + .ndo_do_ioctl = mac802154_wpan_ioctl, + .ndo_set_mac_address = mac802154_wpan_mac_addr, +}; + +void mac802154_wpan_setup(struct net_device *dev) +{ + struct mac802154_sub_if_data *priv; + + dev->addr_len = IEEE802154_ADDR_LEN; + memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); + + dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN; + dev->header_ops = &mac802154_header_ops; + dev->needed_tailroom = 2; /* FCS */ + dev->mtu = IEEE802154_MTU; + dev->tx_queue_len = 10; + dev->type = ARPHRD_IEEE802154; + dev->flags = IFF_NOARP | IFF_BROADCAST; + dev->watchdog_timeo = 0; + + dev->destructor = free_netdev; + dev->netdev_ops = &mac802154_wpan_ops; + dev->ml_priv = &mac802154_mlme_wpan; + + priv = netdev_priv(dev); + priv->type = IEEE802154_DEV_WPAN; + + priv->chan = MAC802154_CHAN_NONE; + priv->page = 0; + + spin_lock_init(&priv->mib_lock); + + get_random_bytes(&priv->bsn, 1); + get_random_bytes(&priv->dsn, 1); + + priv->pan_id = IEEE802154_PANID_BROADCAST; + priv->short_addr = IEEE802154_ADDR_BROADCAST; +} + +static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb) +{ + return netif_rx(skb); +} + +static int +mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb) +{ + pr_debug("getting packet via slave interface %s\n", sdata->dev->name); + + spin_lock_bh(&sdata->mib_lock); + + switch (mac_cb(skb)->da.addr_type) { + case IEEE802154_ADDR_NONE: + if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE) + /* FIXME: check if we are PAN coordinator */ + skb->pkt_type = PACKET_OTHERHOST; + else + /* ACK comes with both addresses empty */ + skb->pkt_type = PACKET_HOST; + break; + case IEEE802154_ADDR_LONG: + if (mac_cb(skb)->da.pan_id != sdata->pan_id && + mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST) + skb->pkt_type = PACKET_OTHERHOST; + else if (!memcmp(mac_cb(skb)->da.hwaddr, sdata->dev->dev_addr, + IEEE802154_ADDR_LEN)) + skb->pkt_type = PACKET_HOST; + else + skb->pkt_type = PACKET_OTHERHOST; + break; + case IEEE802154_ADDR_SHORT: + if (mac_cb(skb)->da.pan_id != sdata->pan_id && + mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST) + skb->pkt_type = PACKET_OTHERHOST; + else if (mac_cb(skb)->da.short_addr == sdata->short_addr) + skb->pkt_type = PACKET_HOST; + else if (mac_cb(skb)->da.short_addr == + IEEE802154_ADDR_BROADCAST) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_OTHERHOST; + break; + default: + break; + } + + spin_unlock_bh(&sdata->mib_lock); + + skb->dev = sdata->dev; + + sdata->dev->stats.rx_packets++; + sdata->dev->stats.rx_bytes += skb->len; + + switch (mac_cb_type(skb)) { + case IEEE802154_FC_TYPE_DATA: + return mac802154_process_data(sdata->dev, skb); + default: + pr_warning("ieee802154: bad frame received (type = %d)\n", + mac_cb_type(skb)); + kfree_skb(skb); + return NET_RX_DROP; + } +} + +static int mac802154_parse_frame_start(struct sk_buff *skb) +{ + u8 *head = skb->data; + u16 fc; + + if (mac802154_fetch_skb_u16(skb, &fc) || + mac802154_fetch_skb_u8(skb, &(mac_cb(skb)->seq))) + goto err; + + pr_debug("fc: %04x dsn: %02x\n", fc, head[2]); + + mac_cb(skb)->flags = IEEE802154_FC_TYPE(fc); + mac_cb(skb)->sa.addr_type = IEEE802154_FC_SAMODE(fc); + mac_cb(skb)->da.addr_type = IEEE802154_FC_DAMODE(fc); + + if (fc & IEEE802154_FC_INTRA_PAN) + mac_cb(skb)->flags |= MAC_CB_FLAG_INTRAPAN; + + if (mac_cb(skb)->da.addr_type != IEEE802154_ADDR_NONE) { + if (mac802154_fetch_skb_u16(skb, &(mac_cb(skb)->da.pan_id))) + goto err; + + /* source PAN id compression */ + if (mac_cb_is_intrapan(skb)) + mac_cb(skb)->sa.pan_id = mac_cb(skb)->da.pan_id; + + pr_debug("dest PAN addr: %04x\n", mac_cb(skb)->da.pan_id); + + if (mac_cb(skb)->da.addr_type == IEEE802154_ADDR_SHORT) { + u16 *da = &(mac_cb(skb)->da.short_addr); + + if (mac802154_fetch_skb_u16(skb, da)) + goto err; + + pr_debug("destination address is short: %04x\n", + mac_cb(skb)->da.short_addr); + } else { + if (!pskb_may_pull(skb, IEEE802154_ADDR_LEN)) + goto err; + + mac802154_haddr_copy_swap(mac_cb(skb)->da.hwaddr, + skb->data); + skb_pull(skb, IEEE802154_ADDR_LEN); + + pr_debug("destination address is hardware\n"); + } + } + + if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE) { + /* non PAN-compression, fetch source address id */ + if (!(mac_cb_is_intrapan(skb))) { + u16 *sa_pan = &(mac_cb(skb)->sa.pan_id); + + if (mac802154_fetch_skb_u16(skb, sa_pan)) + goto err; + } + + pr_debug("source PAN addr: %04x\n", mac_cb(skb)->da.pan_id); + + if (mac_cb(skb)->sa.addr_type == IEEE802154_ADDR_SHORT) { + u16 *sa = &(mac_cb(skb)->sa.short_addr); + + if (mac802154_fetch_skb_u16(skb, sa)) + goto err; + + pr_debug("source address is short: %04x\n", + mac_cb(skb)->sa.short_addr); + } else { + if (!pskb_may_pull(skb, IEEE802154_ADDR_LEN)) + goto err; + + mac802154_haddr_copy_swap(mac_cb(skb)->sa.hwaddr, + skb->data); + skb_pull(skb, IEEE802154_ADDR_LEN); + + pr_debug("source address is hardware\n"); + } + } + + return 0; +err: + return -EINVAL; +} + +void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb) +{ + int ret; + struct sk_buff *sskb; + struct mac802154_sub_if_data *sdata; + + ret = mac802154_parse_frame_start(skb); + if (ret) { + pr_debug("got invalid frame\n"); + return; + } + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &priv->slaves, list) { + if (sdata->type != IEEE802154_DEV_WPAN) + continue; + + sskb = skb_clone(skb, GFP_ATOMIC); + if (sskb) + mac802154_subif_frame(sdata, sskb); + } + rcu_read_unlock(); +} -- cgit v1.2.3-59-g8ed1b From 7b8e19b67c1b171a04f6bd2f973d0b38cb496bf6 Mon Sep 17 00:00:00 2001 From: "alex.bluesman.smirnov@gmail.com" Date: Mon, 25 Jun 2012 23:24:53 +0000 Subject: drivers/ieee802154: add support for the at86rf230/231 transceivers The AT86RF231 is a feature rich, low-power 2.4 GHz radio transceiver designed for industrial and consumer ZigBee/IEEE 802.15.4, 6LoWPAN, RF4CE and high data rate 2.4 GHz ISM band applications. This patch adds support for the Atmel RF230/231 radio transceivers. Signed-off-by: Alexander Smirnov Signed-off-by: David S. Miller --- drivers/ieee802154/Kconfig | 6 + drivers/ieee802154/Makefile | 1 + drivers/ieee802154/at86rf230.c | 965 +++++++++++++++++++++++++++++++++++++++++ include/linux/spi/at86rf230.h | 31 ++ 4 files changed, 1003 insertions(+) create mode 100644 drivers/ieee802154/at86rf230.c create mode 100644 include/linux/spi/at86rf230.h (limited to 'include/linux') diff --git a/drivers/ieee802154/Kconfig b/drivers/ieee802154/Kconfig index 15c064073701..1fc4eefc20ed 100644 --- a/drivers/ieee802154/Kconfig +++ b/drivers/ieee802154/Kconfig @@ -19,6 +19,7 @@ config IEEE802154_FAKEHARD This driver can also be built as a module. To do so say M here. The module will be called 'fakehard'. + config IEEE802154_FAKELB depends on IEEE802154_DRIVERS && MAC802154 tristate "IEEE 802.15.4 loopback driver" @@ -28,3 +29,8 @@ config IEEE802154_FAKELB This driver can also be built as a module. To do so say M here. The module will be called 'fakelb'. + +config IEEE802154_AT86RF230 + depends on IEEE802154_DRIVERS && MAC802154 + tristate "AT86RF230/231 transceiver driver" + depends on SPI diff --git a/drivers/ieee802154/Makefile b/drivers/ieee802154/Makefile index ea784ea6f0f8..4f4371d3aa7d 100644 --- a/drivers/ieee802154/Makefile +++ b/drivers/ieee802154/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_IEEE802154_FAKEHARD) += fakehard.o obj-$(CONFIG_IEEE802154_FAKELB) += fakelb.o +obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o diff --git a/drivers/ieee802154/at86rf230.c b/drivers/ieee802154/at86rf230.c new file mode 100644 index 000000000000..4d033d4c4ddc --- /dev/null +++ b/drivers/ieee802154/at86rf230.c @@ -0,0 +1,965 @@ +/* + * AT86RF230/RF231 driver + * + * Copyright (C) 2009-2012 Siemens AG + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Written by: + * Dmitry Eremin-Solenikov + * Alexander Smirnov + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct at86rf230_local { + struct spi_device *spi; + int rstn, slp_tr, dig2; + + u8 part; + u8 vers; + + u8 buf[2]; + struct mutex bmux; + + struct work_struct irqwork; + struct completion tx_complete; + + struct ieee802154_dev *dev; + + spinlock_t lock; + bool irq_disabled; + bool is_tx; +}; + +#define RG_TRX_STATUS (0x01) +#define SR_TRX_STATUS 0x01, 0x1f, 0 +#define SR_RESERVED_01_3 0x01, 0x20, 5 +#define SR_CCA_STATUS 0x01, 0x40, 6 +#define SR_CCA_DONE 0x01, 0x80, 7 +#define RG_TRX_STATE (0x02) +#define SR_TRX_CMD 0x02, 0x1f, 0 +#define SR_TRAC_STATUS 0x02, 0xe0, 5 +#define RG_TRX_CTRL_0 (0x03) +#define SR_CLKM_CTRL 0x03, 0x07, 0 +#define SR_CLKM_SHA_SEL 0x03, 0x08, 3 +#define SR_PAD_IO_CLKM 0x03, 0x30, 4 +#define SR_PAD_IO 0x03, 0xc0, 6 +#define RG_TRX_CTRL_1 (0x04) +#define SR_IRQ_POLARITY 0x04, 0x01, 0 +#define SR_IRQ_MASK_MODE 0x04, 0x02, 1 +#define SR_SPI_CMD_MODE 0x04, 0x0c, 2 +#define SR_RX_BL_CTRL 0x04, 0x10, 4 +#define SR_TX_AUTO_CRC_ON 0x04, 0x20, 5 +#define SR_IRQ_2_EXT_EN 0x04, 0x40, 6 +#define SR_PA_EXT_EN 0x04, 0x80, 7 +#define RG_PHY_TX_PWR (0x05) +#define SR_TX_PWR 0x05, 0x0f, 0 +#define SR_PA_LT 0x05, 0x30, 4 +#define SR_PA_BUF_LT 0x05, 0xc0, 6 +#define RG_PHY_RSSI (0x06) +#define SR_RSSI 0x06, 0x1f, 0 +#define SR_RND_VALUE 0x06, 0x60, 5 +#define SR_RX_CRC_VALID 0x06, 0x80, 7 +#define RG_PHY_ED_LEVEL (0x07) +#define SR_ED_LEVEL 0x07, 0xff, 0 +#define RG_PHY_CC_CCA (0x08) +#define SR_CHANNEL 0x08, 0x1f, 0 +#define SR_CCA_MODE 0x08, 0x60, 5 +#define SR_CCA_REQUEST 0x08, 0x80, 7 +#define RG_CCA_THRES (0x09) +#define SR_CCA_ED_THRES 0x09, 0x0f, 0 +#define SR_RESERVED_09_1 0x09, 0xf0, 4 +#define RG_RX_CTRL (0x0a) +#define SR_PDT_THRES 0x0a, 0x0f, 0 +#define SR_RESERVED_0a_1 0x0a, 0xf0, 4 +#define RG_SFD_VALUE (0x0b) +#define SR_SFD_VALUE 0x0b, 0xff, 0 +#define RG_TRX_CTRL_2 (0x0c) +#define SR_OQPSK_DATA_RATE 0x0c, 0x03, 0 +#define SR_RESERVED_0c_2 0x0c, 0x7c, 2 +#define SR_RX_SAFE_MODE 0x0c, 0x80, 7 +#define RG_ANT_DIV (0x0d) +#define SR_ANT_CTRL 0x0d, 0x03, 0 +#define SR_ANT_EXT_SW_EN 0x0d, 0x04, 2 +#define SR_ANT_DIV_EN 0x0d, 0x08, 3 +#define SR_RESERVED_0d_2 0x0d, 0x70, 4 +#define SR_ANT_SEL 0x0d, 0x80, 7 +#define RG_IRQ_MASK (0x0e) +#define SR_IRQ_MASK 0x0e, 0xff, 0 +#define RG_IRQ_STATUS (0x0f) +#define SR_IRQ_0_PLL_LOCK 0x0f, 0x01, 0 +#define SR_IRQ_1_PLL_UNLOCK 0x0f, 0x02, 1 +#define SR_IRQ_2_RX_START 0x0f, 0x04, 2 +#define SR_IRQ_3_TRX_END 0x0f, 0x08, 3 +#define SR_IRQ_4_CCA_ED_DONE 0x0f, 0x10, 4 +#define SR_IRQ_5_AMI 0x0f, 0x20, 5 +#define SR_IRQ_6_TRX_UR 0x0f, 0x40, 6 +#define SR_IRQ_7_BAT_LOW 0x0f, 0x80, 7 +#define RG_VREG_CTRL (0x10) +#define SR_RESERVED_10_6 0x10, 0x03, 0 +#define SR_DVDD_OK 0x10, 0x04, 2 +#define SR_DVREG_EXT 0x10, 0x08, 3 +#define SR_RESERVED_10_3 0x10, 0x30, 4 +#define SR_AVDD_OK 0x10, 0x40, 6 +#define SR_AVREG_EXT 0x10, 0x80, 7 +#define RG_BATMON (0x11) +#define SR_BATMON_VTH 0x11, 0x0f, 0 +#define SR_BATMON_HR 0x11, 0x10, 4 +#define SR_BATMON_OK 0x11, 0x20, 5 +#define SR_RESERVED_11_1 0x11, 0xc0, 6 +#define RG_XOSC_CTRL (0x12) +#define SR_XTAL_TRIM 0x12, 0x0f, 0 +#define SR_XTAL_MODE 0x12, 0xf0, 4 +#define RG_RX_SYN (0x15) +#define SR_RX_PDT_LEVEL 0x15, 0x0f, 0 +#define SR_RESERVED_15_2 0x15, 0x70, 4 +#define SR_RX_PDT_DIS 0x15, 0x80, 7 +#define RG_XAH_CTRL_1 (0x17) +#define SR_RESERVED_17_8 0x17, 0x01, 0 +#define SR_AACK_PROM_MODE 0x17, 0x02, 1 +#define SR_AACK_ACK_TIME 0x17, 0x04, 2 +#define SR_RESERVED_17_5 0x17, 0x08, 3 +#define SR_AACK_UPLD_RES_FT 0x17, 0x10, 4 +#define SR_AACK_FLTR_RES_FT 0x17, 0x20, 5 +#define SR_RESERVED_17_2 0x17, 0x40, 6 +#define SR_RESERVED_17_1 0x17, 0x80, 7 +#define RG_FTN_CTRL (0x18) +#define SR_RESERVED_18_2 0x18, 0x7f, 0 +#define SR_FTN_START 0x18, 0x80, 7 +#define RG_PLL_CF (0x1a) +#define SR_RESERVED_1a_2 0x1a, 0x7f, 0 +#define SR_PLL_CF_START 0x1a, 0x80, 7 +#define RG_PLL_DCU (0x1b) +#define SR_RESERVED_1b_3 0x1b, 0x3f, 0 +#define SR_RESERVED_1b_2 0x1b, 0x40, 6 +#define SR_PLL_DCU_START 0x1b, 0x80, 7 +#define RG_PART_NUM (0x1c) +#define SR_PART_NUM 0x1c, 0xff, 0 +#define RG_VERSION_NUM (0x1d) +#define SR_VERSION_NUM 0x1d, 0xff, 0 +#define RG_MAN_ID_0 (0x1e) +#define SR_MAN_ID_0 0x1e, 0xff, 0 +#define RG_MAN_ID_1 (0x1f) +#define SR_MAN_ID_1 0x1f, 0xff, 0 +#define RG_SHORT_ADDR_0 (0x20) +#define SR_SHORT_ADDR_0 0x20, 0xff, 0 +#define RG_SHORT_ADDR_1 (0x21) +#define SR_SHORT_ADDR_1 0x21, 0xff, 0 +#define RG_PAN_ID_0 (0x22) +#define SR_PAN_ID_0 0x22, 0xff, 0 +#define RG_PAN_ID_1 (0x23) +#define SR_PAN_ID_1 0x23, 0xff, 0 +#define RG_IEEE_ADDR_0 (0x24) +#define SR_IEEE_ADDR_0 0x24, 0xff, 0 +#define RG_IEEE_ADDR_1 (0x25) +#define SR_IEEE_ADDR_1 0x25, 0xff, 0 +#define RG_IEEE_ADDR_2 (0x26) +#define SR_IEEE_ADDR_2 0x26, 0xff, 0 +#define RG_IEEE_ADDR_3 (0x27) +#define SR_IEEE_ADDR_3 0x27, 0xff, 0 +#define RG_IEEE_ADDR_4 (0x28) +#define SR_IEEE_ADDR_4 0x28, 0xff, 0 +#define RG_IEEE_ADDR_5 (0x29) +#define SR_IEEE_ADDR_5 0x29, 0xff, 0 +#define RG_IEEE_ADDR_6 (0x2a) +#define SR_IEEE_ADDR_6 0x2a, 0xff, 0 +#define RG_IEEE_ADDR_7 (0x2b) +#define SR_IEEE_ADDR_7 0x2b, 0xff, 0 +#define RG_XAH_CTRL_0 (0x2c) +#define SR_SLOTTED_OPERATION 0x2c, 0x01, 0 +#define SR_MAX_CSMA_RETRIES 0x2c, 0x0e, 1 +#define SR_MAX_FRAME_RETRIES 0x2c, 0xf0, 4 +#define RG_CSMA_SEED_0 (0x2d) +#define SR_CSMA_SEED_0 0x2d, 0xff, 0 +#define RG_CSMA_SEED_1 (0x2e) +#define SR_CSMA_SEED_1 0x2e, 0x07, 0 +#define SR_AACK_I_AM_COORD 0x2e, 0x08, 3 +#define SR_AACK_DIS_ACK 0x2e, 0x10, 4 +#define SR_AACK_SET_PD 0x2e, 0x20, 5 +#define SR_AACK_FVN_MODE 0x2e, 0xc0, 6 +#define RG_CSMA_BE (0x2f) +#define SR_MIN_BE 0x2f, 0x0f, 0 +#define SR_MAX_BE 0x2f, 0xf0, 4 + +#define CMD_REG 0x80 +#define CMD_REG_MASK 0x3f +#define CMD_WRITE 0x40 +#define CMD_FB 0x20 + +#define IRQ_BAT_LOW (1 << 7) +#define IRQ_TRX_UR (1 << 6) +#define IRQ_AMI (1 << 5) +#define IRQ_CCA_ED (1 << 4) +#define IRQ_TRX_END (1 << 3) +#define IRQ_RX_START (1 << 2) +#define IRQ_PLL_UNL (1 << 1) +#define IRQ_PLL_LOCK (1 << 0) + +#define STATE_P_ON 0x00 /* BUSY */ +#define STATE_BUSY_RX 0x01 +#define STATE_BUSY_TX 0x02 +#define STATE_FORCE_TRX_OFF 0x03 +#define STATE_FORCE_TX_ON 0x04 /* IDLE */ +/* 0x05 */ /* INVALID_PARAMETER */ +#define STATE_RX_ON 0x06 +/* 0x07 */ /* SUCCESS */ +#define STATE_TRX_OFF 0x08 +#define STATE_TX_ON 0x09 +/* 0x0a - 0x0e */ /* 0x0a - UNSUPPORTED_ATTRIBUTE */ +#define STATE_SLEEP 0x0F +#define STATE_BUSY_RX_AACK 0x11 +#define STATE_BUSY_TX_ARET 0x12 +#define STATE_BUSY_RX_AACK_ON 0x16 +#define STATE_BUSY_TX_ARET_ON 0x19 +#define STATE_RX_ON_NOCLK 0x1C +#define STATE_RX_AACK_ON_NOCLK 0x1D +#define STATE_BUSY_RX_AACK_NOCLK 0x1E +#define STATE_TRANSITION_IN_PROGRESS 0x1F + +static int +__at86rf230_write(struct at86rf230_local *lp, u8 addr, u8 data) +{ + u8 *buf = lp->buf; + int status; + struct spi_message msg; + struct spi_transfer xfer = { + .len = 2, + .tx_buf = buf, + }; + + buf[0] = (addr & CMD_REG_MASK) | CMD_REG | CMD_WRITE; + buf[1] = data; + dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); + dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + status = spi_sync(lp->spi, &msg); + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + if (msg.status) + status = msg.status; + + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); + dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); + + return status; +} + +static int +__at86rf230_read_subreg(struct at86rf230_local *lp, + u8 addr, u8 mask, int shift, u8 *data) +{ + u8 *buf = lp->buf; + int status; + struct spi_message msg; + struct spi_transfer xfer = { + .len = 2, + .tx_buf = buf, + .rx_buf = buf, + }; + + buf[0] = (addr & CMD_REG_MASK) | CMD_REG; + buf[1] = 0xff; + dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + status = spi_sync(lp->spi, &msg); + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + if (msg.status) + status = msg.status; + + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); + dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); + + if (status == 0) + *data = buf[1]; + + return status; +} + +static int +at86rf230_read_subreg(struct at86rf230_local *lp, + u8 addr, u8 mask, int shift, u8 *data) +{ + int status; + + mutex_lock(&lp->bmux); + status = __at86rf230_read_subreg(lp, addr, mask, shift, data); + mutex_unlock(&lp->bmux); + + return status; +} + +static int +at86rf230_write_subreg(struct at86rf230_local *lp, + u8 addr, u8 mask, int shift, u8 data) +{ + int status; + u8 val; + + mutex_lock(&lp->bmux); + status = __at86rf230_read_subreg(lp, addr, 0xff, 0, &val); + if (status) + goto out; + + val &= ~mask; + val |= (data << shift) & mask; + + status = __at86rf230_write(lp, addr, val); +out: + mutex_unlock(&lp->bmux); + + return status; +} + +static int +at86rf230_write_fbuf(struct at86rf230_local *lp, u8 *data, u8 len) +{ + u8 *buf = lp->buf; + int status; + struct spi_message msg; + struct spi_transfer xfer_head = { + .len = 2, + .tx_buf = buf, + + }; + struct spi_transfer xfer_buf = { + .len = len, + .tx_buf = data, + }; + + mutex_lock(&lp->bmux); + buf[0] = CMD_WRITE | CMD_FB; + buf[1] = len + 2; /* 2 bytes for CRC that isn't written */ + + dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); + dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); + + spi_message_init(&msg); + spi_message_add_tail(&xfer_head, &msg); + spi_message_add_tail(&xfer_buf, &msg); + + status = spi_sync(lp->spi, &msg); + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + if (msg.status) + status = msg.status; + + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); + dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); + + mutex_unlock(&lp->bmux); + return status; +} + +static int +at86rf230_read_fbuf(struct at86rf230_local *lp, u8 *data, u8 *len, u8 *lqi) +{ + u8 *buf = lp->buf; + int status; + struct spi_message msg; + struct spi_transfer xfer_head = { + .len = 2, + .tx_buf = buf, + .rx_buf = buf, + }; + struct spi_transfer xfer_head1 = { + .len = 2, + .tx_buf = buf, + .rx_buf = buf, + }; + struct spi_transfer xfer_buf = { + .len = 0, + .rx_buf = data, + }; + + mutex_lock(&lp->bmux); + + buf[0] = CMD_FB; + buf[1] = 0x00; + + spi_message_init(&msg); + spi_message_add_tail(&xfer_head, &msg); + + status = spi_sync(lp->spi, &msg); + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + + xfer_buf.len = *(buf + 1) + 1; + *len = buf[1]; + + buf[0] = CMD_FB; + buf[1] = 0x00; + + spi_message_init(&msg); + spi_message_add_tail(&xfer_head1, &msg); + spi_message_add_tail(&xfer_buf, &msg); + + status = spi_sync(lp->spi, &msg); + + if (msg.status) + status = msg.status; + + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); + dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); + + if (status) { + if (lqi && (*len > lp->buf[1])) + *lqi = data[lp->buf[1]]; + } + mutex_unlock(&lp->bmux); + + return status; +} + +static int +at86rf230_ed(struct ieee802154_dev *dev, u8 *level) +{ + might_sleep(); + BUG_ON(!level); + *level = 0xbe; + return 0; +} + +static int +at86rf230_state(struct ieee802154_dev *dev, int state) +{ + struct at86rf230_local *lp = dev->priv; + int rc; + u8 val; + u8 desired_status; + + might_sleep(); + + if (state == STATE_FORCE_TX_ON) + desired_status = STATE_TX_ON; + else if (state == STATE_FORCE_TRX_OFF) + desired_status = STATE_TRX_OFF; + else + desired_status = state; + + do { + rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &val); + if (rc) + goto err; + } while (val == STATE_TRANSITION_IN_PROGRESS); + + if (val == desired_status) + return 0; + + /* state is equal to phy states */ + rc = at86rf230_write_subreg(lp, SR_TRX_CMD, state); + if (rc) + goto err; + + do { + rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &val); + if (rc) + goto err; + } while (val == STATE_TRANSITION_IN_PROGRESS); + + + if (val == desired_status) + return 0; + + pr_err("unexpected state change: %d, asked for %d\n", val, state); + return -EBUSY; + +err: + pr_err("error: %d\n", rc); + return rc; +} + +static int +at86rf230_start(struct ieee802154_dev *dev) +{ + struct at86rf230_local *lp = dev->priv; + u8 rc; + + rc = at86rf230_write_subreg(lp, SR_RX_SAFE_MODE, 1); + if (rc) + return rc; + + return at86rf230_state(dev, STATE_RX_ON); +} + +static void +at86rf230_stop(struct ieee802154_dev *dev) +{ + at86rf230_state(dev, STATE_FORCE_TRX_OFF); +} + +static int +at86rf230_channel(struct ieee802154_dev *dev, int page, int channel) +{ + struct at86rf230_local *lp = dev->priv; + int rc; + + might_sleep(); + + if (page != 0 || channel < 11 || channel > 26) { + WARN_ON(1); + return -EINVAL; + } + + rc = at86rf230_write_subreg(lp, SR_CHANNEL, channel); + msleep(1); /* Wait for PLL */ + dev->phy->current_channel = channel; + + return 0; +} + +static int +at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb) +{ + struct at86rf230_local *lp = dev->priv; + int rc; + unsigned long flags; + + might_sleep(); + + rc = at86rf230_state(dev, STATE_FORCE_TX_ON); + if (rc) + goto err; + + spin_lock_irqsave(&lp->lock, flags); + lp->is_tx = 1; + INIT_COMPLETION(lp->tx_complete); + spin_unlock_irqrestore(&lp->lock, flags); + + rc = at86rf230_write_fbuf(lp, skb->data, skb->len); + if (rc) + goto err_rx; + + rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_BUSY_TX); + if (rc) + goto err_rx; + + rc = wait_for_completion_interruptible(&lp->tx_complete); + if (rc < 0) + goto err_rx; + + rc = at86rf230_start(dev); + + return rc; + +err_rx: + at86rf230_start(dev); +err: + pr_err("error: %d\n", rc); + + spin_lock_irqsave(&lp->lock, flags); + lp->is_tx = 0; + spin_unlock_irqrestore(&lp->lock, flags); + + return rc; +} + +static int at86rf230_rx(struct at86rf230_local *lp) +{ + u8 len = 128, lqi = 0; + int rc; + struct sk_buff *skb; + + skb = alloc_skb(len, GFP_KERNEL); + + if (!skb) + return -ENOMEM; + + if (at86rf230_write_subreg(lp, SR_RX_PDT_DIS, 1) || + at86rf230_read_fbuf(lp, skb_put(skb, len), &len, &lqi) || + at86rf230_write_subreg(lp, SR_RX_SAFE_MODE, 1) || + at86rf230_write_subreg(lp, SR_RX_PDT_DIS, 0)) { + goto err; + } + + if (len < 2) + goto err; + + skb_trim(skb, len - 2); /* We do not put CRC into the frame */ + + ieee802154_rx_irqsafe(lp->dev, skb, lqi); + + dev_dbg(&lp->spi->dev, "READ_FBUF: %d %d %x\n", rc, len, lqi); + + return 0; +err: + pr_debug("received frame is too small\n"); + + kfree_skb(skb); + return -EINVAL; +} + +static struct ieee802154_ops at86rf230_ops = { + .owner = THIS_MODULE, + .xmit = at86rf230_xmit, + .ed = at86rf230_ed, + .set_channel = at86rf230_channel, + .start = at86rf230_start, + .stop = at86rf230_stop, +}; + +static void at86rf230_irqwork(struct work_struct *work) +{ + struct at86rf230_local *lp = + container_of(work, struct at86rf230_local, irqwork); + u8 status = 0, val; + int rc; + unsigned long flags; + + spin_lock_irqsave(&lp->lock, flags); + rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &val); + status |= val; + + status &= ~IRQ_PLL_LOCK; /* ignore */ + status &= ~IRQ_RX_START; /* ignore */ + status &= ~IRQ_AMI; /* ignore */ + status &= ~IRQ_TRX_UR; /* FIXME: possibly handle ???*/ + + if (status & IRQ_TRX_END) { + status &= ~IRQ_TRX_END; + if (lp->is_tx) { + lp->is_tx = 0; + complete(&lp->tx_complete); + } else { + at86rf230_rx(lp); + } + } + + if (lp->irq_disabled) { + lp->irq_disabled = 0; + enable_irq(lp->spi->irq); + } + spin_unlock_irqrestore(&lp->lock, flags); +} + +static irqreturn_t at86rf230_isr(int irq, void *data) +{ + struct at86rf230_local *lp = data; + + spin_lock(&lp->lock); + if (!lp->irq_disabled) { + disable_irq_nosync(irq); + lp->irq_disabled = 1; + } + spin_unlock(&lp->lock); + + schedule_work(&lp->irqwork); + + return IRQ_HANDLED; +} + + +static int at86rf230_hw_init(struct at86rf230_local *lp) +{ + u8 status; + int rc; + + rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status); + if (rc) + return rc; + + dev_info(&lp->spi->dev, "Status: %02x\n", status); + if (status == STATE_P_ON) { + rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TRX_OFF); + if (rc) + return rc; + msleep(1); + rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status); + if (rc) + return rc; + dev_info(&lp->spi->dev, "Status: %02x\n", status); + } + + rc = at86rf230_write_subreg(lp, SR_IRQ_MASK, 0xff); /* IRQ_TRX_UR | + * IRQ_CCA_ED | + * IRQ_TRX_END | + * IRQ_PLL_UNL | + * IRQ_PLL_LOCK + */ + if (rc) + return rc; + + /* CLKM changes are applied immediately */ + rc = at86rf230_write_subreg(lp, SR_CLKM_SHA_SEL, 0x00); + if (rc) + return rc; + + /* Turn CLKM Off */ + rc = at86rf230_write_subreg(lp, SR_CLKM_CTRL, 0x00); + if (rc) + return rc; + /* Wait the next SLEEP cycle */ + msleep(100); + + rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TX_ON); + if (rc) + return rc; + msleep(1); + + rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status); + if (rc) + return rc; + dev_info(&lp->spi->dev, "Status: %02x\n", status); + + rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &status); + if (rc) + return rc; + if (!status) { + dev_err(&lp->spi->dev, "DVDD error\n"); + return -EINVAL; + } + + rc = at86rf230_read_subreg(lp, SR_AVDD_OK, &status); + if (rc) + return rc; + if (!status) { + dev_err(&lp->spi->dev, "AVDD error\n"); + return -EINVAL; + } + + return 0; +} + +static int at86rf230_suspend(struct spi_device *spi, pm_message_t message) +{ + return 0; +} + +static int at86rf230_resume(struct spi_device *spi) +{ + return 0; +} + +static int at86rf230_fill_data(struct spi_device *spi) +{ + struct at86rf230_local *lp = spi_get_drvdata(spi); + struct at86rf230_platform_data *pdata = spi->dev.platform_data; + + if (!pdata) { + dev_err(&spi->dev, "no platform_data\n"); + return -EINVAL; + } + + lp->rstn = pdata->rstn; + lp->slp_tr = pdata->slp_tr; + lp->dig2 = pdata->dig2; + + return 0; +} + +static int __devinit at86rf230_probe(struct spi_device *spi) +{ + struct ieee802154_dev *dev; + struct at86rf230_local *lp; + u8 man_id_0, man_id_1; + int rc; + const char *chip; + int supported = 0; + + if (!spi->irq) { + dev_err(&spi->dev, "no IRQ specified\n"); + return -EINVAL; + } + + dev = ieee802154_alloc_device(sizeof(*lp), &at86rf230_ops); + if (!dev) + return -ENOMEM; + + lp = dev->priv; + lp->dev = dev; + + lp->spi = spi; + + dev->priv = lp; + dev->parent = &spi->dev; + dev->extra_tx_headroom = 0; + /* We do support only 2.4 Ghz */ + dev->phy->channels_supported[0] = 0x7FFF800; + dev->flags = IEEE802154_HW_OMIT_CKSUM; + + mutex_init(&lp->bmux); + INIT_WORK(&lp->irqwork, at86rf230_irqwork); + spin_lock_init(&lp->lock); + init_completion(&lp->tx_complete); + + spi_set_drvdata(spi, lp); + + rc = at86rf230_fill_data(spi); + if (rc) + goto err_fill; + + rc = gpio_request(lp->rstn, "rstn"); + if (rc) + goto err_rstn; + + if (gpio_is_valid(lp->slp_tr)) { + rc = gpio_request(lp->slp_tr, "slp_tr"); + if (rc) + goto err_slp_tr; + } + + rc = gpio_direction_output(lp->rstn, 1); + if (rc) + goto err_gpio_dir; + + if (gpio_is_valid(lp->slp_tr)) { + rc = gpio_direction_output(lp->slp_tr, 0); + if (rc) + goto err_gpio_dir; + } + + /* Reset */ + msleep(1); + gpio_set_value(lp->rstn, 0); + msleep(1); + gpio_set_value(lp->rstn, 1); + msleep(1); + + rc = at86rf230_read_subreg(lp, SR_MAN_ID_0, &man_id_0); + if (rc) + goto err_gpio_dir; + rc = at86rf230_read_subreg(lp, SR_MAN_ID_1, &man_id_1); + if (rc) + goto err_gpio_dir; + + if (man_id_1 != 0x00 || man_id_0 != 0x1f) { + dev_err(&spi->dev, "Non-Atmel dev found (MAN_ID %02x %02x)\n", + man_id_1, man_id_0); + rc = -EINVAL; + goto err_gpio_dir; + } + + rc = at86rf230_read_subreg(lp, SR_PART_NUM, &lp->part); + if (rc) + goto err_gpio_dir; + + rc = at86rf230_read_subreg(lp, SR_VERSION_NUM, &lp->vers); + if (rc) + goto err_gpio_dir; + + switch (lp->part) { + case 2: + chip = "at86rf230"; + /* supported = 1; FIXME: should be easy to support; */ + break; + case 3: + chip = "at86rf231"; + supported = 1; + break; + default: + chip = "UNKNOWN"; + break; + } + + dev_info(&spi->dev, "Detected %s chip version %d\n", chip, lp->vers); + if (!supported) { + rc = -ENOTSUPP; + goto err_gpio_dir; + } + + rc = at86rf230_hw_init(lp); + if (rc) + goto err_gpio_dir; + + rc = request_irq(spi->irq, at86rf230_isr, IRQF_SHARED, + dev_name(&spi->dev), lp); + if (rc) + goto err_gpio_dir; + + rc = ieee802154_register_device(lp->dev); + if (rc) + goto err_irq; + + return rc; + + ieee802154_unregister_device(lp->dev); +err_irq: + free_irq(spi->irq, lp); + flush_work(&lp->irqwork); +err_gpio_dir: + if (gpio_is_valid(lp->slp_tr)) + gpio_free(lp->slp_tr); +err_slp_tr: + gpio_free(lp->rstn); +err_rstn: +err_fill: + spi_set_drvdata(spi, NULL); + mutex_destroy(&lp->bmux); + ieee802154_free_device(lp->dev); + return rc; +} + +static int __devexit at86rf230_remove(struct spi_device *spi) +{ + struct at86rf230_local *lp = spi_get_drvdata(spi); + + ieee802154_unregister_device(lp->dev); + + free_irq(spi->irq, lp); + flush_work(&lp->irqwork); + + if (gpio_is_valid(lp->slp_tr)) + gpio_free(lp->slp_tr); + gpio_free(lp->rstn); + + spi_set_drvdata(spi, NULL); + mutex_destroy(&lp->bmux); + ieee802154_free_device(lp->dev); + + dev_dbg(&spi->dev, "unregistered at86rf230\n"); + return 0; +} + +static struct spi_driver at86rf230_driver = { + .driver = { + .name = "at86rf230", + .owner = THIS_MODULE, + }, + .probe = at86rf230_probe, + .remove = __devexit_p(at86rf230_remove), + .suspend = at86rf230_suspend, + .resume = at86rf230_resume, +}; + +static int __init at86rf230_init(void) +{ + return spi_register_driver(&at86rf230_driver); +} +module_init(at86rf230_init); + +static void __exit at86rf230_exit(void) +{ + spi_unregister_driver(&at86rf230_driver); +} +module_exit(at86rf230_exit); + +MODULE_DESCRIPTION("AT86RF230 Transceiver Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/spi/at86rf230.h b/include/linux/spi/at86rf230.h new file mode 100644 index 000000000000..b2b1afbb3202 --- /dev/null +++ b/include/linux/spi/at86rf230.h @@ -0,0 +1,31 @@ +/* + * AT86RF230/RF231 driver + * + * Copyright (C) 2009-2012 Siemens AG + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Written by: + * Dmitry Eremin-Solenikov + */ +#ifndef AT86RF230_H +#define AT86RF230_H + +struct at86rf230_platform_data { + int rstn; + int slp_tr; + int dig2; +}; + +#endif -- cgit v1.2.3-59-g8ed1b From 52a4fd77808662a16cd17ad3b0e1ad75e0162d8b Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 26 Jun 2012 06:52:46 +0000 Subject: team: do not allow to map disabled ports Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 5 ++--- drivers/net/team/team_mode_loadbalance.c | 3 ++- include/linux/if_team.h | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 6b4cf6eca238..5350eeaa22ce 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -614,8 +614,6 @@ static int team_change_mode(struct team *team, const char *kind) * Rx path frame handler ************************/ -static bool team_port_enabled(struct team_port *port); - /* note: already called with rcu_read_lock */ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb) { @@ -673,10 +671,11 @@ static bool team_port_find(const struct team *team, return false; } -static bool team_port_enabled(struct team_port *port) +bool team_port_enabled(struct team_port *port) { return port->index != -1; } +EXPORT_SYMBOL(team_port_enabled); /* * Enable/disable port by adding to enabled port hashlist and setting diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index c92fa02d6a63..51a4b199c75c 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -359,7 +359,8 @@ static int lb_tx_hash_to_port_mapping_set(struct team *team, unsigned char hash = ctx->info->array_index; list_for_each_entry(port, &team->port_list, list) { - if (ctx->data.u32_val == port->dev->ifindex) { + if (ctx->data.u32_val == port->dev->ifindex && + team_port_enabled(port)) { rcu_assign_pointer(LB_HTPM_PORT_BY_HASH(lb_priv, hash), port); return 0; diff --git a/include/linux/if_team.h b/include/linux/if_team.h index c1938869191f..e636a54e7a71 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -64,6 +64,8 @@ struct team_port { long mode_priv[0]; }; +extern bool team_port_enabled(struct team_port *port); + struct team_mode_ops { int (*init)(struct team *team); void (*exit)(struct team *team); -- cgit v1.2.3-59-g8ed1b From 4a9fbcc6d606ae7f6a4e65b8a2759f46be8d45c6 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 26 Jun 2012 06:52:47 +0000 Subject: team: remove unused rcu_head field from team_port struct Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/if_team.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/if_team.h b/include/linux/if_team.h index e636a54e7a71..99efd60fa8c9 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -60,7 +60,6 @@ struct team_port { unsigned int mtu; } orig; - struct rcu_head rcu; long mode_priv[0]; }; -- cgit v1.2.3-59-g8ed1b From c3deafc5261a9421b315ee0d0e8e36ba7857128c Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 26 Jun 2012 21:51:35 -0700 Subject: netlink: Delete NLMSG_PUT and NLMSG_NEW. No longer used and a poor interface as they were macros with embedded gotos. Signed-off-by: David S. Miller --- include/linux/netlink.h | 8 -------- 1 file changed, 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 0f628ffa420c..ed33f0901bc2 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -241,14 +241,6 @@ struct netlink_notify { struct nlmsghdr * __nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags); -#define NLMSG_NEW(skb, pid, seq, type, len, flags) \ -({ if (unlikely(skb_tailroom(skb) < (int)NLMSG_SPACE(len))) \ - goto nlmsg_failure; \ - __nlmsg_put(skb, pid, seq, type, len, flags); }) - -#define NLMSG_PUT(skb, pid, seq, type, len) \ - NLMSG_NEW(skb, pid, seq, type, len, 0) - struct netlink_dump_control { int (*dump)(struct sk_buff *skb, struct netlink_callback *); int (*done)(struct netlink_callback*); -- cgit v1.2.3-59-g8ed1b From 392025f87a5105c640cf1b4b317c21c14c05a6f9 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 26 Jun 2012 20:27:09 +0200 Subject: netfilter: ctnetlink: add new messages to obtain statistics This patch adds the following messages to ctnetlink: IPCTNL_MSG_CT_GET_STATS_CPU IPCTNL_MSG_CT_GET_STATS IPCTNL_MSG_EXP_GET_STATS_CPU To display connection tracking system per-cpu and global statistics. This provides a replacement for the following /proc interfaces: /proc/net/stat/nf_conntrack /proc/sys/net/netfilter/nf_conntrack_count Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nfnetlink_conntrack.h | 38 +++++ net/netfilter/nf_conntrack_netlink.c | 227 +++++++++++++++++++++++++- 2 files changed, 264 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index 768883370080..f649f7423ca2 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -7,6 +7,8 @@ enum cntl_msg_types { IPCTNL_MSG_CT_GET, IPCTNL_MSG_CT_DELETE, IPCTNL_MSG_CT_GET_CTRZERO, + IPCTNL_MSG_CT_GET_STATS_CPU, + IPCTNL_MSG_CT_GET_STATS, IPCTNL_MSG_MAX }; @@ -15,6 +17,7 @@ enum ctnl_exp_msg_types { IPCTNL_MSG_EXP_NEW, IPCTNL_MSG_EXP_GET, IPCTNL_MSG_EXP_DELETE, + IPCTNL_MSG_EXP_GET_STATS_CPU, IPCTNL_MSG_EXP_MAX }; @@ -203,4 +206,39 @@ enum ctattr_secctx { }; #define CTA_SECCTX_MAX (__CTA_SECCTX_MAX - 1) +enum ctattr_stats_cpu { + CTA_STATS_UNSPEC, + CTA_STATS_SEARCHED, + CTA_STATS_FOUND, + CTA_STATS_NEW, + CTA_STATS_INVALID, + CTA_STATS_IGNORE, + CTA_STATS_DELETE, + CTA_STATS_DELETE_LIST, + CTA_STATS_INSERT, + CTA_STATS_INSERT_FAILED, + CTA_STATS_DROP, + CTA_STATS_EARLY_DROP, + CTA_STATS_ERROR, + CTA_STATS_SEARCH_RESTART, + __CTA_STATS_MAX, +}; +#define CTA_STATS_MAX (__CTA_STATS_MAX - 1) + +enum ctattr_stats_global { + CTA_STATS_GLOBAL_UNSPEC, + CTA_STATS_GLOBAL_ENTRIES, + __CTA_STATS_GLOBAL_MAX, +}; +#define CTA_STATS_GLOBAL_MAX (__CTA_STATS_GLOBAL_MAX - 1) + +enum ctattr_expect_stats { + CTA_STATS_EXP_UNSPEC, + CTA_STATS_EXP_NEW, + CTA_STATS_EXP_CREATE, + CTA_STATS_EXP_DELETE, + __CTA_STATS_EXP_MAX, +}; +#define CTA_STATS_EXP_MAX (__CTA_STATS_EXP_MAX - 1) + #endif /* _IPCONNTRACK_NETLINK_H */ diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index b9b8f4ac7a36..14f67a2cbcb5 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -4,7 +4,7 @@ * (C) 2001 by Jay Schulist * (C) 2002-2006 by Harald Welte * (C) 2003 by Patrick Mchardy - * (C) 2005-2011 by Pablo Neira Ayuso + * (C) 2005-2012 by Pablo Neira Ayuso * * Initial connection tracking via netlink development funded and * generally made possible by Network Robots, Inc. (www.networkrobots.com) @@ -1627,6 +1627,155 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, return err; } +static int +ctnetlink_ct_stat_cpu_fill_info(struct sk_buff *skb, u32 pid, u32 seq, + __u16 cpu, const struct ip_conntrack_stat *st) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfmsg; + unsigned int flags = pid ? NLM_F_MULTI : 0, event; + + event = (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_GET_STATS_CPU); + nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); + if (nlh == NULL) + goto nlmsg_failure; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = AF_UNSPEC; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = htons(cpu); + + if (nla_put_be32(skb, CTA_STATS_SEARCHED, htonl(st->searched)) || + nla_put_be32(skb, CTA_STATS_FOUND, htonl(st->found)) || + nla_put_be32(skb, CTA_STATS_NEW, htonl(st->new)) || + nla_put_be32(skb, CTA_STATS_INVALID, htonl(st->invalid)) || + nla_put_be32(skb, CTA_STATS_IGNORE, htonl(st->ignore)) || + nla_put_be32(skb, CTA_STATS_DELETE, htonl(st->delete)) || + nla_put_be32(skb, CTA_STATS_DELETE_LIST, htonl(st->delete_list)) || + nla_put_be32(skb, CTA_STATS_INSERT, htonl(st->insert)) || + nla_put_be32(skb, CTA_STATS_INSERT_FAILED, + htonl(st->insert_failed)) || + nla_put_be32(skb, CTA_STATS_DROP, htonl(st->drop)) || + nla_put_be32(skb, CTA_STATS_EARLY_DROP, htonl(st->early_drop)) || + nla_put_be32(skb, CTA_STATS_ERROR, htonl(st->error)) || + nla_put_be32(skb, CTA_STATS_SEARCH_RESTART, + htonl(st->search_restart))) + goto nla_put_failure; + + nlmsg_end(skb, nlh); + return skb->len; + +nla_put_failure: +nlmsg_failure: + nlmsg_cancel(skb, nlh); + return -1; +} + +static int +ctnetlink_ct_stat_cpu_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int cpu; + struct net *net = sock_net(skb->sk); + + if (cb->args[0] == nr_cpu_ids) + return 0; + + for (cpu = cb->args[0]; cpu < nr_cpu_ids; cpu++) { + const struct ip_conntrack_stat *st; + + if (!cpu_possible(cpu)) + continue; + + st = per_cpu_ptr(net->ct.stat, cpu); + if (ctnetlink_ct_stat_cpu_fill_info(skb, + NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, + cpu, st) < 0) + break; + } + cb->args[0] = cpu; + + return skb->len; +} + +static int +ctnetlink_stat_ct_cpu(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]) +{ + if (nlh->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = ctnetlink_ct_stat_cpu_dump, + }; + return netlink_dump_start(ctnl, skb, nlh, &c); + } + + return 0; +} + +static int +ctnetlink_stat_ct_fill_info(struct sk_buff *skb, u32 pid, u32 seq, u32 type, + struct net *net) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfmsg; + unsigned int flags = pid ? NLM_F_MULTI : 0, event; + unsigned int nr_conntracks = atomic_read(&net->ct.count); + + event = (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_GET_STATS); + nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); + if (nlh == NULL) + goto nlmsg_failure; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = AF_UNSPEC; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = 0; + + if (nla_put_be32(skb, CTA_STATS_GLOBAL_ENTRIES, htonl(nr_conntracks))) + goto nla_put_failure; + + nlmsg_end(skb, nlh); + return skb->len; + +nla_put_failure: +nlmsg_failure: + nlmsg_cancel(skb, nlh); + return -1; +} + +static int +ctnetlink_stat_ct(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]) +{ + struct sk_buff *skb2; + int err; + + skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (skb2 == NULL) + return -ENOMEM; + + err = ctnetlink_stat_ct_fill_info(skb2, NETLINK_CB(skb).pid, + nlh->nlmsg_seq, + NFNL_MSG_TYPE(nlh->nlmsg_type), + sock_net(skb->sk)); + if (err <= 0) + goto free; + + err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); + if (err < 0) + goto out; + + return 0; + +free: + kfree_skb(skb2); +out: + /* this avoids a loop in nfnetlink. */ + return err == -EAGAIN ? -ENOBUFS : err; +} + #ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT static size_t ctnetlink_nfqueue_build_size(const struct nf_conn *ct) @@ -2440,6 +2589,79 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb, return err; } +static int +ctnetlink_exp_stat_fill_info(struct sk_buff *skb, u32 pid, u32 seq, int cpu, + const struct ip_conntrack_stat *st) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfmsg; + unsigned int flags = pid ? NLM_F_MULTI : 0, event; + + event = (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_EXP_GET_STATS_CPU); + nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); + if (nlh == NULL) + goto nlmsg_failure; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = AF_UNSPEC; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = htons(cpu); + + if (nla_put_be32(skb, CTA_STATS_EXP_NEW, htonl(st->expect_new)) || + nla_put_be32(skb, CTA_STATS_EXP_CREATE, htonl(st->expect_create)) || + nla_put_be32(skb, CTA_STATS_EXP_DELETE, htonl(st->expect_delete))) + goto nla_put_failure; + + nlmsg_end(skb, nlh); + return skb->len; + +nla_put_failure: +nlmsg_failure: + nlmsg_cancel(skb, nlh); + return -1; +} + +static int +ctnetlink_exp_stat_cpu_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int cpu; + struct net *net = sock_net(skb->sk); + + if (cb->args[0] == nr_cpu_ids) + return 0; + + for (cpu = cb->args[0]; cpu < nr_cpu_ids; cpu++) { + const struct ip_conntrack_stat *st; + + if (!cpu_possible(cpu)) + continue; + + st = per_cpu_ptr(net->ct.stat, cpu); + if (ctnetlink_exp_stat_fill_info(skb, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, + cpu, st) < 0) + break; + } + cb->args[0] = cpu; + + return skb->len; +} + +static int +ctnetlink_stat_exp_cpu(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]) +{ + if (nlh->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = ctnetlink_exp_stat_cpu_dump, + }; + return netlink_dump_start(ctnl, skb, nlh, &c); + } + + return 0; +} + #ifdef CONFIG_NF_CONNTRACK_EVENTS static struct nf_ct_event_notifier ctnl_notifier = { .fcn = ctnetlink_conntrack_event, @@ -2463,6 +2685,8 @@ static const struct nfnl_callback ctnl_cb[IPCTNL_MSG_MAX] = { [IPCTNL_MSG_CT_GET_CTRZERO] = { .call = ctnetlink_get_conntrack, .attr_count = CTA_MAX, .policy = ct_nla_policy }, + [IPCTNL_MSG_CT_GET_STATS_CPU] = { .call = ctnetlink_stat_ct_cpu }, + [IPCTNL_MSG_CT_GET_STATS] = { .call = ctnetlink_stat_ct }, }; static const struct nfnl_callback ctnl_exp_cb[IPCTNL_MSG_EXP_MAX] = { @@ -2475,6 +2699,7 @@ static const struct nfnl_callback ctnl_exp_cb[IPCTNL_MSG_EXP_MAX] = { [IPCTNL_MSG_EXP_DELETE] = { .call = ctnetlink_del_expect, .attr_count = CTA_EXPECT_MAX, .policy = exp_nla_policy }, + [IPCTNL_MSG_EXP_GET_STATS_CPU] = { .call = ctnetlink_stat_exp_cpu }, }; static const struct nfnetlink_subsystem ctnl_subsys = { -- cgit v1.2.3-59-g8ed1b From d2bb2b9e9161d221badafae063d0c62b41bf3078 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Mon, 25 Jun 2012 22:12:20 +0200 Subject: bcma: define some additional cores IDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of them are BCM4706 specific AFAWK. Most of them was confirmed on Netgear WNDR450. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/scan.c | 6 ++++++ include/linux/bcma/bcma.h | 7 +++++++ 2 files changed, 13 insertions(+) (limited to 'include/linux') diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c index 5ed0718fc660..a3420585d942 100644 --- a/drivers/bcma/scan.c +++ b/drivers/bcma/scan.c @@ -28,6 +28,12 @@ static const struct bcma_device_id_name bcma_arm_device_names[] = { static const struct bcma_device_id_name bcma_bcm_device_names[] = { { BCMA_CORE_OOB_ROUTER, "OOB Router" }, + { BCMA_CORE_4706_CHIPCOMMON, "BCM4706 ChipCommon" }, + { BCMA_CORE_4706_SOC_RAM, "BCM4706 SOC RAM" }, + { BCMA_CORE_4706_MAC_GBIT, "BCM4706 GBit MAC" }, + { BCMA_CORE_AMEMC, "AMEMC (DDR)" }, + { BCMA_CORE_ALTA, "ALTA (I2S)" }, + { BCMA_CORE_4706_MAC_GBIT_COMMON, "BCM4706 GBit MAC Common" }, { BCMA_CORE_INVALID, "Invalid" }, { BCMA_CORE_CHIPCOMMON, "ChipCommon" }, { BCMA_CORE_ILINE20, "ILine 20" }, diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 8deaf6d050c3..12334f9190cb 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -70,6 +70,13 @@ struct bcma_host_ops { /* Core-ID values. */ #define BCMA_CORE_OOB_ROUTER 0x367 /* Out of band */ +#define BCMA_CORE_4706_CHIPCOMMON 0x500 +#define BCMA_CORE_4706_SOC_RAM 0x50E +#define BCMA_CORE_4706_MAC_GBIT 0x52D +#define BCMA_CORE_AMEMC 0x52E /* DDR1/2 memory controller core */ +#define BCMA_CORE_ALTA 0x534 /* I2S core */ +#define BCMA_CORE_4706_MAC_GBIT_COMMON 0x5DC +#define BCMA_CORE_DDR23_PHY 0x5DD #define BCMA_CORE_INVALID 0x700 #define BCMA_CORE_CHIPCOMMON 0x800 #define BCMA_CORE_ILINE20 0x801 -- cgit v1.2.3-59-g8ed1b From 4c3af034fafeb7269176bf1310c9bcff0b9fd9bb Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 26 Jun 2012 23:36:16 +0000 Subject: netlink: Get rid of obsolete rtnetlink macros Removes all RTA_GET*() and RTA_PUT*() variations, as well as the the unused rtattr_strcmp(). Get rid of rtm_get_table() by moving it to its only user decnet. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- include/linux/rtnetlink.h | 129 ---------------------------------------------- net/core/rtnetlink.c | 13 ----- net/decnet/dn_fib.c | 8 +++ 3 files changed, 8 insertions(+), 142 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 2c1de8982c85..ea60b0854109 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -612,12 +612,6 @@ struct tcamsg { #include #include -static __inline__ int rtattr_strcmp(const struct rtattr *rta, const char *str) -{ - int len = strlen(str) + 1; - return len > rta->rta_len || memcmp(RTA_DATA(rta), str, len); -} - extern int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, u32 group, int echo); extern int rtnl_unicast(struct sk_buff *skb, struct net *net, u32 pid); extern void rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, @@ -628,122 +622,6 @@ extern int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id, u32 ts, u32 tsage, long expires, u32 error); -extern void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data); - -#define RTA_PUT(skb, attrtype, attrlen, data) \ -({ if (unlikely(skb_tailroom(skb) < (int)RTA_SPACE(attrlen))) \ - goto rtattr_failure; \ - __rta_fill(skb, attrtype, attrlen, data); }) - -#define RTA_APPEND(skb, attrlen, data) \ -({ if (unlikely(skb_tailroom(skb) < (int)(attrlen))) \ - goto rtattr_failure; \ - memcpy(skb_put(skb, attrlen), data, attrlen); }) - -#define RTA_PUT_NOHDR(skb, attrlen, data) \ -({ RTA_APPEND(skb, RTA_ALIGN(attrlen), data); \ - memset(skb_tail_pointer(skb) - (RTA_ALIGN(attrlen) - attrlen), 0, \ - RTA_ALIGN(attrlen) - attrlen); }) - -#define RTA_PUT_U8(skb, attrtype, value) \ -({ u8 _tmp = (value); \ - RTA_PUT(skb, attrtype, sizeof(u8), &_tmp); }) - -#define RTA_PUT_U16(skb, attrtype, value) \ -({ u16 _tmp = (value); \ - RTA_PUT(skb, attrtype, sizeof(u16), &_tmp); }) - -#define RTA_PUT_U32(skb, attrtype, value) \ -({ u32 _tmp = (value); \ - RTA_PUT(skb, attrtype, sizeof(u32), &_tmp); }) - -#define RTA_PUT_U64(skb, attrtype, value) \ -({ u64 _tmp = (value); \ - RTA_PUT(skb, attrtype, sizeof(u64), &_tmp); }) - -#define RTA_PUT_SECS(skb, attrtype, value) \ - RTA_PUT_U64(skb, attrtype, (value) / HZ) - -#define RTA_PUT_MSECS(skb, attrtype, value) \ - RTA_PUT_U64(skb, attrtype, jiffies_to_msecs(value)) - -#define RTA_PUT_STRING(skb, attrtype, value) \ - RTA_PUT(skb, attrtype, strlen(value) + 1, value) - -#define RTA_PUT_FLAG(skb, attrtype) \ - RTA_PUT(skb, attrtype, 0, NULL); - -#define RTA_NEST(skb, type) \ -({ struct rtattr *__start = (struct rtattr *)skb_tail_pointer(skb); \ - RTA_PUT(skb, type, 0, NULL); \ - __start; }) - -#define RTA_NEST_END(skb, start) \ -({ (start)->rta_len = skb_tail_pointer(skb) - (unsigned char *)(start); \ - (skb)->len; }) - -#define RTA_NEST_COMPAT(skb, type, attrlen, data) \ -({ struct rtattr *__start = (struct rtattr *)skb_tail_pointer(skb); \ - RTA_PUT(skb, type, attrlen, data); \ - RTA_NEST(skb, type); \ - __start; }) - -#define RTA_NEST_COMPAT_END(skb, start) \ -({ struct rtattr *__nest = (void *)(start) + NLMSG_ALIGN((start)->rta_len); \ - (start)->rta_len = skb_tail_pointer(skb) - (unsigned char *)(start); \ - RTA_NEST_END(skb, __nest); \ - (skb)->len; }) - -#define RTA_NEST_CANCEL(skb, start) \ -({ if (start) \ - skb_trim(skb, (unsigned char *) (start) - (skb)->data); \ - -1; }) - -#define RTA_GET_U8(rta) \ -({ if (!rta || RTA_PAYLOAD(rta) < sizeof(u8)) \ - goto rtattr_failure; \ - *(u8 *) RTA_DATA(rta); }) - -#define RTA_GET_U16(rta) \ -({ if (!rta || RTA_PAYLOAD(rta) < sizeof(u16)) \ - goto rtattr_failure; \ - *(u16 *) RTA_DATA(rta); }) - -#define RTA_GET_U32(rta) \ -({ if (!rta || RTA_PAYLOAD(rta) < sizeof(u32)) \ - goto rtattr_failure; \ - *(u32 *) RTA_DATA(rta); }) - -#define RTA_GET_U64(rta) \ -({ u64 _tmp; \ - if (!rta || RTA_PAYLOAD(rta) < sizeof(u64)) \ - goto rtattr_failure; \ - memcpy(&_tmp, RTA_DATA(rta), sizeof(_tmp)); \ - _tmp; }) - -#define RTA_GET_FLAG(rta) (!!(rta)) - -#define RTA_GET_SECS(rta) ((unsigned long) RTA_GET_U64(rta) * HZ) -#define RTA_GET_MSECS(rta) (msecs_to_jiffies((unsigned long) RTA_GET_U64(rta))) - -static inline struct rtattr * -__rta_reserve(struct sk_buff *skb, int attrtype, int attrlen) -{ - struct rtattr *rta; - int size = RTA_LENGTH(attrlen); - - rta = (struct rtattr*)skb_put(skb, RTA_ALIGN(size)); - rta->rta_type = attrtype; - rta->rta_len = size; - memset(RTA_DATA(rta) + attrlen, 0, RTA_ALIGN(size) - size); - return rta; -} - -#define __RTA_PUT(skb, attrtype, attrlen) \ -({ if (unlikely(skb_tailroom(skb) < (int)RTA_SPACE(attrlen))) \ - goto rtattr_failure; \ - __rta_reserve(skb, attrtype, attrlen); }) - extern void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change); /* RTNL is used as a global lock for all changes to network configuration */ @@ -794,13 +672,6 @@ extern void __rtnl_unlock(void); } \ } while(0) -static inline u32 rtm_get_table(struct rtattr **rta, u8 table) -{ - return RTA_GET_U32(rta[RTA_TABLE-1]); -rtattr_failure: - return table; -} - extern int ndo_dflt_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 21318d15bbc3..bc8a1cdaac98 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -541,19 +541,6 @@ static const int rta_max[RTM_NR_FAMILIES] = [RTM_FAM(RTM_NEWACTION)] = TCAA_MAX, }; -void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data) -{ - struct rtattr *rta; - int size = RTA_LENGTH(attrlen); - - rta = (struct rtattr *)skb_put(skb, RTA_ALIGN(size)); - rta->rta_type = attrtype; - rta->rta_len = size; - memcpy(RTA_DATA(rta), data, attrlen); - memset(RTA_DATA(rta) + attrlen, 0, RTA_ALIGN(size) - size); -} -EXPORT_SYMBOL(__rta_fill); - int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned int group, int echo) { struct sock *rtnl = net->rtnl; diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c index 7eaf98799729..102d6106a942 100644 --- a/net/decnet/dn_fib.c +++ b/net/decnet/dn_fib.c @@ -505,6 +505,14 @@ static int dn_fib_check_attr(struct rtmsg *r, struct rtattr **rta) return 0; } +static inline u32 rtm_get_table(struct rtattr **rta, u8 table) +{ + if (rta[RTA_TABLE - 1]) + table = nla_get_u32((struct nlattr *) rta[RTA_TABLE - 1]); + + return table; +} + static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct net *net = sock_net(skb->sk); -- cgit v1.2.3-59-g8ed1b From e440cf2ca0a1b075c64016240d46c3aa9d877bbf Mon Sep 17 00:00:00 2001 From: "parav.pandit@emulex.com" Date: Wed, 27 Jun 2012 03:56:12 +0000 Subject: net: added support for 40GbE link. 1. removed code replication for tov calculation for 1G, 10G and made is common for speed > 1G (1G, 10G, 40G, 100G). 2. defines values for #4 different 40G Phys (KR4, LF4, SR4, CR4) Signed-off-by: Parav Pandit Reviewed-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/ethtool.h | 8 ++++++++ net/packet/af_packet.c | 18 ++++++------------ 2 files changed, 14 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 297370a6cb18..21eff418091b 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -1153,6 +1153,10 @@ struct ethtool_ops { #define SUPPORTED_10000baseR_FEC (1 << 20) #define SUPPORTED_20000baseMLD2_Full (1 << 21) #define SUPPORTED_20000baseKR2_Full (1 << 22) +#define SUPPORTED_40000baseKR4_Full (1 << 23) +#define SUPPORTED_40000baseCR4_Full (1 << 24) +#define SUPPORTED_40000baseSR4_Full (1 << 25) +#define SUPPORTED_40000baseLR4_Full (1 << 26) /* Indicates what features are advertised by the interface. */ #define ADVERTISED_10baseT_Half (1 << 0) @@ -1178,6 +1182,10 @@ struct ethtool_ops { #define ADVERTISED_10000baseR_FEC (1 << 20) #define ADVERTISED_20000baseMLD2_Full (1 << 21) #define ADVERTISED_20000baseKR2_Full (1 << 22) +#define ADVERTISED_40000baseKR4_Full (1 << 23) +#define ADVERTISED_40000baseCR4_Full (1 << 24) +#define ADVERTISED_40000baseSR4_Full (1 << 25) +#define ADVERTISED_40000baseLR4_Full (1 << 26) /* The following are all involved in forcing a particular link * mode for the device for setting things. When getting the diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 8a10d5b3c832..ceaca7c134a0 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -531,6 +531,7 @@ static int prb_calc_retire_blk_tmo(struct packet_sock *po, unsigned int mbits = 0, msec = 0, div = 0, tmo = 0; struct ethtool_cmd ecmd; int err; + u32 speed; rtnl_lock(); dev = __dev_get_by_index(sock_net(&po->sk), po->ifindex); @@ -539,25 +540,18 @@ static int prb_calc_retire_blk_tmo(struct packet_sock *po, return DEFAULT_PRB_RETIRE_TOV; } err = __ethtool_get_settings(dev, &ecmd); + speed = ethtool_cmd_speed(&ecmd); rtnl_unlock(); if (!err) { - switch (ecmd.speed) { - case SPEED_10000: - msec = 1; - div = 10000/1000; - break; - case SPEED_1000: - msec = 1; - div = 1000/1000; - break; /* * If the link speed is so slow you don't really * need to worry about perf anyways */ - case SPEED_100: - case SPEED_10: - default: + if (speed < SPEED_1000 || speed == SPEED_UNKNOWN) { return DEFAULT_PRB_RETIRE_TOV; + } else { + msec = 1; + div = speed / 1000; } } -- cgit v1.2.3-59-g8ed1b From ac28b9f8cd66d6bc54f8063df59e99abd62173a4 Mon Sep 17 00:00:00 2001 From: David Daney Date: Wed, 27 Jun 2012 07:33:35 +0000 Subject: netdev/phy: Handle IEEE802.3 clause 45 Ethernet PHYs The IEEE802.3 clause 45 MDIO bus protocol allows for directly addressing PHY registers using a 21 bit address, and is used by many 10G Ethernet PHYS. Already existing is the ability of MDIO bus drivers to use clause 45, with the MII_ADDR_C45 flag. Here we add struct phy_c45_device_ids to hold the device identifier registers present in clause 45. struct phy_device gets a couple of new fields: c45_ids to hold the identifiers and is_c45 to signal that it is clause 45. get_phy_device() gets a new parameter is_c45 to indicate that the PHY device should use the clause 45 protocol, and its callers are adjusted to pass false. The follow-on patch to of_mdio.c will pass true where appropriate. EXPORT phy_device_create() so that the follow-on patch to of_mdio.c can use it to create phy devices for PHYs, that have non-standard device identifier registers, based on the device tree bindings. Signed-off-by: David Daney Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 2 +- drivers/net/phy/phy_device.c | 105 +++++++++++++++++++++++++++++++++++++++---- drivers/of/of_mdio.c | 2 +- include/linux/phy.h | 18 +++++++- 4 files changed, 116 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 31470b0d0c32..2cee6d218d21 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -232,7 +232,7 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) struct phy_device *phydev; int err; - phydev = get_phy_device(bus, addr); + phydev = get_phy_device(bus, addr, false); if (IS_ERR(phydev) || phydev == NULL) return phydev; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 18ab0daf4490..ef4cdeebedd6 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -152,8 +152,8 @@ int phy_scan_fixups(struct phy_device *phydev) } EXPORT_SYMBOL(phy_scan_fixups); -static struct phy_device* phy_device_create(struct mii_bus *bus, - int addr, int phy_id) +struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, + bool is_c45, struct phy_c45_device_ids *c45_ids) { struct phy_device *dev; @@ -174,8 +174,11 @@ static struct phy_device* phy_device_create(struct mii_bus *bus, dev->autoneg = AUTONEG_ENABLE; + dev->is_c45 = is_c45; dev->addr = addr; dev->phy_id = phy_id; + if (c45_ids) + dev->c45_ids = *c45_ids; dev->bus = bus; dev->dev.parent = bus->parent; dev->dev.bus = &mdio_bus_type; @@ -200,20 +203,99 @@ static struct phy_device* phy_device_create(struct mii_bus *bus, return dev; } +EXPORT_SYMBOL(phy_device_create); + +/** + * get_phy_c45_ids - reads the specified addr for its 802.3-c45 IDs. + * @bus: the target MII bus + * @addr: PHY address on the MII bus + * @phy_id: where to store the ID retrieved. + * @c45_ids: where to store the c45 ID information. + * + * If the PHY devices-in-package appears to be valid, it and the + * corresponding identifiers are stored in @c45_ids, zero is stored + * in @phy_id. Otherwise 0xffffffff is stored in @phy_id. Returns + * zero on success. + * + */ +static int get_phy_c45_ids(struct mii_bus *bus, int addr, u32 *phy_id, + struct phy_c45_device_ids *c45_ids) { + int phy_reg; + int i, reg_addr; + const int num_ids = ARRAY_SIZE(c45_ids->device_ids); + + /* Find first non-zero Devices In package. Device + * zero is reserved, so don't probe it. + */ + for (i = 1; + i < num_ids && c45_ids->devices_in_package == 0; + i++) { + reg_addr = MII_ADDR_C45 | i << 16 | 6; + phy_reg = mdiobus_read(bus, addr, reg_addr); + if (phy_reg < 0) + return -EIO; + c45_ids->devices_in_package = (phy_reg & 0xffff) << 16; + + reg_addr = MII_ADDR_C45 | i << 16 | 5; + phy_reg = mdiobus_read(bus, addr, reg_addr); + if (phy_reg < 0) + return -EIO; + c45_ids->devices_in_package |= (phy_reg & 0xffff); + + /* If mostly Fs, there is no device there, + * let's get out of here. + */ + if ((c45_ids->devices_in_package & 0x1fffffff) == 0x1fffffff) { + *phy_id = 0xffffffff; + return 0; + } + } + + /* Now probe Device Identifiers for each device present. */ + for (i = 1; i < num_ids; i++) { + if (!(c45_ids->devices_in_package & (1 << i))) + continue; + + reg_addr = MII_ADDR_C45 | i << 16 | MII_PHYSID1; + phy_reg = mdiobus_read(bus, addr, reg_addr); + if (phy_reg < 0) + return -EIO; + c45_ids->device_ids[i] = (phy_reg & 0xffff) << 16; + + reg_addr = MII_ADDR_C45 | i << 16 | MII_PHYSID2; + phy_reg = mdiobus_read(bus, addr, reg_addr); + if (phy_reg < 0) + return -EIO; + c45_ids->device_ids[i] |= (phy_reg & 0xffff); + } + *phy_id = 0; + return 0; +} /** * get_phy_id - reads the specified addr for its ID. * @bus: the target MII bus * @addr: PHY address on the MII bus * @phy_id: where to store the ID retrieved. + * @is_c45: If true the PHY uses the 802.3 clause 45 protocol + * @c45_ids: where to store the c45 ID information. + * + * Description: In the case of a 802.3-c22 PHY, reads the ID registers + * of the PHY at @addr on the @bus, stores it in @phy_id and returns + * zero on success. + * + * In the case of a 802.3-c45 PHY, get_phy_c45_ids() is invoked, and + * its return value is in turn returned. * - * Description: Reads the ID registers of the PHY at @addr on the - * @bus, stores it in @phy_id and returns zero on success. */ -static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id) +static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id, + bool is_c45, struct phy_c45_device_ids *c45_ids) { int phy_reg; + if (is_c45) + return get_phy_c45_ids(bus, addr, phy_id, c45_ids); + /* Grab the bits from PHYIR1, and put them * in the upper half */ phy_reg = mdiobus_read(bus, addr, MII_PHYSID1); @@ -238,17 +320,19 @@ static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id) * get_phy_device - reads the specified PHY device and returns its @phy_device struct * @bus: the target MII bus * @addr: PHY address on the MII bus + * @is_c45: If true the PHY uses the 802.3 clause 45 protocol * * Description: Reads the ID registers of the PHY at @addr on the * @bus, then allocates and returns the phy_device to represent it. */ -struct phy_device * get_phy_device(struct mii_bus *bus, int addr) +struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) { struct phy_device *dev = NULL; u32 phy_id; + struct phy_c45_device_ids c45_ids = {0}; int r; - r = get_phy_id(bus, addr, &phy_id); + r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids); if (r) return ERR_PTR(r); @@ -256,7 +340,7 @@ struct phy_device * get_phy_device(struct mii_bus *bus, int addr) if ((phy_id & 0x1fffffff) == 0x1fffffff) return NULL; - dev = phy_device_create(bus, addr, phy_id); + dev = phy_device_create(bus, addr, phy_id, is_c45, &c45_ids); return dev; } @@ -449,6 +533,11 @@ static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, /* Assume that if there is no driver, that it doesn't * exist, and we should use the genphy driver. */ if (NULL == d->driver) { + if (phydev->is_c45) { + pr_err("No driver for phy %x\n", phydev->phy_id); + return -ENODEV; + } + d->driver = &genphy_driver.driver; err = d->driver->probe(d); diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 2574abde8d99..6c24cad322df 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -79,7 +79,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) mdio->irq[addr] = PHY_POLL; } - phy = get_phy_device(mdio, addr); + phy = get_phy_device(mdio, addr, false); if (!phy || IS_ERR(phy)) { dev_err(&mdio->dev, "error probing PHY at address %i\n", addr); diff --git a/include/linux/phy.h b/include/linux/phy.h index c291cae8ce32..597d05dd0fb4 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -243,6 +243,15 @@ enum phy_state { PHY_RESUMING }; +/** + * struct phy_c45_device_ids - 802.3-c45 Device Identifiers + * @devices_in_package: Bit vector of devices present. + * @device_ids: The device identifer for each present device. + */ +struct phy_c45_device_ids { + u32 devices_in_package; + u32 device_ids[8]; +}; /* phy_device: An instance of a PHY * @@ -250,6 +259,8 @@ enum phy_state { * bus: Pointer to the bus this PHY is on * dev: driver model device structure for this PHY * phy_id: UID for this device found during discovery + * c45_ids: 802.3-c45 Device Identifers if is_c45. + * is_c45: Set to true if this phy uses clause 45 addressing. * state: state of the PHY for management purposes * dev_flags: Device-specific flags used by the PHY driver. * addr: Bus address of PHY @@ -285,6 +296,9 @@ struct phy_device { u32 phy_id; + struct phy_c45_device_ids c45_ids; + bool is_c45; + enum phy_state state; u32 dev_flags; @@ -480,7 +494,9 @@ static inline int phy_write(struct phy_device *phydev, u32 regnum, u16 val) return mdiobus_write(phydev->bus, phydev->addr, regnum, val); } -struct phy_device* get_phy_device(struct mii_bus *bus, int addr); +struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, + bool is_c45, struct phy_c45_device_ids *c45_ids); +struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); int phy_device_register(struct phy_device *phy); int phy_init_hw(struct phy_device *phydev); struct phy_device * phy_attach(struct net_device *dev, -- cgit v1.2.3-59-g8ed1b From a30e2c1891296b5ee8de48430a07fdf8b818c661 Mon Sep 17 00:00:00 2001 From: David Daney Date: Wed, 27 Jun 2012 07:33:37 +0000 Subject: netdev/phy/of: Add more methods for binding PHY devices to drivers. Allow PHY drivers to supply their own device matching function (match_phy_device()), or to be matched OF compatible properties. PHYs following IEEE802.3 clause 45 have more than one device identifier constants, which breaks the default device matching code. Other 10G PHYs don't follow the standard manufacturer/device identifier register layout standards, but they do use the standard MDIO bus protocols for register access. Both of these require adjustments to the PHY driver to device matching code. If the there is an of_node associated with such a PHY, we can match it to its driver using the "compatible" properties, just as we do with certain platform devices. If the "compatible" property match fails, first check if there is a driver supplied matching function, and if not fall back to the existing identifier matching rules. Signed-off-by: David Daney Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 7 +++++++ include/linux/phy.h | 6 ++++++ 2 files changed, 13 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 2cee6d218d21..170eb411ab5d 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -308,6 +309,12 @@ static int mdio_bus_match(struct device *dev, struct device_driver *drv) struct phy_device *phydev = to_phy_device(dev); struct phy_driver *phydrv = to_phy_driver(drv); + if (of_driver_match_device(dev, drv)) + return 1; + + if (phydrv->match_phy_device) + return phydrv->match_phy_device(phydev); + return ((phydrv->phy_id & phydrv->phy_id_mask) == (phydev->phy_id & phydrv->phy_id_mask)); } diff --git a/include/linux/phy.h b/include/linux/phy.h index 597d05dd0fb4..7eac80a2557b 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -426,6 +426,12 @@ struct phy_driver { /* Clears up any memory if needed */ void (*remove)(struct phy_device *phydev); + /* Returns true if this is a suitable driver for the given + * phydev. If NULL, matching is based on phy_id and + * phy_id_mask. + */ + int (*match_phy_device)(struct phy_device *phydev); + /* Handles ethtool queries for hardware time stamping. */ int (*ts_info)(struct phy_device *phydev, struct ethtool_ts_info *ti); -- cgit v1.2.3-59-g8ed1b From 46028e6d10cbf9ccd5fb49aa0c23a430f314144c Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Fri, 15 Jun 2012 16:52:29 +0800 Subject: mm: cleanup on the comments of zone_reclaim_stat Signed-off-by: Wanpeng Li Acked-by: Minchan Kim Signed-off-by: Jiri Kosina --- include/linux/mmzone.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 4871e31ae277..54631776dff2 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -316,7 +316,7 @@ enum zone_type { struct zone_reclaim_stat { /* * The pageout code in vmscan.c keeps track of how many of the - * mem/swap backed and file backed pages are refeferenced. + * mem/swap backed and file backed pages are referenced. * The higher the rotated/scanned ratio, the more valuable * that cache is. * -- cgit v1.2.3-59-g8ed1b From ce0e169529a2db1cd910d2d45a5713fcdc29f6e1 Mon Sep 17 00:00:00 2001 From: Mahesh Palivela Date: Fri, 22 Jun 2012 07:27:46 +0000 Subject: wireless: add VHT (802.11ac) definitions Add the VHT definitions to be used by drivers supporting it. Signed-off-by: Mahesh Palivela Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 70 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 318fc1f705b1..abf0e5fe6d24 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1092,6 +1092,73 @@ struct ieee80211_ht_operation { #define WLAN_HT_SMPS_CONTROL_STATIC 1 #define WLAN_HT_SMPS_CONTROL_DYNAMIC 3 +#define VHT_MCS_SUPPORTED_SET_SIZE 8 + +struct ieee80211_vht_capabilities { + __le32 vht_capabilities_info; + u8 vht_supported_mcs_set[VHT_MCS_SUPPORTED_SET_SIZE]; +} __packed; + +struct ieee80211_vht_operation { + u8 vht_op_info_chwidth; + u8 vht_op_info_chan_center_freq_seg1_idx; + u8 vht_op_info_chan_center_freq_seg2_idx; + __le16 vht_basic_mcs_set; +} __packed; + +/** + * struct ieee80211_vht_mcs_info - VHT MCS information + * @rx_mcs_map: RX MCS map 2 bits for each stream, total 8 streams + * @rx_highest: Indicates highest long GI VHT PPDU data rate + * STA can receive. Rate expressed in units of 1 Mbps. + * If this field is 0 this value should not be used to + * consider the highest RX data rate supported. + * @tx_mcs_map: TX MCS map 2 bits for each stream, total 8 streams + * @tx_highest: Indicates highest long GI VHT PPDU data rate + * STA can transmit. Rate expressed in units of 1 Mbps. + * If this field is 0 this value should not be used to + * consider the highest TX data rate supported. + */ +struct ieee80211_vht_mcs_info { + __le16 rx_mcs_map; + __le16 rx_highest; + __le16 tx_mcs_map; + __le16 tx_highest; +} __packed; + +#define IEEE80211_VHT_MCS_ZERO_TO_SEVEN_SUPPORT 0 +#define IEEE80211_VHT_MCS_ZERO_TO_EIGHT_SUPPORT 1 +#define IEEE80211_VHT_MCS_ZERO_TO_NINE_SUPPORT 2 +#define IEEE80211_VHT_MCS_NOT_SUPPORTED 3 + +/* 802.11ac VHT Capabilities */ +#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 0x00000000 +#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 0x00000001 +#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 0x00000002 +#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ 0x00000004 +#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ 0x00000008 +#define IEEE80211_VHT_CAP_RXLDPC 0x00000010 +#define IEEE80211_VHT_CAP_SHORT_GI_80 0x00000020 +#define IEEE80211_VHT_CAP_SHORT_GI_160 0x00000040 +#define IEEE80211_VHT_CAP_TXSTBC 0x00000080 +#define IEEE80211_VHT_CAP_RXSTBC_1 0x00000100 +#define IEEE80211_VHT_CAP_RXSTBC_2 0x00000200 +#define IEEE80211_VHT_CAP_RXSTBC_3 0x00000300 +#define IEEE80211_VHT_CAP_RXSTBC_4 0x00000400 +#define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE 0x00000800 +#define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE 0x00001000 +#define IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX 0x00006000 +#define IEEE80211_VHT_CAP_SOUNDING_DIMENTION_MAX 0x00030000 +#define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE 0x00080000 +#define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE 0x00100000 +#define IEEE80211_VHT_CAP_VHT_TXOP_PS 0x00200000 +#define IEEE80211_VHT_CAP_HTC_VHT 0x00400000 +#define IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT 0x00800000 +#define IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB 0x08000000 +#define IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB 0x0c000000 +#define IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN 0x10000000 +#define IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN 0x20000000 + /* Authentication algorithms */ #define WLAN_AUTH_OPEN 0 #define WLAN_AUTH_SHARED_KEY 1 @@ -1352,6 +1419,9 @@ enum ieee80211_eid { WLAN_EID_DSE_REGISTERED_LOCATION = 58, WLAN_EID_SUPPORTED_REGULATORY_CLASSES = 59, WLAN_EID_EXT_CHANSWITCH_ANN = 60, + + WLAN_EID_VHT_CAPABILITY = 191, + WLAN_EID_VHT_OPERATION = 192, }; /* Action category code */ -- cgit v1.2.3-59-g8ed1b From bf0c111ec80355ee9fe2e2bdb609a536b54768d8 Mon Sep 17 00:00:00 2001 From: Mahesh Palivela Date: Fri, 22 Jun 2012 07:27:46 +0000 Subject: cfg80211: allow advertising VHT capabilities Allow drivers to advertise their VHT capabilities and export them to userspace via nl80211. Signed-off-by: Mahesh Palivela Signed-off-by: Johannes Berg --- include/linux/nl80211.h | 6 ++++++ include/net/cfg80211.h | 17 +++++++++++++++++ net/wireless/nl80211.c | 9 +++++++++ 3 files changed, 32 insertions(+) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index c0fc5d277338..23003272c70e 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1813,6 +1813,9 @@ enum nl80211_mpath_info { * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n + * @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as + * defined in 802.11ac + * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined * @__NL80211_BAND_ATTR_AFTER_LAST: internal use */ @@ -1826,6 +1829,9 @@ enum nl80211_band_attr { NL80211_BAND_ATTR_HT_AMPDU_FACTOR, NL80211_BAND_ATTR_HT_AMPDU_DENSITY, + NL80211_BAND_ATTR_VHT_MCS_SET, + NL80211_BAND_ATTR_VHT_CAPA, + /* keep last */ __NL80211_BAND_ATTR_AFTER_LAST, NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1 diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7d3cd3ce9a26..1fc89c4f930c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -210,6 +210,22 @@ struct ieee80211_sta_ht_cap { struct ieee80211_mcs_info mcs; }; +/** + * struct ieee80211_sta_vht_cap - STA's VHT capabilities + * + * This structure describes most essential parameters needed + * to describe 802.11ac VHT capabilities for an STA. + * + * @vht_supported: is VHT supported by the STA + * @cap: VHT capabilities map as described in 802.11ac spec + * @vht_mcs: Supported VHT MCS rates + */ +struct ieee80211_sta_vht_cap { + bool vht_supported; + u32 cap; /* use IEEE80211_VHT_CAP_ */ + struct ieee80211_vht_mcs_info vht_mcs; +}; + /** * struct ieee80211_supported_band - frequency band definition * @@ -233,6 +249,7 @@ struct ieee80211_supported_band { int n_channels; int n_bitrates; struct ieee80211_sta_ht_cap ht_cap; + struct ieee80211_sta_vht_cap vht_cap; }; /* diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 067c9fe02a7f..5c4a720f0442 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -921,6 +921,15 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, dev->wiphy.bands[band]->ht_cap.ampdu_density))) goto nla_put_failure; + /* add VHT info */ + if (dev->wiphy.bands[band]->vht_cap.vht_supported && + (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET, + sizeof(dev->wiphy.bands[band]->vht_cap.vht_mcs), + &dev->wiphy.bands[band]->vht_cap.vht_mcs) || + nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA, + dev->wiphy.bands[band]->vht_cap.cap))) + goto nla_put_failure; + /* add frequencies */ nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS); if (!nl_freqs) -- cgit v1.2.3-59-g8ed1b From 6d158a813efcd09661c23f16ddf7e2ff834cb20c Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 27 Jun 2012 20:46:14 -0400 Subject: tracing: Remove NR_CPUS array from trace_iterator Replace the NR_CPUS array of buffer_iter from the trace_iterator with an allocated array. This will just create an array of possible CPUS instead of the max number specified. The use of NR_CPUS in that array caused allocation failures for machines that were tight on memory. This did not cause any failures to the system itself (no crashes), but caused unnecessary failures for reading the trace files. Added a helper function called 'trace_buffer_iter()' that returns the buffer_iter item or NULL if it is not defined or the array was not allocated. Some routines do not require the array (tracing_open_pipe() for one). Reported-by: Dave Jones Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 2 +- kernel/trace/trace.c | 27 ++++++++++++++++++--------- kernel/trace/trace.h | 8 ++++++++ kernel/trace/trace_functions_graph.c | 2 +- 4 files changed, 28 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 1aff18346c71..af961d6f7ab1 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -65,7 +65,7 @@ struct trace_iterator { void *private; int cpu_file; struct mutex mutex; - struct ring_buffer_iter *buffer_iter[NR_CPUS]; + struct ring_buffer_iter **buffer_iter; unsigned long iter_flags; /* trace_seq for __print_flags() and __print_symbolic() etc. */ diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 748f6401edf6..b2af14e94c28 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1710,9 +1710,11 @@ EXPORT_SYMBOL_GPL(trace_vprintk); static void trace_iterator_increment(struct trace_iterator *iter) { + struct ring_buffer_iter *buf_iter = trace_buffer_iter(iter, iter->cpu); + iter->idx++; - if (iter->buffer_iter[iter->cpu]) - ring_buffer_read(iter->buffer_iter[iter->cpu], NULL); + if (buf_iter) + ring_buffer_read(buf_iter, NULL); } static struct trace_entry * @@ -1720,7 +1722,7 @@ peek_next_entry(struct trace_iterator *iter, int cpu, u64 *ts, unsigned long *lost_events) { struct ring_buffer_event *event; - struct ring_buffer_iter *buf_iter = iter->buffer_iter[cpu]; + struct ring_buffer_iter *buf_iter = trace_buffer_iter(iter, cpu); if (buf_iter) event = ring_buffer_iter_peek(buf_iter, ts); @@ -1858,10 +1860,10 @@ void tracing_iter_reset(struct trace_iterator *iter, int cpu) tr->data[cpu]->skipped_entries = 0; - if (!iter->buffer_iter[cpu]) + buf_iter = trace_buffer_iter(iter, cpu); + if (!buf_iter) return; - buf_iter = iter->buffer_iter[cpu]; ring_buffer_iter_reset(buf_iter); /* @@ -2207,13 +2209,15 @@ static enum print_line_t print_bin_fmt(struct trace_iterator *iter) int trace_empty(struct trace_iterator *iter) { + struct ring_buffer_iter *buf_iter; int cpu; /* If we are looking at one CPU buffer, only check that one */ if (iter->cpu_file != TRACE_PIPE_ALL_CPU) { cpu = iter->cpu_file; - if (iter->buffer_iter[cpu]) { - if (!ring_buffer_iter_empty(iter->buffer_iter[cpu])) + buf_iter = trace_buffer_iter(iter, cpu); + if (buf_iter) { + if (!ring_buffer_iter_empty(buf_iter)) return 0; } else { if (!ring_buffer_empty_cpu(iter->tr->buffer, cpu)) @@ -2223,8 +2227,9 @@ int trace_empty(struct trace_iterator *iter) } for_each_tracing_cpu(cpu) { - if (iter->buffer_iter[cpu]) { - if (!ring_buffer_iter_empty(iter->buffer_iter[cpu])) + buf_iter = trace_buffer_iter(iter, cpu); + if (buf_iter) { + if (!ring_buffer_iter_empty(buf_iter)) return 0; } else { if (!ring_buffer_empty_cpu(iter->tr->buffer, cpu)) @@ -2383,6 +2388,8 @@ __tracing_open(struct inode *inode, struct file *file) if (!iter) return ERR_PTR(-ENOMEM); + iter->buffer_iter = kzalloc(sizeof(*iter->buffer_iter) * num_possible_cpus(), + GFP_KERNEL); /* * We make a copy of the current tracer to avoid concurrent * changes on it while we are reading. @@ -2443,6 +2450,7 @@ __tracing_open(struct inode *inode, struct file *file) fail: mutex_unlock(&trace_types_lock); kfree(iter->trace); + kfree(iter->buffer_iter); seq_release_private(inode, file); return ERR_PTR(-ENOMEM); } @@ -2483,6 +2491,7 @@ static int tracing_release(struct inode *inode, struct file *file) mutex_destroy(&iter->mutex); free_cpumask_var(iter->started); kfree(iter->trace); + kfree(iter->buffer_iter); seq_release_private(inode, file); return 0; } diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 5aec220d2de0..55e1f7f0db12 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -317,6 +317,14 @@ struct tracer { #define TRACE_PIPE_ALL_CPU -1 +static inline struct ring_buffer_iter * +trace_buffer_iter(struct trace_iterator *iter, int cpu) +{ + if (iter->buffer_iter && iter->buffer_iter[cpu]) + return iter->buffer_iter[cpu]; + return NULL; +} + int tracer_init(struct tracer *t, struct trace_array *tr); int tracing_is_enabled(void); void trace_wake_up(void); diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index a7d2a4c653d8..ce27c8ba8d31 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c @@ -538,7 +538,7 @@ get_return_for_leaf(struct trace_iterator *iter, next = &data->ret; } else { - ring_iter = iter->buffer_iter[iter->cpu]; + ring_iter = trace_buffer_iter(iter, iter->cpu); /* First peek to compare current entry and the next one */ if (ring_iter) -- cgit v1.2.3-59-g8ed1b From a31f2d17b331db970259e875b7223d3aba7e3821 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 29 Jun 2012 06:15:21 +0000 Subject: netlink: add netlink_kernel_cfg parameter to netlink_kernel_create This patch adds the following structure: struct netlink_kernel_cfg { unsigned int groups; void (*input)(struct sk_buff *skb); struct mutex *cb_mutex; }; That can be passed to netlink_kernel_create to set optional configurations for netlink kernel sockets. I've populated this structure by looking for NULL and zero parameters at the existing code. The remaining parameters that always need to be set are still left in the original interface. That includes optional parameters for the netlink socket creation. This allows easy extensibility of this interface in the future. This patch also adapts all callers to use this new interface. Signed-off-by: Pablo Neira Ayuso Signed-off-by: David S. Miller --- crypto/crypto_user.c | 7 +++++-- drivers/connector/connector.c | 13 +++++++++---- drivers/infiniband/core/netlink.c | 7 +++++-- drivers/scsi/scsi_netlink.c | 7 +++++-- drivers/scsi/scsi_transport_iscsi.c | 9 ++++++--- drivers/staging/gdm72xx/netlink_k.c | 6 ++++-- include/linux/netlink.h | 15 ++++++++++----- kernel/audit.c | 7 +++++-- lib/kobject_uevent.c | 5 ++++- net/bridge/netfilter/ebt_ulog.c | 6 ++++-- net/core/rtnetlink.c | 9 +++++++-- net/core/sock_diag.c | 8 ++++++-- net/decnet/netfilter/dn_rtmsg.c | 8 +++++--- net/ipv4/fib_frontend.c | 7 +++++-- net/ipv4/netfilter/ipt_ULOG.c | 8 +++++--- net/netfilter/nfnetlink.c | 7 +++++-- net/netlink/af_netlink.c | 16 ++++++++++------ net/netlink/genetlink.c | 10 +++++++--- net/xfrm/xfrm_user.c | 7 +++++-- security/selinux/netlink.c | 6 +++++- 20 files changed, 117 insertions(+), 51 deletions(-) (limited to 'include/linux') diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c index 5a37eadb4e56..ba2c611154af 100644 --- a/crypto/crypto_user.c +++ b/crypto/crypto_user.c @@ -496,9 +496,12 @@ static void crypto_netlink_rcv(struct sk_buff *skb) static int __init crypto_user_init(void) { + struct netlink_kernel_cfg cfg = { + .input = crypto_netlink_rcv, + }; + crypto_nlsk = netlink_kernel_create(&init_net, NETLINK_CRYPTO, - 0, crypto_netlink_rcv, - NULL, THIS_MODULE); + THIS_MODULE, &cfg); if (!crypto_nlsk) return -ENOMEM; diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index 34e0e9e4d913..116cf8d02834 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -251,15 +251,20 @@ static const struct file_operations cn_file_ops = { .release = single_release }; +static struct cn_dev cdev = { + .input = cn_rx_skb, +}; + static int __devinit cn_init(void) { struct cn_dev *dev = &cdev; - - dev->input = cn_rx_skb; + struct netlink_kernel_cfg cfg = { + .groups = CN_NETLINK_USERS + 0xf, + .input = dev->input, + }; dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR, - CN_NETLINK_USERS + 0xf, - dev->input, NULL, THIS_MODULE); + THIS_MODULE, &cfg); if (!dev->nls) return -EIO; diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c index 1e691dca1820..3ae2bfd31015 100644 --- a/drivers/infiniband/core/netlink.c +++ b/drivers/infiniband/core/netlink.c @@ -173,8 +173,11 @@ static void ibnl_rcv(struct sk_buff *skb) int __init ibnl_init(void) { - nls = netlink_kernel_create(&init_net, NETLINK_RDMA, 0, ibnl_rcv, - NULL, THIS_MODULE); + struct netlink_kernel_cfg cfg = { + .input = ibnl_rcv, + }; + + nls = netlink_kernel_create(&init_net, NETLINK_RDMA, THIS_MODULE, &cfg); if (!nls) { pr_warn("Failed to create netlink socket\n"); return -ENOMEM; diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c index c77628afbf9f..8818dd681c19 100644 --- a/drivers/scsi/scsi_netlink.c +++ b/drivers/scsi/scsi_netlink.c @@ -486,6 +486,10 @@ void scsi_netlink_init(void) { int error; + struct netlink_kernel_cfg cfg = { + .input = scsi_nl_rcv_msg, + .groups = SCSI_NL_GRP_CNT, + }; INIT_LIST_HEAD(&scsi_nl_drivers); @@ -497,8 +501,7 @@ scsi_netlink_init(void) } scsi_nl_sock = netlink_kernel_create(&init_net, NETLINK_SCSITRANSPORT, - SCSI_NL_GRP_CNT, scsi_nl_rcv_msg, NULL, - THIS_MODULE); + THIS_MODULE, &cfg); if (!scsi_nl_sock) { printk(KERN_ERR "%s: register of receive handler failed\n", __func__); diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 1cf640e575da..6042954d8f3b 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -2936,7 +2936,10 @@ EXPORT_SYMBOL_GPL(iscsi_unregister_transport); static __init int iscsi_transport_init(void) { int err; - + struct netlink_kernel_cfg cfg = { + .groups = 1, + .input = iscsi_if_rx, + }; printk(KERN_INFO "Loading iSCSI transport class v%s.\n", ISCSI_TRANSPORT_VERSION); @@ -2966,8 +2969,8 @@ static __init int iscsi_transport_init(void) if (err) goto unregister_conn_class; - nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx, - NULL, THIS_MODULE); + nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, + THIS_MODULE, &cfg); if (!nls) { err = -ENOBUFS; goto unregister_session_class; diff --git a/drivers/staging/gdm72xx/netlink_k.c b/drivers/staging/gdm72xx/netlink_k.c index 2489bb5597ca..87c3a07ed80e 100644 --- a/drivers/staging/gdm72xx/netlink_k.c +++ b/drivers/staging/gdm72xx/netlink_k.c @@ -88,13 +88,15 @@ struct sock *netlink_init(int unit, void (*cb)(struct net_device *dev, u16 type, void *msg, int len)) { struct sock *sock; + struct netlink_kernel_cfg cfg = { + .input = netlink_rcv, + }; #if !defined(DEFINE_MUTEX) init_MUTEX(&netlink_mutex); #endif - sock = netlink_kernel_create(&init_net, unit, 0, netlink_rcv, NULL, - THIS_MODULE); + sock = netlink_kernel_create(&init_net, unit, THIS_MODULE, &cfg); if (sock) rcv_cb = cb; diff --git a/include/linux/netlink.h b/include/linux/netlink.h index ed33f0901bc2..6085e4919cb3 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -174,11 +174,16 @@ struct netlink_skb_parms { extern void netlink_table_grab(void); extern void netlink_table_ungrab(void); -extern struct sock *netlink_kernel_create(struct net *net, - int unit,unsigned int groups, - void (*input)(struct sk_buff *skb), - struct mutex *cb_mutex, - struct module *module); +/* optional Netlink kernel configuration parameters */ +struct netlink_kernel_cfg { + unsigned int groups; + void (*input)(struct sk_buff *skb); + struct mutex *cb_mutex; +}; + +extern struct sock *netlink_kernel_create(struct net *net, int unit, + struct module *module, + struct netlink_kernel_cfg *cfg); extern void netlink_kernel_release(struct sock *sk); extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups); extern int netlink_change_ngroups(struct sock *sk, unsigned int groups); diff --git a/kernel/audit.c b/kernel/audit.c index 30b252a1fb61..4a3f28d2ca65 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -962,14 +962,17 @@ static void audit_receive(struct sk_buff *skb) static int __init audit_init(void) { int i; + struct netlink_kernel_cfg cfg = { + .input = audit_receive, + }; if (audit_initialized == AUDIT_DISABLED) return 0; printk(KERN_INFO "audit: initializing netlink socket (%s)\n", audit_default ? "enabled" : "disabled"); - audit_sock = netlink_kernel_create(&init_net, NETLINK_AUDIT, 0, - audit_receive, NULL, THIS_MODULE); + audit_sock = netlink_kernel_create(&init_net, NETLINK_AUDIT, + THIS_MODULE, &cfg); if (!audit_sock) audit_panic("cannot initialize netlink socket"); else diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 1a91efa6d121..0401d2916d9f 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -373,13 +373,16 @@ EXPORT_SYMBOL_GPL(add_uevent_var); static int uevent_net_init(struct net *net) { struct uevent_sock *ue_sk; + struct netlink_kernel_cfg cfg = { + .groups = 1, + }; ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL); if (!ue_sk) return -ENOMEM; ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, - 1, NULL, NULL, THIS_MODULE); + THIS_MODULE, &cfg); if (!ue_sk->sk) { printk(KERN_ERR "kobject_uevent: unable to create netlink socket!\n"); diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 1bd173218f7b..374bdcd77039 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -282,6 +282,9 @@ static int __init ebt_ulog_init(void) { int ret; int i; + struct netlink_kernel_cfg cfg = { + .groups = EBT_ULOG_MAXNLGROUPS, + }; if (nlbufsiz >= 128*1024) { pr_warning("Netlink buffer has to be <= 128kB," @@ -296,8 +299,7 @@ static int __init ebt_ulog_init(void) } ebtulognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, - EBT_ULOG_MAXNLGROUPS, NULL, NULL, - THIS_MODULE); + THIS_MODULE, &cfg); if (!ebtulognl) ret = -ENOMEM; else if ((ret = xt_register_target(&ebt_ulog_tg_reg)) != 0) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index bc8a1cdaac98..2b325c340b44 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2353,8 +2353,13 @@ static struct notifier_block rtnetlink_dev_notifier = { static int __net_init rtnetlink_net_init(struct net *net) { struct sock *sk; - sk = netlink_kernel_create(net, NETLINK_ROUTE, RTNLGRP_MAX, - rtnetlink_rcv, &rtnl_mutex, THIS_MODULE); + struct netlink_kernel_cfg cfg = { + .groups = RTNLGRP_MAX, + .input = rtnetlink_rcv, + .cb_mutex = &rtnl_mutex, + }; + + sk = netlink_kernel_create(net, NETLINK_ROUTE, THIS_MODULE, &cfg); if (!sk) return -ENOMEM; net->rtnl = sk; diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index ff2967acbfae..07a29eb34a41 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -171,8 +171,12 @@ EXPORT_SYMBOL_GPL(sock_diag_nlsk); static int __init sock_diag_init(void) { - sock_diag_nlsk = netlink_kernel_create(&init_net, NETLINK_SOCK_DIAG, 0, - sock_diag_rcv, NULL, THIS_MODULE); + struct netlink_kernel_cfg cfg = { + .input = sock_diag_rcv, + }; + + sock_diag_nlsk = netlink_kernel_create(&init_net, NETLINK_SOCK_DIAG, + THIS_MODULE, &cfg); return sock_diag_nlsk == NULL ? -ENOMEM : 0; } diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c index b8f7f5b8c350..11db0ecf342f 100644 --- a/net/decnet/netfilter/dn_rtmsg.c +++ b/net/decnet/netfilter/dn_rtmsg.c @@ -125,11 +125,13 @@ static struct nf_hook_ops dnrmg_ops __read_mostly = { static int __init dn_rtmsg_init(void) { int rv = 0; + struct netlink_kernel_cfg cfg = { + .groups = DNRNG_NLGRP_MAX, + .input = dnrmg_receive_user_skb, + }; dnrmg = netlink_kernel_create(&init_net, - NETLINK_DNRTMSG, DNRNG_NLGRP_MAX, - dnrmg_receive_user_skb, - NULL, THIS_MODULE); + NETLINK_DNRTMSG, THIS_MODULE, &cfg); if (dnrmg == NULL) { printk(KERN_ERR "dn_rtmsg: Cannot create netlink socket"); return -ENOMEM; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index ae528d1b293a..3e11ea225dad 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -976,8 +976,11 @@ static void nl_fib_input(struct sk_buff *skb) static int __net_init nl_fib_lookup_init(struct net *net) { struct sock *sk; - sk = netlink_kernel_create(net, NETLINK_FIB_LOOKUP, 0, - nl_fib_input, NULL, THIS_MODULE); + struct netlink_kernel_cfg cfg = { + .input = nl_fib_input, + }; + + sk = netlink_kernel_create(net, NETLINK_FIB_LOOKUP, THIS_MODULE, &cfg); if (sk == NULL) return -EAFNOSUPPORT; net->ipv4.fibnl = sk; diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c index 99b3f53f16a7..1109f7f6c254 100644 --- a/net/ipv4/netfilter/ipt_ULOG.c +++ b/net/ipv4/netfilter/ipt_ULOG.c @@ -381,6 +381,9 @@ static struct nf_logger ipt_ulog_logger __read_mostly = { static int __init ulog_tg_init(void) { int ret, i; + struct netlink_kernel_cfg cfg = { + .groups = ULOG_MAXNLGROUPS, + }; pr_debug("init module\n"); @@ -393,9 +396,8 @@ static int __init ulog_tg_init(void) for (i = 0; i < ULOG_MAXNLGROUPS; i++) setup_timer(&ulog_buffers[i].timer, ulog_timer, i); - nflognl = netlink_kernel_create(&init_net, - NETLINK_NFLOG, ULOG_MAXNLGROUPS, NULL, - NULL, THIS_MODULE); + nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, + THIS_MODULE, &cfg); if (!nflognl) return -ENOMEM; diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 3e797d1fcb94..700e4616a098 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -203,9 +203,12 @@ static void nfnetlink_rcv(struct sk_buff *skb) static int __net_init nfnetlink_net_init(struct net *net) { struct sock *nfnl; + struct netlink_kernel_cfg cfg = { + .groups = NFNLGRP_MAX, + .input = nfnetlink_rcv, + }; - nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, NFNLGRP_MAX, - nfnetlink_rcv, NULL, THIS_MODULE); + nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, THIS_MODULE, &cfg); if (!nfnl) return -ENOMEM; net->nfnl_stash = nfnl; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index b3025a603d56..43a124feaad8 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1503,14 +1503,16 @@ static void netlink_data_ready(struct sock *sk, int len) */ struct sock * -netlink_kernel_create(struct net *net, int unit, unsigned int groups, - void (*input)(struct sk_buff *skb), - struct mutex *cb_mutex, struct module *module) +netlink_kernel_create(struct net *net, int unit, + struct module *module, + struct netlink_kernel_cfg *cfg) { struct socket *sock; struct sock *sk; struct netlink_sock *nlk; struct listeners *listeners = NULL; + struct mutex *cb_mutex = cfg ? cfg->cb_mutex : NULL; + unsigned int groups; BUG_ON(!nl_table); @@ -1532,16 +1534,18 @@ netlink_kernel_create(struct net *net, int unit, unsigned int groups, sk = sock->sk; sk_change_net(sk, net); - if (groups < 32) + if (!cfg || cfg->groups < 32) groups = 32; + else + groups = cfg->groups; listeners = kzalloc(sizeof(*listeners) + NLGRPSZ(groups), GFP_KERNEL); if (!listeners) goto out_sock_release; sk->sk_data_ready = netlink_data_ready; - if (input) - nlk_sk(sk)->netlink_rcv = input; + if (cfg && cfg->input) + nlk_sk(sk)->netlink_rcv = cfg->input; if (netlink_insert(sk, net, 0)) goto out_sock_release; diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 2cc7c1ee7690..32761b53015e 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -915,10 +915,14 @@ static struct genl_multicast_group notify_grp = { static int __net_init genl_pernet_init(struct net *net) { + struct netlink_kernel_cfg cfg = { + .input = genl_rcv, + .cb_mutex = &genl_mutex, + }; + /* we'll bump the group number right afterwards */ - net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, 0, - genl_rcv, &genl_mutex, - THIS_MODULE); + net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, + THIS_MODULE, &cfg); if (!net->genl_sock && net_eq(net, &init_net)) panic("GENL: Cannot initialize generic netlink\n"); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 540762726aaf..e75d8e47f35c 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2959,9 +2959,12 @@ static struct xfrm_mgr netlink_mgr = { static int __net_init xfrm_user_net_init(struct net *net) { struct sock *nlsk; + struct netlink_kernel_cfg cfg = { + .groups = XFRMNLGRP_MAX, + .input = xfrm_netlink_rcv, + }; - nlsk = netlink_kernel_create(net, NETLINK_XFRM, XFRMNLGRP_MAX, - xfrm_netlink_rcv, NULL, THIS_MODULE); + nlsk = netlink_kernel_create(net, NETLINK_XFRM, THIS_MODULE, &cfg); if (nlsk == NULL) return -ENOMEM; net->xfrm.nlsk_stash = nlsk; /* Don't set to NULL */ diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c index 8a23a35b9c5b..8a77725423e0 100644 --- a/security/selinux/netlink.c +++ b/security/selinux/netlink.c @@ -111,8 +111,12 @@ void selnl_notify_policyload(u32 seqno) static int __init selnl_init(void) { + struct netlink_kernel_cfg cfg = { + .groups = SELNLGRP_MAX, + }; + selnl = netlink_kernel_create(&init_net, NETLINK_SELINUX, - SELNLGRP_MAX, NULL, NULL, THIS_MODULE); + THIS_MODULE, &cfg); if (selnl == NULL) panic("SELinux: Cannot create netlink socket."); netlink_set_nonroot(NETLINK_SELINUX, NL_NONROOT_RECV); -- cgit v1.2.3-59-g8ed1b From 03292745b02d1166e2a215504407e096b8427be5 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 29 Jun 2012 06:15:22 +0000 Subject: netlink: add nlk->netlink_bind hook for module auto-loading This patch adds a hook in the binding path of netlink. This is used by ctnetlink to allow module autoloading for the case in which one user executes: conntrack -E So far, this resulted in nfnetlink loaded, but not nf_conntrack_netlink. I have received in the past many complains on this behaviour. Signed-off-by: Pablo Neira Ayuso Signed-off-by: David S. Miller --- include/linux/netlink.h | 1 + net/netfilter/nfnetlink.c | 29 +++++++++++++++++++++++++++++ net/netlink/af_netlink.c | 19 +++++++++++++++++++ 3 files changed, 49 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 6085e4919cb3..f74dd133788f 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -179,6 +179,7 @@ struct netlink_kernel_cfg { unsigned int groups; void (*input)(struct sk_buff *skb); struct mutex *cb_mutex; + void (*bind)(int group); }; extern struct sock *netlink_kernel_create(struct net *net, int unit, diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 700e4616a098..5a2132b97fe9 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -39,6 +39,15 @@ static char __initdata nfversion[] = "0.30"; static const struct nfnetlink_subsystem __rcu *subsys_table[NFNL_SUBSYS_COUNT]; static DEFINE_MUTEX(nfnl_mutex); +static const int nfnl_group2type[NFNLGRP_MAX+1] = { + [NFNLGRP_CONNTRACK_NEW] = NFNL_SUBSYS_CTNETLINK, + [NFNLGRP_CONNTRACK_UPDATE] = NFNL_SUBSYS_CTNETLINK, + [NFNLGRP_CONNTRACK_DESTROY] = NFNL_SUBSYS_CTNETLINK, + [NFNLGRP_CONNTRACK_EXP_NEW] = NFNL_SUBSYS_CTNETLINK_EXP, + [NFNLGRP_CONNTRACK_EXP_UPDATE] = NFNL_SUBSYS_CTNETLINK_EXP, + [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP, +}; + void nfnl_lock(void) { mutex_lock(&nfnl_mutex); @@ -200,12 +209,32 @@ static void nfnetlink_rcv(struct sk_buff *skb) netlink_rcv_skb(skb, &nfnetlink_rcv_msg); } +#ifdef CONFIG_MODULES +static void nfnetlink_bind(int group) +{ + const struct nfnetlink_subsystem *ss; + int type = nfnl_group2type[group]; + + rcu_read_lock(); + ss = nfnetlink_get_subsys(type); + if (!ss) { + rcu_read_unlock(); + request_module("nfnetlink-subsys-%d", type); + return; + } + rcu_read_unlock(); +} +#endif + static int __net_init nfnetlink_net_init(struct net *net) { struct sock *nfnl; struct netlink_kernel_cfg cfg = { .groups = NFNLGRP_MAX, .input = nfnetlink_rcv, +#ifdef CONFIG_MODULES + .bind = nfnetlink_bind, +#endif }; nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, THIS_MODULE, &cfg); diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 43a124feaad8..5463969da45b 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -80,6 +80,7 @@ struct netlink_sock { struct mutex *cb_mutex; struct mutex cb_def_mutex; void (*netlink_rcv)(struct sk_buff *skb); + void (*netlink_bind)(int group); struct module *module; }; @@ -124,6 +125,7 @@ struct netlink_table { unsigned int groups; struct mutex *cb_mutex; struct module *module; + void (*bind)(int group); int registered; }; @@ -444,6 +446,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol, struct module *module = NULL; struct mutex *cb_mutex; struct netlink_sock *nlk; + void (*bind)(int group); int err = 0; sock->state = SS_UNCONNECTED; @@ -468,6 +471,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol, else err = -EPROTONOSUPPORT; cb_mutex = nl_table[protocol].cb_mutex; + bind = nl_table[protocol].bind; netlink_unlock_table(); if (err < 0) @@ -483,6 +487,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol, nlk = nlk_sk(sock->sk); nlk->module = module; + nlk->netlink_bind = bind; out: return err; @@ -683,6 +688,15 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, netlink_update_listeners(sk); netlink_table_ungrab(); + if (nlk->netlink_bind && nlk->groups[0]) { + int i; + + for (i=0; ingroups; i++) { + if (test_bit(i, nlk->groups)) + nlk->netlink_bind(i); + } + } + return 0; } @@ -1239,6 +1253,10 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, netlink_update_socket_mc(nlk, val, optname == NETLINK_ADD_MEMBERSHIP); netlink_table_ungrab(); + + if (nlk->netlink_bind) + nlk->netlink_bind(val); + err = 0; break; } @@ -1559,6 +1577,7 @@ netlink_kernel_create(struct net *net, int unit, rcu_assign_pointer(nl_table[unit].listeners, listeners); nl_table[unit].cb_mutex = cb_mutex; nl_table[unit].module = module; + nl_table[unit].bind = cfg ? cfg->bind : NULL; nl_table[unit].registered = 1; } else { kfree(listeners); -- cgit v1.2.3-59-g8ed1b From bb35f67195fcdbe79faa7a15ce148a67c9ab923d Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 29 Jun 2012 05:10:05 +0000 Subject: net: introduce new priv_flag indicating iface capable of change mac when running Introduce IFF_LIVE_ADDR_CHANGE priv_flag and use it to disable netif_running() check in eth_mac_addr() Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/if.h | 2 ++ net/ethernet/eth.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/if.h b/include/linux/if.h index f995c663c493..1ec407b01e46 100644 --- a/include/linux/if.h +++ b/include/linux/if.h @@ -81,6 +81,8 @@ #define IFF_UNICAST_FLT 0x20000 /* Supports unicast filtering */ #define IFF_TEAM_PORT 0x40000 /* device used as team port */ #define IFF_SUPP_NOFCS 0x80000 /* device supports sending custom FCS */ +#define IFF_LIVE_ADDR_CHANGE 0x100000 /* device supports hardware address + * change when it's running */ #define IF_GET_IFACE 0x0001 /* for querying only */ diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index 36e58800a9e3..db6a6c17d790 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -283,7 +283,7 @@ int eth_mac_addr(struct net_device *dev, void *p) { struct sockaddr *addr = p; - if (netif_running(dev)) + if (!(dev->priv_flags & IFF_LIVE_ADDR_CHANGE) && netif_running(dev)) return -EBUSY; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; -- cgit v1.2.3-59-g8ed1b From a59a4d1921664da63d801ba477950114c71c88c9 Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Wed, 27 Jun 2012 21:14:38 +0000 Subject: phy: add the EEE support and the way to access to the MMD registers. This patch adds the support for the Energy-Efficient Ethernet (EEE) to the Physical Abstraction Layer. To support the EEE we have to access to the MMD registers 3.20 and 7.60/61. So two new functions have been added to read/write the MMD registers (clause 45). An Ethernet driver (I tested the stmmac) can invoke the phy_init_eee to properly check if the EEE is supported by the PHYs and it can also set the clock stop enable bit in the 3.0 register. The phy_get_eee_err can be used for reporting the number of time where the PHY failed to complete its normal wake sequence. In the end, this patch also adds the EEE ethtool support implementing: o phy_ethtool_set_eee o phy_ethtool_get_eee v1: initial patch v2: fixed some errors especially on naming convention v3: renamed again the mmd read/write functions thank to Ben's feedback v4: moved file to phy.c and added the ethtool support. v5: fixed phy_adv_to_eee, phy_eee_to_supported, phy_eee_to_adv return values according to ethtool API (thanks to Ben's feedback). Renamed some macros to avoid too long names. v6: fixed kernel-doc comments to be properly parsed. Fixed the phy_init_eee function: we need to check which link mode was autonegotiated and then the corresponding bits in 7.60 and 7.61 registers. v7: reviewed the way to get the negotiated settings. v8: fixed a problem in the phy_init_eee return value erroneously added when included the phy_read_status call. v9: do not remove the MDIO_AN_EEE_ADV_100TX and MDIO_AN_EEE_ADV_1000T and fixed the eee_{cap,lp,adv} declaration as "int" instead of u16. Signed-off-by: Giuseppe Cavallaro Reviewed-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mdio.h | 28 ++++- include/linux/mii.h | 9 ++ include/linux/phy.h | 5 + 4 files changed, 319 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 2e1c23731ded..7ca2ff97c368 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -967,3 +968,283 @@ void phy_state_machine(struct work_struct *work) schedule_delayed_work(&phydev->state_queue, PHY_STATE_TIME * HZ); } + +static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, + int addr) +{ + /* Write the desired MMD Devad */ + bus->write(bus, addr, MII_MMD_CTRL, devad); + + /* Write the desired MMD register address */ + bus->write(bus, addr, MII_MMD_DATA, prtad); + + /* Select the Function : DATA with no post increment */ + bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); +} + +/** + * phy_read_mmd_indirect - reads data from the MMD registers + * @bus: the target MII bus + * @prtad: MMD Address + * @devad: MMD DEVAD + * @addr: PHY address on the MII bus + * + * Description: it reads data from the MMD registers (clause 22 to access to + * clause 45) of the specified phy address. + * To read these register we have: + * 1) Write reg 13 // DEVAD + * 2) Write reg 14 // MMD Address + * 3) Write reg 13 // MMD Data Command for MMD DEVAD + * 3) Read reg 14 // Read MMD data + */ +static int phy_read_mmd_indirect(struct mii_bus *bus, int prtad, int devad, + int addr) +{ + u32 ret; + + mmd_phy_indirect(bus, prtad, devad, addr); + + /* Read the content of the MMD's selected register */ + ret = bus->read(bus, addr, MII_MMD_DATA); + + return ret; +} + +/** + * phy_write_mmd_indirect - writes data to the MMD registers + * @bus: the target MII bus + * @prtad: MMD Address + * @devad: MMD DEVAD + * @addr: PHY address on the MII bus + * @data: data to write in the MMD register + * + * Description: Write data from the MMD registers of the specified + * phy address. + * To write these register we have: + * 1) Write reg 13 // DEVAD + * 2) Write reg 14 // MMD Address + * 3) Write reg 13 // MMD Data Command for MMD DEVAD + * 3) Write reg 14 // Write MMD data + */ +static void phy_write_mmd_indirect(struct mii_bus *bus, int prtad, int devad, + int addr, u32 data) +{ + mmd_phy_indirect(bus, prtad, devad, addr); + + /* Write the data into MMD's selected register */ + bus->write(bus, addr, MII_MMD_DATA, data); +} + +static u32 phy_eee_to_adv(u16 eee_adv) +{ + u32 adv = 0; + + if (eee_adv & MDIO_EEE_100TX) + adv |= ADVERTISED_100baseT_Full; + if (eee_adv & MDIO_EEE_1000T) + adv |= ADVERTISED_1000baseT_Full; + if (eee_adv & MDIO_EEE_10GT) + adv |= ADVERTISED_10000baseT_Full; + if (eee_adv & MDIO_EEE_1000KX) + adv |= ADVERTISED_1000baseKX_Full; + if (eee_adv & MDIO_EEE_10GKX4) + adv |= ADVERTISED_10000baseKX4_Full; + if (eee_adv & MDIO_EEE_10GKR) + adv |= ADVERTISED_10000baseKR_Full; + + return adv; +} + +static u32 phy_eee_to_supported(u16 eee_caported) +{ + u32 supported = 0; + + if (eee_caported & MDIO_EEE_100TX) + supported |= SUPPORTED_100baseT_Full; + if (eee_caported & MDIO_EEE_1000T) + supported |= SUPPORTED_1000baseT_Full; + if (eee_caported & MDIO_EEE_10GT) + supported |= SUPPORTED_10000baseT_Full; + if (eee_caported & MDIO_EEE_1000KX) + supported |= SUPPORTED_1000baseKX_Full; + if (eee_caported & MDIO_EEE_10GKX4) + supported |= SUPPORTED_10000baseKX4_Full; + if (eee_caported & MDIO_EEE_10GKR) + supported |= SUPPORTED_10000baseKR_Full; + + return supported; +} + +static u16 phy_adv_to_eee(u32 adv) +{ + u16 reg = 0; + + if (adv & ADVERTISED_100baseT_Full) + reg |= MDIO_EEE_100TX; + if (adv & ADVERTISED_1000baseT_Full) + reg |= MDIO_EEE_1000T; + if (adv & ADVERTISED_10000baseT_Full) + reg |= MDIO_EEE_10GT; + if (adv & ADVERTISED_1000baseKX_Full) + reg |= MDIO_EEE_1000KX; + if (adv & ADVERTISED_10000baseKX4_Full) + reg |= MDIO_EEE_10GKX4; + if (adv & ADVERTISED_10000baseKR_Full) + reg |= MDIO_EEE_10GKR; + + return reg; +} + +/** + * phy_init_eee - init and check the EEE feature + * @phydev: target phy_device struct + * @clk_stop_enable: PHY may stop the clock during LPI + * + * Description: it checks if the Energy-Efficient Ethernet (EEE) + * is supported by looking at the MMD registers 3.20 and 7.60/61 + * and it programs the MMD register 3.0 setting the "Clock stop enable" + * bit if required. + */ +int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) +{ + int ret = -EPROTONOSUPPORT; + + /* According to 802.3az,the EEE is supported only in full duplex-mode. + * Also EEE feature is active when core is operating with MII, GMII + * or RGMII. + */ + if ((phydev->duplex == DUPLEX_FULL) && + ((phydev->interface == PHY_INTERFACE_MODE_MII) || + (phydev->interface == PHY_INTERFACE_MODE_GMII) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII))) { + int eee_lp, eee_cap, eee_adv; + u32 lp, cap, adv; + int idx, status; + + /* Read phy status to properly get the right settings */ + status = phy_read_status(phydev); + if (status) + return status; + + /* First check if the EEE ability is supported */ + eee_cap = phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_ABLE, + MDIO_MMD_PCS, phydev->addr); + if (eee_cap < 0) + return eee_cap; + + cap = phy_eee_to_supported(eee_cap); + if (!cap) + goto eee_exit; + + /* Check which link settings negotiated and verify it in + * the EEE advertising registers. + */ + eee_lp = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_LPABLE, + MDIO_MMD_AN, phydev->addr); + if (eee_lp < 0) + return eee_lp; + + eee_adv = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, + MDIO_MMD_AN, phydev->addr); + if (eee_adv < 0) + return eee_adv; + + adv = phy_eee_to_adv(eee_adv); + lp = phy_eee_to_adv(eee_lp); + idx = phy_find_setting(phydev->speed, phydev->duplex); + if ((lp & adv & settings[idx].setting)) + goto eee_exit; + + if (clk_stop_enable) { + /* Configure the PHY to stop receiving xMII + * clock while it is signaling LPI. + */ + int val = phy_read_mmd_indirect(phydev->bus, MDIO_CTRL1, + MDIO_MMD_PCS, + phydev->addr); + if (val < 0) + return val; + + val |= MDIO_PCS_CTRL1_CLKSTOP_EN; + phy_write_mmd_indirect(phydev->bus, MDIO_CTRL1, + MDIO_MMD_PCS, phydev->addr, val); + } + + ret = 0; /* EEE supported */ + } + +eee_exit: + return ret; +} +EXPORT_SYMBOL(phy_init_eee); + +/** + * phy_get_eee_err - report the EEE wake error count + * @phydev: target phy_device struct + * + * Description: it is to report the number of time where the PHY + * failed to complete its normal wake sequence. + */ +int phy_get_eee_err(struct phy_device *phydev) +{ + return phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_WK_ERR, + MDIO_MMD_PCS, phydev->addr); + +} +EXPORT_SYMBOL(phy_get_eee_err); + +/** + * phy_ethtool_get_eee - get EEE supported and status + * @phydev: target phy_device struct + * @data: ethtool_eee data + * + * Description: it reportes the Supported/Advertisement/LP Advertisement + * capabilities. + */ +int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data) +{ + int val; + + /* Get Supported EEE */ + val = phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_ABLE, + MDIO_MMD_PCS, phydev->addr); + if (val < 0) + return val; + data->supported = phy_eee_to_supported(val); + + /* Get advertisement EEE */ + val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, + MDIO_MMD_AN, phydev->addr); + if (val < 0) + return val; + data->advertised = phy_eee_to_adv(val); + + /* Get LP advertisement EEE */ + val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_LPABLE, + MDIO_MMD_AN, phydev->addr); + if (val < 0) + return val; + data->lp_advertised = phy_eee_to_adv(val); + + return 0; +} +EXPORT_SYMBOL(phy_ethtool_get_eee); + +/** + * phy_ethtool_set_eee - set EEE supported and status + * @phydev: target phy_device struct + * @data: ethtool_eee data + * + * Description: it is to program the Advertisement EEE register. + */ +int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) +{ + int val; + + val = phy_adv_to_eee(data->advertised); + phy_write_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, MDIO_MMD_AN, + phydev->addr, val); + + return 0; +} +EXPORT_SYMBOL(phy_ethtool_set_eee); diff --git a/include/linux/mdio.h b/include/linux/mdio.h index dfb947959ec9..7cccafe50e7b 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -43,7 +43,11 @@ #define MDIO_PKGID2 15 #define MDIO_AN_ADVERTISE 16 /* AN advertising (base page) */ #define MDIO_AN_LPA 19 /* AN LP abilities (base page) */ +#define MDIO_PCS_EEE_ABLE 20 /* EEE Capability register */ +#define MDIO_PCS_EEE_WK_ERR 22 /* EEE wake error counter */ #define MDIO_PHYXS_LNSTAT 24 /* PHY XGXS lane state */ +#define MDIO_AN_EEE_ADV 60 /* EEE advertisement */ +#define MDIO_AN_EEE_LPABLE 61 /* EEE link partner ability */ /* Media-dependent registers. */ #define MDIO_PMA_10GBT_SWAPPOL 130 /* 10GBASE-T pair swap & polarity */ @@ -56,7 +60,6 @@ #define MDIO_PCS_10GBRT_STAT2 33 /* 10GBASE-R/-T PCS status 2 */ #define MDIO_AN_10GBT_CTRL 32 /* 10GBASE-T auto-negotiation control */ #define MDIO_AN_10GBT_STAT 33 /* 10GBASE-T auto-negotiation status */ -#define MDIO_AN_EEE_ADV 60 /* EEE advertisement */ /* LASI (Link Alarm Status Interrupt) registers, defined by XENPAK MSA. */ #define MDIO_PMA_LASI_RXCTRL 0x9000 /* RX_ALARM control */ @@ -82,6 +85,7 @@ #define MDIO_AN_CTRL1_RESTART BMCR_ANRESTART #define MDIO_AN_CTRL1_ENABLE BMCR_ANENABLE #define MDIO_AN_CTRL1_XNP 0x2000 /* Enable extended next page */ +#define MDIO_PCS_CTRL1_CLKSTOP_EN 0x400 /* Stop the clock during LPI */ /* 10 Gb/s */ #define MDIO_CTRL1_SPEED10G (MDIO_CTRL1_SPEEDSELEXT | 0x00) @@ -237,9 +241,25 @@ #define MDIO_AN_10GBT_STAT_MS 0x4000 /* Master/slave config */ #define MDIO_AN_10GBT_STAT_MSFLT 0x8000 /* Master/slave config fault */ -/* AN EEE Advertisement register. */ -#define MDIO_AN_EEE_ADV_100TX 0x0002 /* Advertise 100TX EEE cap */ -#define MDIO_AN_EEE_ADV_1000T 0x0004 /* Advertise 1000T EEE cap */ +/* EEE Supported/Advertisement/LP Advertisement registers. + * + * EEE capability Register (3.20), Advertisement (7.60) and + * Link partner ability (7.61) registers have and can use the same identical + * bit masks. + */ +#define MDIO_AN_EEE_ADV_100TX 0x0002 /* Advertise 100TX EEE cap */ +#define MDIO_AN_EEE_ADV_1000T 0x0004 /* Advertise 1000T EEE cap */ +/* Note: the two defines above can be potentially used by the user-land + * and cannot remove them now. + * So, we define the new generic MDIO_EEE_100TX and MDIO_EEE_1000T macros + * using the previous ones (that can be considered obsolete). + */ +#define MDIO_EEE_100TX MDIO_AN_EEE_ADV_100TX /* 100TX EEE cap */ +#define MDIO_EEE_1000T MDIO_AN_EEE_ADV_1000T /* 1000T EEE cap */ +#define MDIO_EEE_10GT 0x0008 /* 10GT EEE cap */ +#define MDIO_EEE_1000KX 0x0010 /* 1000KX EEE cap */ +#define MDIO_EEE_10GKX4 0x0020 /* 10G KX4 EEE cap */ +#define MDIO_EEE_10GKR 0x0040 /* 10G KR EEE cap */ /* LASI RX_ALARM control/status registers. */ #define MDIO_PMA_LASI_RX_PHYXSLFLT 0x0001 /* PHY XS RX local fault */ diff --git a/include/linux/mii.h b/include/linux/mii.h index 2783eca629a0..8ef3a7a11592 100644 --- a/include/linux/mii.h +++ b/include/linux/mii.h @@ -21,6 +21,8 @@ #define MII_EXPANSION 0x06 /* Expansion register */ #define MII_CTRL1000 0x09 /* 1000BASE-T control */ #define MII_STAT1000 0x0a /* 1000BASE-T status */ +#define MII_MMD_CTRL 0x0d /* MMD Access Control Register */ +#define MII_MMD_DATA 0x0e /* MMD Access Data Register */ #define MII_ESTATUS 0x0f /* Extended Status */ #define MII_DCOUNTER 0x12 /* Disconnect counter */ #define MII_FCSCOUNTER 0x13 /* False carrier counter */ @@ -141,6 +143,13 @@ #define FLOW_CTRL_TX 0x01 #define FLOW_CTRL_RX 0x02 +/* MMD Access Control register fields */ +#define MII_MMD_CTRL_DEVAD_MASK 0x1f /* Mask MMD DEVAD*/ +#define MII_MMD_CTRL_ADDR 0x0000 /* Address */ +#define MII_MMD_CTRL_NOINCR 0x4000 /* no post increment */ +#define MII_MMD_CTRL_INCR_RDWT 0x8000 /* post increment on reads & writes */ +#define MII_MMD_CTRL_INCR_ON_WT 0xC000 /* post increment on writes only */ + /* This structure is used in all SIOCxMIIxxx ioctl calls */ struct mii_ioctl_data { __u16 phy_id; diff --git a/include/linux/phy.h b/include/linux/phy.h index 7eac80a2557b..c35299e4da8e 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -554,6 +554,11 @@ int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask, int (*run)(struct phy_device *)); int phy_scan_fixups(struct phy_device *phydev); +int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable); +int phy_get_eee_err(struct phy_device *phydev); +int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data); +int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data); + int __init mdio_bus_init(void); void mdio_bus_exit(void); -- cgit v1.2.3-59-g8ed1b From b2df1d4f8b95d9d1e3f064cef02fc5c5116b05cf Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 21 Jun 2012 00:19:33 +0200 Subject: PM / Sleep: Separate printing suspend times from initcall_debug Change the behavior of the newly introduced /sys/power/pm_print_times attribute so that its initial value depends on initcall_debug, but setting it to 0 will cause device suspend/resume times not to be printed, even if initcall_debug has been set. This way, the people who use initcall_debug for reasons other than PM debugging will be able to switch the suspend/resume times printing off, if need be. Signed-off-by: Rafael J. Wysocki Reviewed-by: Srivatsa S. Bhat Acked-by: Greg Kroah-Hartman --- drivers/base/power/main.c | 4 +-- drivers/base/power/power.h | 11 ------- include/linux/suspend.h | 6 ++++ kernel/power/Kconfig | 4 +-- kernel/power/main.c | 76 +++++++++++++++++++++++++++------------------- 5 files changed, 54 insertions(+), 47 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 6e4db96958d1..df5f41d2ec95 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -166,7 +166,7 @@ static ktime_t initcall_debug_start(struct device *dev) { ktime_t calltime = ktime_set(0, 0); - if (pm_print_times) { + if (pm_print_times_enabled) { pr_info("calling %s+ @ %i, parent: %s\n", dev_name(dev), task_pid_nr(current), dev->parent ? dev_name(dev->parent) : "none"); @@ -181,7 +181,7 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime, { ktime_t delta, rettime; - if (pm_print_times) { + if (pm_print_times_enabled) { rettime = ktime_get(); delta = ktime_sub(rettime, calltime); pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev), diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 12c77b7ff8e8..eeb4bff9505c 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -85,14 +85,3 @@ static inline int pm_qos_sysfs_add(struct device *dev) { return 0; } static inline void pm_qos_sysfs_remove(struct device *dev) {} #endif - -#ifdef CONFIG_PM_DEBUG - -extern int pm_print_times_enabled; -#define pm_print_times (initcall_debug || pm_print_times_enabled) - -#else /* CONFIG_PM_DEBUG */ - -#define pm_print_times initcall_debug - -#endif /* CONFIG_PM_DEBUG */ diff --git a/include/linux/suspend.h b/include/linux/suspend.h index cd83059fb592..0c808d7fa579 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -408,6 +408,12 @@ static inline void unlock_system_sleep(void) {} #endif /* !CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM_SLEEP_DEBUG +extern bool pm_print_times_enabled; +#else +#define pm_print_times_enabled (false) +#endif + #ifdef CONFIG_PM_AUTOSLEEP /* kernel/power/autosleep.c */ diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 8f9b4eb974e0..a70518c9d82f 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -175,7 +175,7 @@ config PM_TEST_SUSPEND You probably want to have your system's RTC driver statically linked, ensuring that it's available when this test runs. -config CAN_PM_TRACE +config PM_SLEEP_DEBUG def_bool y depends on PM_DEBUG && PM_SLEEP @@ -196,7 +196,7 @@ config PM_TRACE config PM_TRACE_RTC bool "Suspend/resume event tracing" - depends on CAN_PM_TRACE + depends on PM_SLEEP_DEBUG depends on X86 select PM_TRACE ---help--- diff --git a/kernel/power/main.c b/kernel/power/main.c index 7beb3fb3670b..f458238109cc 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -132,38 +132,6 @@ static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr, } power_attr(pm_test); - -/* - * pm_print_times: print time taken by devices to suspend and resume. - * - * show() returns whether printing of suspend and resume times is enabled. - * store() accepts 0 or 1. 0 disables printing and 1 enables it. - */ -int pm_print_times_enabled; - -static ssize_t pm_print_times_show(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) -{ - return sprintf(buf, "%d\n", pm_print_times_enabled); -} - -static ssize_t pm_print_times_store(struct kobject *kobj, - struct kobj_attribute *attr, - const char *buf, size_t n) -{ - unsigned long val; - - if (kstrtoul(buf, 10, &val)) - return -EINVAL; - - if (val > 1) - return -EINVAL; - - pm_print_times_enabled = val; - return n; -} - -power_attr(pm_print_times); #endif /* CONFIG_PM_DEBUG */ #ifdef CONFIG_DEBUG_FS @@ -267,6 +235,47 @@ late_initcall(pm_debugfs_init); #endif /* CONFIG_PM_SLEEP */ +#ifdef CONFIG_PM_SLEEP_DEBUG +/* + * pm_print_times: print time taken by devices to suspend and resume. + * + * show() returns whether printing of suspend and resume times is enabled. + * store() accepts 0 or 1. 0 disables printing and 1 enables it. + */ +bool pm_print_times_enabled; + +static ssize_t pm_print_times_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", pm_print_times_enabled); +} + +static ssize_t pm_print_times_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + if (val > 1) + return -EINVAL; + + pm_print_times_enabled = !!val; + return n; +} + +power_attr(pm_print_times); + +static inline void pm_print_times_init(void) +{ + pm_print_times_enabled = !!initcall_debug; +} +#else /* !CONFIG_PP_SLEEP_DEBUG */ +static inline void pm_print_times_init(void) {} +#endif /* CONFIG_PM_SLEEP_DEBUG */ + struct kobject *power_kobj; /** @@ -562,6 +571,8 @@ static struct attribute * g[] = { #endif #ifdef CONFIG_PM_DEBUG &pm_test_attr.attr, +#endif +#ifdef CONFIG_PM_SLEEP_DEBUG &pm_print_times_attr.attr, #endif #endif @@ -599,6 +610,7 @@ static int __init pm_init(void) error = sysfs_create_group(power_kobj, &attr_group); if (error) return error; + pm_print_times_init(); return pm_autosleep_init(); } -- cgit v1.2.3-59-g8ed1b From 4fe23791a4052ad4c8ba79dab9ff5febc8095714 Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Sat, 30 Jun 2012 16:05:36 -0700 Subject: regulator: add missing defintion regulator_is_supported_voltage This definition is missing when CONFIG_REGULATOR is not defined. This causes compiler errors when compiling sdhci.c. This can be worked around by adding #ifdef CONFIG_REGULATOR .. #endif but since other definitions are there we have defined the missing definition Signed-off-by: Philip Rakity Signed-off-by: Mark Brown --- include/linux/regulator/consumer.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 9ff29ef317c2..da339fd8c755 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -294,6 +294,12 @@ static inline int regulator_get_voltage(struct regulator *regulator) return -EINVAL; } +static inline int regulator_is_supported_voltage(struct regulator *regulator, + int min_uV, int max_uV) +{ + return 0; +} + static inline int regulator_set_current_limit(struct regulator *regulator, int min_uA, int max_uA) { -- cgit v1.2.3-59-g8ed1b From d172f319c1094ef22d2a00f43e8a7da4dd02c8f3 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 29 Jun 2012 09:45:16 +0800 Subject: regulator: tps65217: Convert LDO1 to use regulator_list_voltage_table Convert tps65217_pmic_ldo1_ops to use regulator_list_voltage_table. We have n_voltages and volt_table settings in regulator_desc, so we don't need the table and table_len fields in struct tps_info. Thus remove them from struct tps_info. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps65217-regulator.c | 47 ++++++++++++++++------------------ include/linux/mfd/tps65217.h | 4 --- 2 files changed, 22 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index f5fa05b5bea4..3a2b839276fe 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -26,7 +26,7 @@ #include #include -#define TPS65217_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _em) \ +#define TPS65217_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _em, _t) \ { \ .name = _name, \ .id = _id, \ @@ -38,20 +38,19 @@ .vsel_mask = _vm, \ .enable_reg = TPS65217_REG_ENABLE, \ .enable_mask = _em, \ + .volt_table = _t, \ } \ -#define TPS65217_INFO(_nm, _min, _max, _f1, _f2, _t, _n)\ +#define TPS65217_INFO(_nm, _min, _max, _f1, _f2) \ { \ .name = _nm, \ .min_uV = _min, \ .max_uV = _max, \ .vsel_to_uv = _f1, \ .uv_to_vsel = _f2, \ - .table = _t, \ - .table_len = _n, \ } -static const int LDO1_VSEL_table[] = { +static const unsigned int LDO1_VSEL_table[] = { 1000000, 1100000, 1200000, 1250000, 1300000, 1350000, 1400000, 1500000, 1600000, 1800000, 2500000, 2750000, @@ -128,19 +127,18 @@ static int tps65217_uv_to_vsel2(int uV, unsigned int *vsel) static struct tps_info tps65217_pmic_regs[] = { TPS65217_INFO("DCDC1", 900000, 1800000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64), + tps65217_uv_to_vsel1), TPS65217_INFO("DCDC2", 900000, 3300000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64), + tps65217_uv_to_vsel1), TPS65217_INFO("DCDC3", 900000, 1500000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64), - TPS65217_INFO("LDO1", 1000000, 3300000, NULL, NULL, LDO1_VSEL_table, - 16), + tps65217_uv_to_vsel1), + TPS65217_INFO("LDO1", 1000000, 3300000, NULL, NULL), TPS65217_INFO("LDO2", 900000, 3300000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64), + tps65217_uv_to_vsel1), TPS65217_INFO("LDO3", 1800000, 3300000, tps65217_vsel_to_uv2, - tps65217_uv_to_vsel2, NULL, 32), + tps65217_uv_to_vsel2), TPS65217_INFO("LDO4", 1800000, 3300000, tps65217_vsel_to_uv2, - tps65217_uv_to_vsel2, NULL, 32), + tps65217_uv_to_vsel2), }; static int tps65217_pmic_enable(struct regulator_dev *dev) @@ -230,12 +228,9 @@ static int tps65217_pmic_list_voltage(struct regulator_dev *dev, if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) return -EINVAL; - if (selector >= tps->info[rid]->table_len) + if (selector >= dev->desc->n_voltages) return -EINVAL; - if (tps->info[rid]->table) - return tps->info[rid]->table[selector]; - return tps->info[rid]->vsel_to_uv(selector); } @@ -257,31 +252,33 @@ static struct regulator_ops tps65217_pmic_ldo1_ops = { .disable = tps65217_pmic_disable, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = tps65217_pmic_set_voltage_sel, - .list_voltage = tps65217_pmic_list_voltage, + .list_voltage = regulator_list_voltage_table, }; static const struct regulator_desc regulators[] = { TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC1, TPS65217_DEFDCDCX_DCDC_MASK, - TPS65217_ENABLE_DC1_EN), + TPS65217_ENABLE_DC1_EN, NULL), TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC2, TPS65217_DEFDCDCX_DCDC_MASK, - TPS65217_ENABLE_DC2_EN), + TPS65217_ENABLE_DC2_EN, NULL), TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC3, TPS65217_DEFDCDCX_DCDC_MASK, - TPS65217_ENABLE_DC3_EN), + TPS65217_ENABLE_DC3_EN, NULL), TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, tps65217_pmic_ldo1_ops, 16, TPS65217_REG_DEFLDO1, TPS65217_DEFLDO1_LDO1_MASK, - TPS65217_ENABLE_LDO1_EN), + TPS65217_ENABLE_LDO1_EN, LDO1_VSEL_table), TPS65217_REGULATOR("LDO2", TPS65217_LDO_2, tps65217_pmic_ops, 64, TPS65217_REG_DEFLDO2, TPS65217_DEFLDO2_LDO2_MASK, - TPS65217_ENABLE_LDO2_EN), + TPS65217_ENABLE_LDO2_EN, NULL), TPS65217_REGULATOR("LDO3", TPS65217_LDO_3, tps65217_pmic_ops, 32, TPS65217_REG_DEFLS1, TPS65217_DEFLDO3_LDO3_MASK, - TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN), + TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN, + NULL), TPS65217_REGULATOR("LDO4", TPS65217_LDO_4, tps65217_pmic_ops, 32, TPS65217_REG_DEFLS2, TPS65217_DEFLDO4_LDO4_MASK, - TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN), + TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN, + NULL), }; static int __devinit tps65217_regulator_probe(struct platform_device *pdev) diff --git a/include/linux/mfd/tps65217.h b/include/linux/mfd/tps65217.h index 4e035a41a9b0..3a80da103f3f 100644 --- a/include/linux/mfd/tps65217.h +++ b/include/linux/mfd/tps65217.h @@ -227,8 +227,6 @@ struct tps65217_board { * @max_uV: minimum micro volts * @vsel_to_uv: Function pointer to get voltage from selector * @uv_to_vsel: Function pointer to get selector from voltage - * @table: Table for non-uniform voltage step-size - * @table_len: Length of the voltage table * * This data is used to check the regualtor voltage limits while setting. */ @@ -238,8 +236,6 @@ struct tps_info { int max_uV; int (*vsel_to_uv)(unsigned int vsel); int (*uv_to_vsel)(int uV, unsigned int *vsel); - const int *table; - unsigned int table_len; }; /** -- cgit v1.2.3-59-g8ed1b From e2fb50521c3811eddd60d911bc6d4d191f5d6e61 Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Wed, 23 May 2012 15:08:10 -0500 Subject: omap2+: add drm device Register OMAP DRM/KMS platform device. DMM is split into a separate device using hwmod. Signed-off-by: Andy Gross Signed-off-by: Rob Clark Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/Makefile | 4 +++ arch/arm/mach-omap2/drm.c | 61 ++++++++++++++++++++++++++++++++++ drivers/staging/omapdrm/omap_drv.h | 2 +- drivers/staging/omapdrm/omap_priv.h | 55 ------------------------------ include/linux/platform_data/omap_drm.h | 52 +++++++++++++++++++++++++++++ 5 files changed, 118 insertions(+), 56 deletions(-) create mode 100644 arch/arm/mach-omap2/drm.c delete mode 100644 drivers/staging/omapdrm/omap_priv.h create mode 100644 include/linux/platform_data/omap_drm.h (limited to 'include/linux') diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index fa742f3c2629..a636cdc08876 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -189,6 +189,10 @@ endif # OMAP2420 MSDI controller integration support ("MMC") obj-$(CONFIG_SOC_OMAP2420) += msdi.o +ifneq ($(CONFIG_DRM_OMAP),) +obj-y += drm.o +endif + # Specific board support obj-$(CONFIG_MACH_OMAP_GENERIC) += board-generic.o obj-$(CONFIG_MACH_OMAP_H4) += board-h4.o diff --git a/arch/arm/mach-omap2/drm.c b/arch/arm/mach-omap2/drm.c new file mode 100644 index 000000000000..72e0f01b715c --- /dev/null +++ b/arch/arm/mach-omap2/drm.c @@ -0,0 +1,61 @@ +/* + * DRM/KMS device registration for TI OMAP platforms + * + * Copyright (C) 2012 Texas Instruments + * Author: Rob Clark + * + * 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, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#if defined(CONFIG_DRM_OMAP) || (CONFIG_DRM_OMAP_MODULE) + +static struct platform_device omap_drm_device = { + .dev = { + .coherent_dma_mask = DMA_BIT_MASK(32), + }, + .name = "omapdrm", + .id = 0, +}; + +static int __init omap_init_drm(void) +{ + struct omap_hwmod *oh = NULL; + struct platform_device *pdev; + + /* lookup and populate the DMM information, if present - OMAP4+ */ + oh = omap_hwmod_lookup("dmm"); + + if (oh) { + pdev = omap_device_build(oh->name, -1, oh, NULL, 0, NULL, 0, + false); + WARN(IS_ERR(pdev), "Could not build omap_device for %s\n", + oh->name); + } + + return platform_device_register(&omap_drm_device); + +} + +arch_initcall(omap_init_drm); + +#endif diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h index f238d574da0c..2092a9167d29 100644 --- a/drivers/staging/omapdrm/omap_drv.h +++ b/drivers/staging/omapdrm/omap_drv.h @@ -25,8 +25,8 @@ #include #include #include +#include #include "omap_drm.h" -#include "omap_priv.h" #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) #define VERB(fmt, ...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */ diff --git a/drivers/staging/omapdrm/omap_priv.h b/drivers/staging/omapdrm/omap_priv.h deleted file mode 100644 index ef6441447147..000000000000 --- a/drivers/staging/omapdrm/omap_priv.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * include/drm/omap_priv.h - * - * Copyright (C) 2011 Texas Instruments - * Author: Rob Clark - * - * 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, see . - */ - -#ifndef __OMAP_PRIV_H__ -#define __OMAP_PRIV_H__ - -/* Non-userspace facing APIs - */ - -/* optional platform data to configure the default configuration of which - * pipes/overlays/CRTCs are used.. if this is not provided, then instead the - * first CONFIG_DRM_OMAP_NUM_CRTCS are used, and they are each connected to - * one manager, with priority given to managers that are connected to - * detected devices. Remaining overlays are used as video planes. This - * should be a good default behavior for most cases, but yet there still - * might be times when you wish to do something different. - */ -struct omap_kms_platform_data { - /* overlays to use as CRTCs: */ - int ovl_cnt; - const int *ovl_ids; - - /* overlays to use as video planes: */ - int pln_cnt; - const int *pln_ids; - - int mgr_cnt; - const int *mgr_ids; - - int dev_cnt; - const char **dev_names; -}; - -struct omap_drm_platform_data { - struct omap_kms_platform_data *kms_pdata; - struct omap_dmm_platform_data *dmm_pdata; -}; - -#endif /* __OMAP_DRM_H__ */ diff --git a/include/linux/platform_data/omap_drm.h b/include/linux/platform_data/omap_drm.h new file mode 100644 index 000000000000..3da73bdc2031 --- /dev/null +++ b/include/linux/platform_data/omap_drm.h @@ -0,0 +1,52 @@ +/* + * DRM/KMS platform data for TI OMAP platforms + * + * Copyright (C) 2012 Texas Instruments + * Author: Rob Clark + * + * 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, see . + */ + +#ifndef __PLATFORM_DATA_OMAP_DRM_H__ +#define __PLATFORM_DATA_OMAP_DRM_H__ + +/* + * Optional platform data to configure the default configuration of which + * pipes/overlays/CRTCs are used.. if this is not provided, then instead the + * first CONFIG_DRM_OMAP_NUM_CRTCS are used, and they are each connected to + * one manager, with priority given to managers that are connected to + * detected devices. Remaining overlays are used as video planes. This + * should be a good default behavior for most cases, but yet there still + * might be times when you wish to do something different. + */ +struct omap_kms_platform_data { + /* overlays to use as CRTCs: */ + int ovl_cnt; + const int *ovl_ids; + + /* overlays to use as video planes: */ + int pln_cnt; + const int *pln_ids; + + int mgr_cnt; + const int *mgr_ids; + + int dev_cnt; + const char **dev_names; +}; + +struct omap_drm_platform_data { + struct omap_kms_platform_data *kms_pdata; +}; + +#endif /* __PLATFORM_DATA_OMAP_DRM_H__ */ -- cgit v1.2.3-59-g8ed1b From 3a0c52a6d82cc41da965284412608c74aece34e4 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 2 Jul 2012 09:32:32 +0300 Subject: cfg80211: add 802.11ad (60gHz band) support Add enumerations for both cfg80211 and nl80211. This expands wiphy.bands etc. arrays. Extend channel <-> frequency translation to cover 60g band and modify the rate check logic since there are no legacy mandatory rates (only MCS is used.) Signed-off-by: Vladimir Kondratiev Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlegacy/3945-rs.c | 2 +- include/linux/nl80211.h | 2 ++ include/net/cfg80211.h | 2 ++ net/mac80211/tx.c | 2 ++ net/wireless/core.c | 10 ++++++++-- net/wireless/util.c | 35 +++++++++++++++++++++++++-------- 6 files changed, 42 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/iwlegacy/3945-rs.c b/drivers/net/wireless/iwlegacy/3945-rs.c index 4b10157d8686..d4fd29ad90dc 100644 --- a/drivers/net/wireless/iwlegacy/3945-rs.c +++ b/drivers/net/wireless/iwlegacy/3945-rs.c @@ -946,7 +946,7 @@ il3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) case IEEE80211_BAND_5GHZ: rs_sta->expected_tpt = il3945_expected_tpt_a; break; - case IEEE80211_NUM_BANDS: + default: BUG(); break; } diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 23003272c70e..74cc55c1bf28 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -2545,10 +2545,12 @@ enum nl80211_tx_rate_attributes { * enum nl80211_band - Frequency band * @NL80211_BAND_2GHZ: 2.4 GHz ISM band * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) + * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz) */ enum nl80211_band { NL80211_BAND_2GHZ, NL80211_BAND_5GHZ, + NL80211_BAND_60GHZ, }; /** diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index fa269347355b..0b564e83a24b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -70,11 +70,13 @@ * * @IEEE80211_BAND_2GHZ: 2.4GHz ISM band * @IEEE80211_BAND_5GHZ: around 5GHz band (4.9-5.7) + * @IEEE80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz) * @IEEE80211_NUM_BANDS: number of defined bands */ enum ieee80211_band { IEEE80211_BAND_2GHZ = NL80211_BAND_2GHZ, IEEE80211_BAND_5GHZ = NL80211_BAND_5GHZ, + IEEE80211_BAND_60GHZ = NL80211_BAND_60GHZ, /* keep last */ IEEE80211_NUM_BANDS diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 4e753032e48d..4990f4fb5864 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -140,6 +140,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, if (r->flags & IEEE80211_RATE_MANDATORY_A) mrate = r->bitrate; break; + case IEEE80211_BAND_60GHZ: + /* TODO, for now fall through */ case IEEE80211_NUM_BANDS: WARN_ON(1); break; diff --git a/net/wireless/core.c b/net/wireless/core.c index ca2b95f24846..e13365f1fa63 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -468,8 +468,14 @@ int wiphy_register(struct wiphy *wiphy) continue; sband->band = band; - - if (WARN_ON(!sband->n_channels || !sband->n_bitrates)) + if (WARN_ON(!sband->n_channels)) + return -EINVAL; + /* + * on 60gHz band, there are no legacy rates, so + * n_bitrates is 0 + */ + if (WARN_ON(band != IEEE80211_BAND_60GHZ && + !sband->n_bitrates)) return -EINVAL; /* diff --git a/net/wireless/util.c b/net/wireless/util.c index a9260ac85cf1..0228c64e73d8 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -35,19 +35,29 @@ int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band) { /* see 802.11 17.3.8.3.2 and Annex J * there are overlapping channel numbers in 5GHz and 2GHz bands */ - if (band == IEEE80211_BAND_5GHZ) { - if (chan >= 182 && chan <= 196) - return 4000 + chan * 5; - else - return 5000 + chan * 5; - } else { /* IEEE80211_BAND_2GHZ */ + if (chan <= 0) + return 0; /* not supported */ + switch (band) { + case IEEE80211_BAND_2GHZ: if (chan == 14) return 2484; else if (chan < 14) return 2407 + chan * 5; + break; + case IEEE80211_BAND_5GHZ: + if (chan >= 182 && chan <= 196) + return 4000 + chan * 5; else - return 0; /* not supported */ + return 5000 + chan * 5; + break; + case IEEE80211_BAND_60GHZ: + if (chan < 5) + return 56160 + chan * 2160; + break; + default: + ; } + return 0; /* not supported */ } EXPORT_SYMBOL(ieee80211_channel_to_frequency); @@ -60,8 +70,12 @@ int ieee80211_frequency_to_channel(int freq) return (freq - 2407) / 5; else if (freq >= 4910 && freq <= 4980) return (freq - 4000) / 5; - else + else if (freq <= 45000) /* DMG band lower limit */ return (freq - 5000) / 5; + else if (freq >= 58320 && freq <= 64800) + return (freq - 56160) / 2160; + else + return 0; } EXPORT_SYMBOL(ieee80211_frequency_to_channel); @@ -137,6 +151,11 @@ static void set_mandatory_flags_band(struct ieee80211_supported_band *sband, } WARN_ON(want != 0 && want != 3 && want != 6); break; + case IEEE80211_BAND_60GHZ: + /* check for mandatory HT MCS 1..4 */ + WARN_ON(!sband->ht_cap.ht_supported); + WARN_ON((sband->ht_cap.mcs.rx_mask[0] & 0x1e) != 0x1e); + break; case IEEE80211_NUM_BANDS: WARN_ON(1); break; -- cgit v1.2.3-59-g8ed1b From b188148c690e15284d5b20d384f950506d02e3e2 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 2 Jul 2012 09:32:35 +0300 Subject: wireless: 60g protocol constants Provide various constants as defined by the 802.11ad: frame types, IE's, capability bits, action categories Introduce GCMP cipher, mandatory by 802.11ad Signed-off-by: Vladimir Kondratiev Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 90 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index abf0e5fe6d24..e02fc682bb68 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -47,6 +47,7 @@ #define IEEE80211_FCTL_MOREDATA 0x2000 #define IEEE80211_FCTL_PROTECTED 0x4000 #define IEEE80211_FCTL_ORDER 0x8000 +#define IEEE80211_FCTL_CTL_EXT 0x0f00 #define IEEE80211_SCTL_FRAG 0x000F #define IEEE80211_SCTL_SEQ 0xFFF0 @@ -54,6 +55,7 @@ #define IEEE80211_FTYPE_MGMT 0x0000 #define IEEE80211_FTYPE_CTL 0x0004 #define IEEE80211_FTYPE_DATA 0x0008 +#define IEEE80211_FTYPE_EXT 0x000c /* management */ #define IEEE80211_STYPE_ASSOC_REQ 0x0000 @@ -70,6 +72,7 @@ #define IEEE80211_STYPE_ACTION 0x00D0 /* control */ +#define IEEE80211_STYPE_CTL_EXT 0x0060 #define IEEE80211_STYPE_BACK_REQ 0x0080 #define IEEE80211_STYPE_BACK 0x0090 #define IEEE80211_STYPE_PSPOLL 0x00A0 @@ -97,6 +100,18 @@ #define IEEE80211_STYPE_QOS_CFPOLL 0x00E0 #define IEEE80211_STYPE_QOS_CFACKPOLL 0x00F0 +/* extension, added by 802.11ad */ +#define IEEE80211_STYPE_DMG_BEACON 0x0000 + +/* control extension - for IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTL_EXT */ +#define IEEE80211_CTL_EXT_POLL 0x2000 +#define IEEE80211_CTL_EXT_SPR 0x3000 +#define IEEE80211_CTL_EXT_GRANT 0x4000 +#define IEEE80211_CTL_EXT_DMG_CTS 0x5000 +#define IEEE80211_CTL_EXT_DMG_DTS 0x6000 +#define IEEE80211_CTL_EXT_SSW 0x8000 +#define IEEE80211_CTL_EXT_SSW_FBACK 0x9000 +#define IEEE80211_CTL_EXT_SSW_ACK 0xa000 /* miscellaneous IEEE 802.11 constants */ #define IEEE80211_MAX_FRAG_THRESHOLD 2352 @@ -1191,6 +1206,21 @@ struct ieee80211_vht_mcs_info { #define WLAN_CAPABILITY_QOS (1<<9) #define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10) #define WLAN_CAPABILITY_DSSS_OFDM (1<<13) + +/* DMG (60gHz) 802.11ad */ +/* type - bits 0..1 */ +#define WLAN_CAPABILITY_DMG_TYPE_IBSS (1<<0) /* Tx by: STA */ +#define WLAN_CAPABILITY_DMG_TYPE_PBSS (2<<0) /* Tx by: PCP */ +#define WLAN_CAPABILITY_DMG_TYPE_AP (3<<0) /* Tx by: AP */ + +#define WLAN_CAPABILITY_DMG_CBAP_ONLY (1<<2) +#define WLAN_CAPABILITY_DMG_CBAP_SOURCE (1<<3) +#define WLAN_CAPABILITY_DMG_PRIVACY (1<<4) +#define WLAN_CAPABILITY_DMG_ECPAC (1<<5) + +#define WLAN_CAPABILITY_DMG_SPECTRUM_MGMT (1<<8) +#define WLAN_CAPABILITY_DMG_RADIO_MEASURE (1<<12) + /* measurement */ #define IEEE80211_SPCT_MSR_RPRT_MODE_LATE (1<<0) #define IEEE80211_SPCT_MSR_RPRT_MODE_INCAPABLE (1<<1) @@ -1200,7 +1230,6 @@ struct ieee80211_vht_mcs_info { #define IEEE80211_SPCT_MSR_RPRT_TYPE_CCA 1 #define IEEE80211_SPCT_MSR_RPRT_TYPE_RPI 2 - /* 802.11g ERP information element */ #define WLAN_ERP_NON_ERP_PRESENT (1<<0) #define WLAN_ERP_USE_PROTECTION (1<<1) @@ -1212,6 +1241,16 @@ enum { WLAN_ERP_PREAMBLE_LONG = 1, }; +/* Band ID, 802.11ad #8.4.1.45 */ +enum { + IEEE80211_BANDID_TV_WS = 0, /* TV white spaces */ + IEEE80211_BANDID_SUB1 = 1, /* Sub-1 GHz (excluding TV white spaces) */ + IEEE80211_BANDID_2G = 2, /* 2.4 GHz */ + IEEE80211_BANDID_3G = 3, /* 3.6 GHz */ + IEEE80211_BANDID_5G = 4, /* 4.9 and 5 GHz */ + IEEE80211_BANDID_60G = 5, /* 60 GHz */ +}; + /* Status codes */ enum ieee80211_statuscode { WLAN_STATUS_SUCCESS = 0, @@ -1263,6 +1302,17 @@ enum ieee80211_statuscode { WLAN_STATUS_ANTI_CLOG_REQUIRED = 76, WLAN_STATUS_FCG_NOT_SUPP = 78, WLAN_STATUS_STA_NO_TBTT = 78, + /* 802.11ad */ + WLAN_STATUS_REJECTED_WITH_SUGGESTED_CHANGES = 39, + WLAN_STATUS_REJECTED_FOR_DELAY_PERIOD = 47, + WLAN_STATUS_REJECT_WITH_SCHEDULE = 83, + WLAN_STATUS_PENDING_ADMITTING_FST_SESSION = 86, + WLAN_STATUS_PERFORMING_FST_NOW = 87, + WLAN_STATUS_PENDING_GAP_IN_BA_WINDOW = 88, + WLAN_STATUS_REJECT_U_PID_SETTING = 89, + WLAN_STATUS_REJECT_DSE_BAND = 96, + WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL = 99, + WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT = 103, }; @@ -1422,6 +1472,40 @@ enum ieee80211_eid { WLAN_EID_VHT_CAPABILITY = 191, WLAN_EID_VHT_OPERATION = 192, + + /* 802.11ad */ + WLAN_EID_NON_TX_BSSID_CAP = 83, + WLAN_EID_WAKEUP_SCHEDULE = 143, + WLAN_EID_EXT_SCHEDULE = 144, + WLAN_EID_STA_AVAILABILITY = 145, + WLAN_EID_DMG_TSPEC = 146, + WLAN_EID_DMG_AT = 147, + WLAN_EID_DMG_CAP = 148, + WLAN_EID_DMG_OPERATION = 151, + WLAN_EID_DMG_BSS_PARAM_CHANGE = 152, + WLAN_EID_DMG_BEAM_REFINEMENT = 153, + WLAN_EID_CHANNEL_MEASURE_FEEDBACK = 154, + WLAN_EID_AWAKE_WINDOW = 157, + WLAN_EID_MULTI_BAND = 158, + WLAN_EID_ADDBA_EXT = 159, + WLAN_EID_NEXT_PCP_LIST = 160, + WLAN_EID_PCP_HANDOVER = 161, + WLAN_EID_DMG_LINK_MARGIN = 162, + WLAN_EID_SWITCHING_STREAM = 163, + WLAN_EID_SESSION_TRANSITION = 164, + WLAN_EID_DYN_TONE_PAIRING_REPORT = 165, + WLAN_EID_CLUSTER_REPORT = 166, + WLAN_EID_RELAY_CAP = 167, + WLAN_EID_RELAY_XFER_PARAM_SET = 168, + WLAN_EID_BEAM_LINK_MAINT = 169, + WLAN_EID_MULTIPLE_MAC_ADDR = 170, + WLAN_EID_U_PID = 171, + WLAN_EID_DMG_LINK_ADAPT_ACK = 172, + WLAN_EID_QUIET_PERIOD_REQ = 175, + WLAN_EID_QUIET_PERIOD_RESP = 177, + WLAN_EID_EPAC_POLICY = 182, + WLAN_EID_CLISTER_TIME_OFF = 183, + WLAN_EID_ANTENNA_SECTOR_ID_PATTERN = 190, }; /* Action category code */ @@ -1438,7 +1522,10 @@ enum ieee80211_category { WLAN_CATEGORY_MESH_ACTION = 13, WLAN_CATEGORY_MULTIHOP_ACTION = 14, WLAN_CATEGORY_SELF_PROTECTED = 15, + WLAN_CATEGORY_DMG = 16, WLAN_CATEGORY_WMM = 17, + WLAN_CATEGORY_FST = 18, + WLAN_CATEGORY_UNPROT_DMG = 20, WLAN_CATEGORY_VENDOR_SPECIFIC_PROTECTED = 126, WLAN_CATEGORY_VENDOR_SPECIFIC = 127, }; @@ -1686,6 +1773,7 @@ enum ieee80211_sa_query_action { #define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 #define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 #define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 +#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08 #define WLAN_CIPHER_SUITE_SMS4 0x00147201 -- cgit v1.2.3-59-g8ed1b From cba6d0d64ee53772b285d0c0c288deefbeaf7775 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 2 Jul 2012 07:08:42 -0700 Subject: Revert "rcu: Move PREEMPT_RCU preemption to switch_to() invocation" This reverts commit 616c310e83b872024271c915c1b9ab505b9efad9. (Move PREEMPT_RCU preemption to switch_to() invocation). Testing by Sasha Levin showed that this can result in deadlock due to invoking the scheduler when one of the runqueue locks is held. Because this commit was simply a performance optimization, revert it. Reported-by: Sasha Levin Signed-off-by: Paul E. McKenney Tested-by: Sasha Levin --- arch/um/drivers/mconsole_kern.c | 1 - include/linux/rcupdate.h | 1 - include/linux/rcutiny.h | 6 ++++++ include/linux/sched.h | 10 ---------- kernel/rcutree.c | 1 + kernel/rcutree.h | 1 + kernel/rcutree_plugin.h | 14 +++++++++++--- kernel/sched/core.c | 1 - 8 files changed, 19 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c index 88e466b159dc..43b39d61b538 100644 --- a/arch/um/drivers/mconsole_kern.c +++ b/arch/um/drivers/mconsole_kern.c @@ -705,7 +705,6 @@ static void stack_proc(void *arg) struct task_struct *from = current, *to = arg; to->thread.saved_task = from; - rcu_switch_from(from); switch_to(from, to, from); } diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 26d1a47591f1..9cac722b169c 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -184,7 +184,6 @@ static inline int rcu_preempt_depth(void) /* Internal to kernel */ extern void rcu_sched_qs(int cpu); extern void rcu_bh_qs(int cpu); -extern void rcu_preempt_note_context_switch(void); extern void rcu_check_callbacks(int cpu, int user); struct notifier_block; extern void rcu_idle_enter(void); diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h index 854dc4c5c271..4e56a9c69a35 100644 --- a/include/linux/rcutiny.h +++ b/include/linux/rcutiny.h @@ -87,6 +87,10 @@ static inline void kfree_call_rcu(struct rcu_head *head, #ifdef CONFIG_TINY_RCU +static inline void rcu_preempt_note_context_switch(void) +{ +} + static inline int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies) { *delta_jiffies = ULONG_MAX; @@ -95,6 +99,7 @@ static inline int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies) #else /* #ifdef CONFIG_TINY_RCU */ +void rcu_preempt_note_context_switch(void); int rcu_preempt_needs_cpu(void); static inline int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies) @@ -108,6 +113,7 @@ static inline int rcu_needs_cpu(int cpu, unsigned long *delta_jiffies) static inline void rcu_note_context_switch(int cpu) { rcu_sched_qs(cpu); + rcu_preempt_note_context_switch(); } /* diff --git a/include/linux/sched.h b/include/linux/sched.h index 4059c0f33f07..06a4c5f4f55c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1871,22 +1871,12 @@ static inline void rcu_copy_process(struct task_struct *p) INIT_LIST_HEAD(&p->rcu_node_entry); } -static inline void rcu_switch_from(struct task_struct *prev) -{ - if (prev->rcu_read_lock_nesting != 0) - rcu_preempt_note_context_switch(); -} - #else static inline void rcu_copy_process(struct task_struct *p) { } -static inline void rcu_switch_from(struct task_struct *prev) -{ -} - #endif #ifdef CONFIG_SMP diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 38ecdda3f55f..4b97bba7396e 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -201,6 +201,7 @@ void rcu_note_context_switch(int cpu) { trace_rcu_utilization("Start context switch"); rcu_sched_qs(cpu); + rcu_preempt_note_context_switch(cpu); trace_rcu_utilization("End context switch"); } EXPORT_SYMBOL_GPL(rcu_note_context_switch); diff --git a/kernel/rcutree.h b/kernel/rcutree.h index ea056495783e..19b61ac1079f 100644 --- a/kernel/rcutree.h +++ b/kernel/rcutree.h @@ -444,6 +444,7 @@ DECLARE_PER_CPU(char, rcu_cpu_has_work); /* 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, diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index 5271a020887e..3e4899459f3d 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -153,7 +153,7 @@ static void rcu_preempt_qs(int cpu) * * Caller must disable preemption. */ -void rcu_preempt_note_context_switch(void) +static void rcu_preempt_note_context_switch(int cpu) { struct task_struct *t = current; unsigned long flags; @@ -164,7 +164,7 @@ void rcu_preempt_note_context_switch(void) (t->rcu_read_unlock_special & RCU_READ_UNLOCK_BLOCKED) == 0) { /* Possibly blocking in an RCU read-side critical section. */ - rdp = __this_cpu_ptr(rcu_preempt_state.rda); + 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; @@ -228,7 +228,7 @@ void rcu_preempt_note_context_switch(void) * means that we continue to block the current grace period. */ local_irq_save(flags); - rcu_preempt_qs(smp_processor_id()); + rcu_preempt_qs(cpu); local_irq_restore(flags); } @@ -1001,6 +1001,14 @@ void rcu_force_quiescent_state(void) } 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. diff --git a/kernel/sched/core.c b/kernel/sched/core.c index d5594a4268d4..eaead2df6aa8 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2081,7 +2081,6 @@ context_switch(struct rq *rq, struct task_struct *prev, #endif /* Here we just switch the register state and the stack. */ - rcu_switch_from(prev); switch_to(prev, next, prev); barrier(); -- cgit v1.2.3-59-g8ed1b From 172708d002e0a2aca032b04fe6f2b8525c29244a Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 16 May 2012 15:23:45 -0700 Subject: rcu: Add a gcc-style structure initializer for RCU pointers RCU_INIT_POINTER() returns a value that is never used, and which should be abolished due to terminal ugliness: q = RCU_INIT_POINTER(global_p, p); However, there are two uses that cannot be handled by a do-while formulation because they do gcc-style initialization: RCU_INIT_POINTER(.real_cred, &init_cred), RCU_INIT_POINTER(.cred, &init_cred), This usage is clever, but not necessarily the nicest approach. This commit therefore creates an RCU_POINTER_INITIALIZER() macro that is specifically designed for gcc-style initialization. Signed-off-by: Paul E. McKenney Acked-by: David Howells --- include/linux/rcupdate.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include/linux') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 9cac722b169c..ffe24c09e53d 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -905,6 +905,14 @@ static inline notrace void rcu_read_unlock_sched_notrace(void) #define RCU_INIT_POINTER(p, v) \ p = (typeof(*v) __force __rcu *)(v) +/** + * RCU_POINTER_INITIALIZER() - statically initialize an RCU protected pointer + * + * GCC-style initialization for an RCU-protected pointer in a structure field. + */ +#define RCU_POINTER_INITIALIZER(p, v) \ + .p = (typeof(*v) __force __rcu *)(v) + static __always_inline bool __is_kfree_rcu_offset(unsigned long offset) { return offset < 4096; -- cgit v1.2.3-59-g8ed1b From d36cc701b28983ea2c8d80afe68aae5452aea3bf Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 16 May 2012 15:33:15 -0700 Subject: rcu: Use new RCU_POINTER_INITIALIZER for gcc-style initializations This commit applies the INIT_RCU_POINTER() macro to all uses of RCU_POINTER_INITIALIZER() that were all too cleverly creating gcc-style initializations. Signed-off-by: Paul E. McKenney Acked-by: David Howells --- include/linux/init_task.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 9e65eff6af3b..8a7476186990 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -168,8 +168,8 @@ extern struct cred init_cred; .children = LIST_HEAD_INIT(tsk.children), \ .sibling = LIST_HEAD_INIT(tsk.sibling), \ .group_leader = &tsk, \ - RCU_INIT_POINTER(.real_cred, &init_cred), \ - RCU_INIT_POINTER(.cred, &init_cred), \ + RCU_POINTER_INITIALIZER(real_cred, &init_cred), \ + RCU_POINTER_INITIALIZER(cred, &init_cred), \ .comm = INIT_TASK_COMM, \ .thread = INIT_THREAD, \ .fs = &init_fs, \ -- cgit v1.2.3-59-g8ed1b From d1b88eb9e3bccaa43fb5d1bde1cbe210b3434731 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 16 May 2012 15:42:30 -0700 Subject: rcu: Remove return value from RCU_INIT_POINTER() The return value from RCU_INIT_POINTER() is not used, and using it would be quite ugly, for example: q = RCU_INIT_POINTER(global_p, p); To prevent this sort of ugliness from appearing, this commit wraps RCU_INIT_POINTER() in a do-while loop. Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett Acked-by: David Howells --- include/linux/rcupdate.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index ffe24c09e53d..abf44d89c6ce 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -903,7 +903,9 @@ static inline notrace void rcu_read_unlock_sched_notrace(void) * the reader-accessible portions of the linked structure. */ #define RCU_INIT_POINTER(p, v) \ - p = (typeof(*v) __force __rcu *)(v) + do { \ + p = (typeof(*v) __force __rcu *)(v); \ + } while (0) /** * RCU_POINTER_INITIALIZER() - statically initialize an RCU protected pointer -- cgit v1.2.3-59-g8ed1b From e5c1f444d28b1a9eaf9c3927041db0414f684ef4 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 16 May 2012 16:31:38 -0700 Subject: key: Remove extraneous parentheses from rcu_assign_keypointer() This commit removes the extraneous parentheses from rcu_assign_keypointer() so that rcu_assign_pointer() can be wrapped in do-while. It also wraps rcu_assign_keypointer() in a do-while and parenthesizes its final argument, as suggested by David Howells. Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- include/linux/key.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/key.h b/include/linux/key.h index 4cd22ed627ef..cef3b315ba7c 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -303,7 +303,9 @@ static inline bool key_is_instantiated(const struct key *key) rwsem_is_locked(&((struct key *)(KEY))->sem))) #define rcu_assign_keypointer(KEY, PAYLOAD) \ - (rcu_assign_pointer((KEY)->payload.rcudata, PAYLOAD)) +do { \ + rcu_assign_pointer((KEY)->payload.rcudata, (PAYLOAD)); \ +} while (0) #ifdef CONFIG_SYSCTL extern ctl_table key_sysctls[]; -- cgit v1.2.3-59-g8ed1b From e9023c4061054cbf59c5288068118a4d0b152f01 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 16 May 2012 15:51:08 -0700 Subject: rcu: Remove return value from rcu_assign_pointer() The return value from rcu_assign_pointer() is not used, and using it would be quite ugly, for example: q = rcu_assign_pointer(global_p, p); To prevent this sort of ugliness from spreading, this commit wraps rcu_assign_pointer() in a do-while loop. Reported-by: Mathieu Desnoyers Reported-by: Josh Triplett Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- include/linux/rcupdate.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index abf44d89c6ce..fb8e6db511d7 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -513,10 +513,10 @@ static inline void rcu_preempt_sleep_check(void) (_________p1); \ }) #define __rcu_assign_pointer(p, v, space) \ - ({ \ + do { \ smp_wmb(); \ (p) = (typeof(*v) __force space *)(v); \ - }) + } while (0) /** @@ -851,7 +851,7 @@ static inline notrace void rcu_read_unlock_sched_notrace(void) * * Assigns the specified value to the specified RCU-protected * pointer, ensuring that any concurrent RCU readers will see - * any prior initialization. Returns the value assigned. + * any prior initialization. * * Inserts memory barriers on architectures that require them * (which is most of them), and also prevents the compiler from -- cgit v1.2.3-59-g8ed1b From 2a3fa843b555d202e682bf08c65ee1a4a93c79cf Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 21 May 2012 11:58:36 -0700 Subject: rcu: Consolidate tree/tiny __rcu_read_{,un}lock() implementations The CONFIG_TREE_PREEMPT_RCU and CONFIG_TINY_PREEMPT_RCU versions of __rcu_read_lock() and __rcu_read_unlock() are identical, so this commit consolidates them into kernel/rcupdate.h. Signed-off-by: Paul E. McKenney Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- include/linux/rcupdate.h | 1 + kernel/rcupdate.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ kernel/rcutiny_plugin.h | 47 +---------------------------------------------- kernel/rcutree_plugin.h | 47 +---------------------------------------------- 4 files changed, 47 insertions(+), 92 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index fb8e6db511d7..698555ebf49b 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -147,6 +147,7 @@ extern void synchronize_sched(void); extern void __rcu_read_lock(void); extern void __rcu_read_unlock(void); +extern void rcu_read_unlock_special(struct task_struct *t); void synchronize_rcu(void); /* diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index 95cba41ce1e9..4e6a61b15e86 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c @@ -53,6 +53,50 @@ #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; + 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); + /* * Check for a task exiting while in a preemptible-RCU read-side * critical section, clean up if so. No need to issue warnings, diff --git a/kernel/rcutiny_plugin.h b/kernel/rcutiny_plugin.h index fc31a2d65100..a269b0da0eb6 100644 --- a/kernel/rcutiny_plugin.h +++ b/kernel/rcutiny_plugin.h @@ -132,7 +132,6 @@ static struct rcu_preempt_ctrlblk rcu_preempt_ctrlblk = { RCU_TRACE(.rcb.name = "rcu_preempt") }; -static void rcu_read_unlock_special(struct task_struct *t); static int rcu_preempted_readers_exp(void); static void rcu_report_exp_done(void); @@ -526,24 +525,12 @@ void rcu_preempt_note_context_switch(void) local_irq_restore(flags); } -/* - * Tiny-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(); /* needed if we ever invoke rcu_read_lock in rcutiny.c */ -} -EXPORT_SYMBOL_GPL(__rcu_read_lock); - /* * 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. */ -static noinline void rcu_read_unlock_special(struct task_struct *t) +void rcu_read_unlock_special(struct task_struct *t) { int empty; int empty_exp; @@ -626,38 +613,6 @@ static noinline void rcu_read_unlock_special(struct task_struct *t) local_irq_restore(flags); } -/* - * Tiny-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; - - barrier(); /* needed if we ever invoke rcu_read_unlock in rcutiny.c */ - if (t->rcu_read_lock_nesting != 1) - --t->rcu_read_lock_nesting; - else { - t->rcu_read_lock_nesting = INT_MIN; - 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); - /* * Check for a quiescent state from the current CPU. When a task blocks, * the task is recorded in the rcu_preempt_ctrlblk structure, which is diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index 3e4899459f3d..4b6b17cdf66b 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -78,7 +78,6 @@ struct rcu_state rcu_preempt_state = RCU_STATE_INITIALIZER(rcu_preempt); DEFINE_PER_CPU(struct rcu_data, rcu_preempt_data); static struct rcu_state *rcu_state = &rcu_preempt_state; -static void rcu_read_unlock_special(struct task_struct *t); static int rcu_preempted_readers_exp(struct rcu_node *rnp); /* @@ -232,18 +231,6 @@ static void rcu_preempt_note_context_switch(int cpu) local_irq_restore(flags); } -/* - * Tree-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(); /* needed if we ever invoke rcu_read_lock in rcutree.c */ -} -EXPORT_SYMBOL_GPL(__rcu_read_lock); - /* * Check for preempted RCU readers blocking the current grace period * for the specified rcu_node structure. If the caller needs a reliable @@ -310,7 +297,7 @@ static struct list_head *rcu_next_node_entry(struct task_struct *t, * notify RCU core processing or task having blocked during the RCU * read-side critical section. */ -static noinline void rcu_read_unlock_special(struct task_struct *t) +void rcu_read_unlock_special(struct task_struct *t) { int empty; int empty_exp; @@ -418,38 +405,6 @@ static noinline void rcu_read_unlock_special(struct task_struct *t) } } -/* - * Tree-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; - 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); - #ifdef CONFIG_RCU_CPU_STALL_VERBOSE /* -- cgit v1.2.3-59-g8ed1b From 28f5c693d03530eb15c5354f758b789189b98c37 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 25 May 2012 14:25:58 -0700 Subject: rcu: Remove function versions of __kfree_rcu and __is_kfree_rcu_offset Commit d8169d4c (Make __kfree_rcu() less dependent on compiler choices) added cpp macro versions of __kfree_rcu() and __is_kfree_rcu_offset(), but failed to remove the old inline-function versions. This commit does this cleanup. Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- include/linux/rcupdate.h | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 698555ebf49b..31568c734525 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -916,24 +916,6 @@ static inline notrace void rcu_read_unlock_sched_notrace(void) #define RCU_POINTER_INITIALIZER(p, v) \ .p = (typeof(*v) __force __rcu *)(v) -static __always_inline bool __is_kfree_rcu_offset(unsigned long offset) -{ - return offset < 4096; -} - -static __always_inline -void __kfree_rcu(struct rcu_head *head, unsigned long offset) -{ - typedef void (*rcu_callback)(struct rcu_head *); - - BUILD_BUG_ON(!__builtin_constant_p(offset)); - - /* See the kfree_rcu() header comment. */ - BUILD_BUG_ON(!__is_kfree_rcu_offset(offset)); - - kfree_call_rcu(head, (rcu_callback)offset); -} - /* * Does the specified offset indicate that the corresponding rcu_head * structure can be handled by kfree_rcu()? -- cgit v1.2.3-59-g8ed1b From 62fde6edf12b60fddb13a3f0a779c8be0bb7447e Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 22 May 2012 22:10:24 -0700 Subject: rcu: Make __call_rcu() handle invocation from idle Although __call_rcu() is handled correctly when called from a momentary non-idle period, if it is called on a CPU that RCU believes to be idle on RCU_FAST_NO_HZ kernels, the callback might be indefinitely postponed. This commit therefore ensures that RCU is aware of the new callback and has a chance to force the CPU out of dyntick-idle mode when a new callback is posted. Reported-by: Frederic Weisbecker Signed-off-by: Paul E. McKenney Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- include/linux/rcupdate.h | 13 ++++--------- kernel/rcutree.c | 15 +++++++++------ 2 files changed, 13 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 31568c734525..26f6417f0264 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -256,6 +256,10 @@ 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) */ @@ -267,15 +271,6 @@ static inline bool rcu_lockdep_current_cpu_online(void) #ifdef CONFIG_DEBUG_LOCK_ALLOC -#ifdef CONFIG_PROVE_RCU -extern int rcu_is_cpu_idle(void); -#else /* !CONFIG_PROVE_RCU */ -static inline int rcu_is_cpu_idle(void) -{ - return 0; -} -#endif /* else !CONFIG_PROVE_RCU */ - static inline void rcu_lock_acquire(struct lockdep_map *map) { lock_acquire(map, 0, 0, 2, 1, NULL, _THIS_IP_); diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 89addada3e3a..a4a9c916ad36 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -585,8 +585,6 @@ void rcu_nmi_exit(void) WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1); } -#ifdef CONFIG_PROVE_RCU - /** * rcu_is_cpu_idle - see if RCU thinks that the current CPU is idle * @@ -604,7 +602,7 @@ int rcu_is_cpu_idle(void) } EXPORT_SYMBOL(rcu_is_cpu_idle); -#ifdef CONFIG_HOTPLUG_CPU +#if defined(CONFIG_PROVE_RCU) && defined(CONFIG_HOTPLUG_CPU) /* * Is the current CPU online? Disable preemption to avoid false positives @@ -645,9 +643,7 @@ bool rcu_lockdep_current_cpu_online(void) } EXPORT_SYMBOL_GPL(rcu_lockdep_current_cpu_online); -#endif /* #ifdef CONFIG_HOTPLUG_CPU */ - -#endif /* #ifdef CONFIG_PROVE_RCU */ +#endif /* #if defined(CONFIG_PROVE_RCU) && defined(CONFIG_HOTPLUG_CPU) */ /** * rcu_is_cpu_rrupt_from_idle - see if idle or immediately interrupted from idle @@ -1904,6 +1900,13 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu), else trace_rcu_callback(rsp->name, head, rdp->qlen_lazy, rdp->qlen); + /* + * 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()) + invoke_rcu_core(); + /* If interrupts were disabled, don't dive into RCU core. */ if (irqs_disabled_flags(flags)) { local_irq_restore(flags); -- cgit v1.2.3-59-g8ed1b From 4fa3b6cb1bc8c14b81b4c8ffdfd3f2500a7e9367 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 5 Jun 2012 15:53:53 -0700 Subject: rcu: Fix qlen_lazy breakage Commit d8169d4c (Make __kfree_rcu() less dependent on compiler choices) created a macro out of an inline function in order to avoid build breakage for certain combinations of gcc flags. Unfortunately, it also converted a kfree_call_rcu() to a call_rcu(), which made the rcu_data structure's ->qlen_lazy field lose counts. This commit therefore changes the call_rcu() back to kfree_call_rcu(). Signed-off-by: Paul E. McKenney Signed-off-by: Paul E. McKenney --- include/linux/rcupdate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 9cac722b169c..46d45e0f9134 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -935,7 +935,7 @@ void __kfree_rcu(struct rcu_head *head, unsigned long offset) #define __kfree_rcu(head, offset) \ do { \ BUILD_BUG_ON(!__is_kfree_rcu_offset(offset)); \ - call_rcu(head, (void (*)(struct rcu_head *))(unsigned long)(offset)); \ + kfree_call_rcu(head, (void (*)(struct rcu_head *))(unsigned long)(offset)); \ } while (0) /** -- cgit v1.2.3-59-g8ed1b From ce4409b5821f170c018b94bacb36df1ffadb751f Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 25 Jun 2012 19:22:09 +0800 Subject: video: mxsfb: move mxsfb.h into include/linux Move mxsfb.h into include/linux, so that mxsfb driver does not have to include header. Signed-off-by: Shawn Guo --- arch/arm/mach-mxs/devices-mx23.h | 2 +- arch/arm/mach-mxs/devices-mx28.h | 2 +- arch/arm/mach-mxs/devices/platform-mxsfb.c | 2 +- arch/arm/mach-mxs/include/mach/mxsfb.h | 49 ------------------------------ drivers/video/mxsfb.c | 2 +- include/linux/mxsfb.h | 49 ++++++++++++++++++++++++++++++ 6 files changed, 53 insertions(+), 53 deletions(-) delete mode 100644 arch/arm/mach-mxs/include/mach/mxsfb.h create mode 100644 include/linux/mxsfb.h (limited to 'include/linux') diff --git a/arch/arm/mach-mxs/devices-mx23.h b/arch/arm/mach-mxs/devices-mx23.h index 9acdd6387047..9ee5cede3d42 100644 --- a/arch/arm/mach-mxs/devices-mx23.h +++ b/arch/arm/mach-mxs/devices-mx23.h @@ -10,7 +10,7 @@ */ #include #include -#include +#include #include static inline int mx23_add_duart(void) diff --git a/arch/arm/mach-mxs/devices-mx28.h b/arch/arm/mach-mxs/devices-mx28.h index 84b2960df117..fcab431060f4 100644 --- a/arch/arm/mach-mxs/devices-mx28.h +++ b/arch/arm/mach-mxs/devices-mx28.h @@ -10,7 +10,7 @@ */ #include #include -#include +#include #include static inline int mx28_add_duart(void) diff --git a/arch/arm/mach-mxs/devices/platform-mxsfb.c b/arch/arm/mach-mxs/devices/platform-mxsfb.c index 5a75b7180f74..76b53f73418e 100644 --- a/arch/arm/mach-mxs/devices/platform-mxsfb.c +++ b/arch/arm/mach-mxs/devices/platform-mxsfb.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #ifdef CONFIG_SOC_IMX23 struct platform_device *__init mx23_add_mxsfb( diff --git a/arch/arm/mach-mxs/include/mach/mxsfb.h b/arch/arm/mach-mxs/include/mach/mxsfb.h deleted file mode 100644 index e4d79791515e..000000000000 --- a/arch/arm/mach-mxs/include/mach/mxsfb.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#ifndef __MACH_FB_H -#define __MACH_FB_H - -#include - -#define STMLCDIF_8BIT 1 /** pixel data bus to the display is of 8 bit width */ -#define STMLCDIF_16BIT 0 /** pixel data bus to the display is of 16 bit width */ -#define STMLCDIF_18BIT 2 /** pixel data bus to the display is of 18 bit width */ -#define STMLCDIF_24BIT 3 /** pixel data bus to the display is of 24 bit width */ - -#define FB_SYNC_DATA_ENABLE_HIGH_ACT (1 << 6) -#define FB_SYNC_DOTCLK_FAILING_ACT (1 << 7) /* failing/negtive edge sampling */ - -struct mxsfb_platform_data { - struct fb_videomode *mode_list; - unsigned mode_count; - - unsigned default_bpp; - - unsigned dotclk_delay; /* refer manual HW_LCDIF_VDCTRL4 register */ - unsigned ld_intf_width; /* refer STMLCDIF_* macros */ - - unsigned fb_size; /* Size of the video memory. If zero a - * default will be used - */ - unsigned long fb_phys; /* physical address for the video memory. If - * zero the framebuffer memory will be dynamically - * allocated. If specified,fb_size must also be specified. - * fb_phys must be unused by Linux. - */ -}; - -#endif /* __MACH_FB_H */ diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index abbe691047bd..08dad8d8abba 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -46,7 +46,7 @@ #include #include #include -#include +#include #define REG_SET 4 #define REG_CLR 8 diff --git a/include/linux/mxsfb.h b/include/linux/mxsfb.h new file mode 100644 index 000000000000..f14943d55315 --- /dev/null +++ b/include/linux/mxsfb.h @@ -0,0 +1,49 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __LINUX_MXSFB_H +#define __LINUX_MXSFB_H + +#include + +#define STMLCDIF_8BIT 1 /** pixel data bus to the display is of 8 bit width */ +#define STMLCDIF_16BIT 0 /** pixel data bus to the display is of 16 bit width */ +#define STMLCDIF_18BIT 2 /** pixel data bus to the display is of 18 bit width */ +#define STMLCDIF_24BIT 3 /** pixel data bus to the display is of 24 bit width */ + +#define FB_SYNC_DATA_ENABLE_HIGH_ACT (1 << 6) +#define FB_SYNC_DOTCLK_FAILING_ACT (1 << 7) /* failing/negtive edge sampling */ + +struct mxsfb_platform_data { + struct fb_videomode *mode_list; + unsigned mode_count; + + unsigned default_bpp; + + unsigned dotclk_delay; /* refer manual HW_LCDIF_VDCTRL4 register */ + unsigned ld_intf_width; /* refer STMLCDIF_* macros */ + + unsigned fb_size; /* Size of the video memory. If zero a + * default will be used + */ + unsigned long fb_phys; /* physical address for the video memory. If + * zero the framebuffer memory will be dynamically + * allocated. If specified,fb_size must also be specified. + * fb_phys must be unused by Linux. + */ +}; + +#endif /* __LINUX_MXSFB_H */ -- cgit v1.2.3-59-g8ed1b From dc7fd275ae60ef8edf952aff2a62462f5d892fd4 Mon Sep 17 00:00:00 2001 From: ShuoX Liu Date: Tue, 3 Jul 2012 19:05:31 +0200 Subject: cpuidle: move field disable from per-driver to per-cpu Andrew J.Schorr raises a question. When he changes the disable setting on a single CPU, it affects all the other CPUs. Basically, currently, the disable field is per-driver instead of per-cpu. All the C states of the same driver are shared by all CPU in the same machine. The patch changes the `disable' field to per-cpu, so we could set this separately for each cpu. Signed-off-by: ShuoX Liu Reported-by: Andrew J.Schorr Reviewed-by: Yanmin Zhang Signed-off-by: Andrew Morton Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/cpuidle.c | 1 - drivers/cpuidle/governors/menu.c | 5 +++-- drivers/cpuidle/sysfs.c | 21 ++++++++++++--------- include/linux/cpuidle.h | 2 +- 4 files changed, 16 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index d90519cec880..04e4b7674a47 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -265,7 +265,6 @@ static void poll_idle_init(struct cpuidle_driver *drv) state->power_usage = -1; state->flags = 0; state->enter = poll_idle; - state->disable = 0; } #else static void poll_idle_init(struct cpuidle_driver *drv) {} diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 06335756ea14..8391d93f57d5 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -281,7 +281,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) * unless the timer is happening really really soon. */ if (data->expected_us > 5 && - drv->states[CPUIDLE_DRIVER_STATE_START].disable == 0) + dev->states_usage[CPUIDLE_DRIVER_STATE_START].disable == 0) data->last_state_idx = CPUIDLE_DRIVER_STATE_START; /* @@ -290,8 +290,9 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) */ for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { struct cpuidle_state *s = &drv->states[i]; + struct cpuidle_state_usage *su = &dev->states_usage[i]; - if (s->disable) + if (su->disable) continue; if (s->target_residency > data->predicted_us) continue; diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 88032b4dc6d2..5f809e337b89 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -217,7 +217,8 @@ struct cpuidle_state_attr { struct attribute attr; ssize_t (*show)(struct cpuidle_state *, \ struct cpuidle_state_usage *, char *); - ssize_t (*store)(struct cpuidle_state *, const char *, size_t); + ssize_t (*store)(struct cpuidle_state *, \ + struct cpuidle_state_usage *, const char *, size_t); }; #define define_one_state_ro(_name, show) \ @@ -233,21 +234,22 @@ static ssize_t show_state_##_name(struct cpuidle_state *state, \ return sprintf(buf, "%u\n", state->_name);\ } -#define define_store_state_function(_name) \ +#define define_store_state_ull_function(_name) \ static ssize_t store_state_##_name(struct cpuidle_state *state, \ + struct cpuidle_state_usage *state_usage, \ const char *buf, size_t size) \ { \ - long value; \ + unsigned long long value; \ int err; \ if (!capable(CAP_SYS_ADMIN)) \ return -EPERM; \ - err = kstrtol(buf, 0, &value); \ + err = kstrtoull(buf, 0, &value); \ if (err) \ return err; \ if (value) \ - state->disable = 1; \ + state_usage->_name = 1; \ else \ - state->disable = 0; \ + state_usage->_name = 0; \ return size; \ } @@ -273,8 +275,8 @@ define_show_state_ull_function(usage) define_show_state_ull_function(time) define_show_state_str_function(name) define_show_state_str_function(desc) -define_show_state_function(disable) -define_store_state_function(disable) +define_show_state_ull_function(disable) +define_store_state_ull_function(disable) define_one_state_ro(name, show_state_name); define_one_state_ro(desc, show_state_desc); @@ -318,10 +320,11 @@ static ssize_t cpuidle_state_store(struct kobject *kobj, { int ret = -EIO; struct cpuidle_state *state = kobj_to_state(kobj); + struct cpuidle_state_usage *state_usage = kobj_to_state_usage(kobj); struct cpuidle_state_attr *cattr = attr_to_stateattr(attr); if (cattr->store) - ret = cattr->store(state, buf, size); + ret = cattr->store(state, state_usage, buf, size); return ret; } diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 6c26a3da0e03..8570012a535a 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -34,6 +34,7 @@ struct cpuidle_driver; struct cpuidle_state_usage { void *driver_data; + unsigned long long disable; unsigned long long usage; unsigned long long time; /* in US */ }; @@ -46,7 +47,6 @@ struct cpuidle_state { unsigned int exit_latency; /* in US */ int power_usage; /* in mW */ unsigned int target_residency; /* in US */ - unsigned int disable; int (*enter) (struct cpuidle_device *dev, struct cpuidle_driver *drv, -- cgit v1.2.3-59-g8ed1b From 6e797a078824b30afbfae6cc4b1c2b21c51761ef Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 16 Jun 2012 15:20:11 +0200 Subject: PM / cpuidle: Add driver reference counter Add a reference counter for the cpuidle driver, so that it can't be unregistered when it is in use. Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/driver.c | 29 ++++++++++++++++++++++++++++- include/linux/cpuidle.h | 6 +++++- 2 files changed, 33 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 40cd3f3024df..58bf3b1ac9c4 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -16,6 +16,7 @@ static struct cpuidle_driver *cpuidle_curr_driver; DEFINE_SPINLOCK(cpuidle_driver_lock); +int cpuidle_driver_refcount; static void __cpuidle_register_driver(struct cpuidle_driver *drv) { @@ -89,8 +90,34 @@ void cpuidle_unregister_driver(struct cpuidle_driver *drv) } spin_lock(&cpuidle_driver_lock); - cpuidle_curr_driver = NULL; + + if (!WARN_ON(cpuidle_driver_refcount > 0)) + cpuidle_curr_driver = NULL; + spin_unlock(&cpuidle_driver_lock); } EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); + +struct cpuidle_driver *cpuidle_driver_ref(void) +{ + struct cpuidle_driver *drv; + + spin_lock(&cpuidle_driver_lock); + + drv = cpuidle_curr_driver; + cpuidle_driver_refcount++; + + spin_unlock(&cpuidle_driver_lock); + return drv; +} + +void cpuidle_driver_unref(void) +{ + spin_lock(&cpuidle_driver_lock); + + if (!WARN_ON(cpuidle_driver_refcount <= 0)) + cpuidle_driver_refcount--; + + spin_unlock(&cpuidle_driver_lock); +} diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 8570012a535a..27cfced7b57b 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -136,7 +136,9 @@ struct cpuidle_driver { extern void disable_cpuidle(void); extern int cpuidle_idle_call(void); extern int cpuidle_register_driver(struct cpuidle_driver *drv); -struct cpuidle_driver *cpuidle_get_driver(void); +extern struct cpuidle_driver *cpuidle_get_driver(void); +extern struct cpuidle_driver *cpuidle_driver_ref(void); +extern void cpuidle_driver_unref(void); extern void cpuidle_unregister_driver(struct cpuidle_driver *drv); extern int cpuidle_register_device(struct cpuidle_device *dev); extern void cpuidle_unregister_device(struct cpuidle_device *dev); @@ -157,6 +159,8 @@ static inline int cpuidle_idle_call(void) { return -ENODEV; } static inline int cpuidle_register_driver(struct cpuidle_driver *drv) {return -ENODEV; } static inline struct cpuidle_driver *cpuidle_get_driver(void) {return NULL; } +static inline struct cpuidle_driver *cpuidle_driver_ref(void) {return NULL; } +static inline void cpuidle_driver_unref(void) {} static inline void cpuidle_unregister_driver(struct cpuidle_driver *drv) { } static inline int cpuidle_register_device(struct cpuidle_device *dev) {return -ENODEV; } -- cgit v1.2.3-59-g8ed1b From cbc9ef0287ab764d3da0129efa673808df641fe3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 3 Jul 2012 19:07:42 +0200 Subject: PM / Domains: Add preliminary support for cpuidle, v2 On some systems there are CPU cores located in the same power domains as I/O devices. Then, power can only be removed from the domain if all I/O devices in it are not in use and the CPU core is idle. Add preliminary support for that to the generic PM domains framework. First, the platform is expected to provide a cpuidle driver with one extra state designated for use with the generic PM domains code. This state should be initially disabled and its exit_latency value should be set to whatever time is needed to bring up the CPU core itself after restoring power to it, not including the domain's power on latency. Its .enter() callback should point to a procedure that will remove power from the domain containing the CPU core at the end of the CPU power transition. The remaining characteristics of the extra cpuidle state, referred to as the "domain" cpuidle state below, (e.g. power usage, target residency) should be populated in accordance with the properties of the hardware. Next, the platform should execute genpd_attach_cpuidle() on the PM domain containing the CPU core. That will cause the generic PM domains framework to treat that domain in a special way such that: * When all devices in the domain have been suspended and it is about to be turned off, the states of the devices will be saved, but power will not be removed from the domain. Instead, the "domain" cpuidle state will be enabled so that power can be removed from the domain when the CPU core is idle and the state has been chosen as the target by the cpuidle governor. * When the first I/O device in the domain is resumed and __pm_genpd_poweron(() is called for the first time after power has been removed from the domain, the "domain" cpuidle state will be disabled to avoid subsequent surprise power removals via cpuidle. The effective exit_latency value of the "domain" cpuidle state depends on the time needed to bring up the CPU core itself after restoring power to it as well as on the power on latency of the domain containing the CPU core. Thus the "domain" cpuidle state's exit_latency has to be recomputed every time the domain's power on latency is updated, which may happen every time power is restored to the domain, if the measured power on latency is greater than the latency stored in the corresponding generic_pm_domain structure. Signed-off-by: Rafael J. Wysocki Reviewed-by: Kevin Hilman --- drivers/base/power/domain.c | 117 +++++++++++++++++++++++++++++++++++++++ drivers/cpuidle/cpuidle.c | 1 + drivers/cpuidle/governors/menu.c | 3 +- include/linux/cpuidle.h | 1 + include/linux/pm_domain.h | 17 ++++++ 5 files changed, 138 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index da1d52576ec9..4b5f090fccb6 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -139,6 +139,19 @@ static void genpd_set_active(struct generic_pm_domain *genpd) genpd->status = GPD_STATE_ACTIVE; } +static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd) +{ + s64 usecs64; + + if (!genpd->cpu_data) + return; + + usecs64 = genpd->power_on_latency_ns; + do_div(usecs64, NSEC_PER_USEC); + usecs64 += genpd->cpu_data->saved_exit_latency; + genpd->cpu_data->idle_state->exit_latency = usecs64; +} + /** * __pm_genpd_poweron - Restore power to a given PM domain and its masters. * @genpd: PM domain to power up. @@ -176,6 +189,13 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) return 0; } + if (genpd->cpu_data) { + cpuidle_pause_and_lock(); + genpd->cpu_data->idle_state->disabled = true; + cpuidle_resume_and_unlock(); + goto out; + } + /* * The list is guaranteed not to change while the loop below is being * executed, unless one of the masters' .power_on() callbacks fiddles @@ -215,6 +235,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) if (elapsed_ns > genpd->power_on_latency_ns) { genpd->power_on_latency_ns = elapsed_ns; genpd->max_off_time_changed = true; + genpd_recalc_cpu_exit_latency(genpd); if (genpd->name) pr_warning("%s: Power-on latency exceeded, " "new value %lld ns\n", genpd->name, @@ -222,6 +243,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) } } + out: genpd_set_active(genpd); return 0; @@ -455,6 +477,21 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) } } + if (genpd->cpu_data) { + /* + * If cpu_data is set, cpuidle should turn the domain off when + * the CPU in it is idle. In that case we don't decrement the + * subdomain counts of the master domains, so that power is not + * removed from the current domain prematurely as a result of + * cutting off the masters' power. + */ + genpd->status = GPD_STATE_POWER_OFF; + cpuidle_pause_and_lock(); + genpd->cpu_data->idle_state->disabled = false; + cpuidle_resume_and_unlock(); + goto out; + } + if (genpd->power_off) { ktime_t time_start; s64 elapsed_ns; @@ -1600,6 +1637,86 @@ int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) } EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); +int genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state) +{ + struct cpuidle_driver *cpuidle_drv; + struct gpd_cpu_data *cpu_data; + struct cpuidle_state *idle_state; + int ret = 0; + + if (IS_ERR_OR_NULL(genpd) || state < 0) + return -EINVAL; + + genpd_acquire_lock(genpd); + + if (genpd->cpu_data) { + ret = -EEXIST; + goto out; + } + cpu_data = kzalloc(sizeof(*cpu_data), GFP_KERNEL); + if (!cpu_data) { + ret = -ENOMEM; + goto out; + } + cpuidle_drv = cpuidle_driver_ref(); + if (!cpuidle_drv) { + ret = -ENODEV; + goto out; + } + if (cpuidle_drv->state_count <= state) { + ret = -EINVAL; + goto err; + } + idle_state = &cpuidle_drv->states[state]; + if (!idle_state->disabled) { + ret = -EAGAIN; + goto err; + } + cpu_data->idle_state = idle_state; + cpu_data->saved_exit_latency = idle_state->exit_latency; + genpd->cpu_data = cpu_data; + genpd_recalc_cpu_exit_latency(genpd); + + out: + genpd_release_lock(genpd); + return ret; + + err: + cpuidle_driver_unref(); + goto out; +} + +int genpd_detach_cpuidle(struct generic_pm_domain *genpd) +{ + struct gpd_cpu_data *cpu_data; + struct cpuidle_state *idle_state; + int ret = 0; + + if (IS_ERR_OR_NULL(genpd)) + return -EINVAL; + + genpd_acquire_lock(genpd); + + cpu_data = genpd->cpu_data; + if (!cpu_data) { + ret = -ENODEV; + goto out; + } + idle_state = cpu_data->idle_state; + if (!idle_state->disabled) { + ret = -EAGAIN; + goto out; + } + idle_state->exit_latency = cpu_data->saved_exit_latency; + cpuidle_driver_unref(); + genpd->cpu_data = NULL; + kfree(cpu_data); + + out: + genpd_release_lock(genpd); + return ret; +} + /* Default device callbacks for generic PM domains. */ /** diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 04e4b7674a47..0132706251df 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -265,6 +265,7 @@ static void poll_idle_init(struct cpuidle_driver *drv) state->power_usage = -1; state->flags = 0; state->enter = poll_idle; + state->disabled = false; } #else static void poll_idle_init(struct cpuidle_driver *drv) {} diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 8391d93f57d5..5b1f2c372c1f 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -281,6 +281,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) * unless the timer is happening really really soon. */ if (data->expected_us > 5 && + !drv->states[CPUIDLE_DRIVER_STATE_START].disabled && dev->states_usage[CPUIDLE_DRIVER_STATE_START].disable == 0) data->last_state_idx = CPUIDLE_DRIVER_STATE_START; @@ -292,7 +293,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) struct cpuidle_state *s = &drv->states[i]; struct cpuidle_state_usage *su = &dev->states_usage[i]; - if (su->disable) + if (s->disabled || su->disable) continue; if (s->target_residency > data->predicted_us) continue; diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 27cfced7b57b..8684a0d07b87 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -47,6 +47,7 @@ struct cpuidle_state { unsigned int exit_latency; /* in US */ int power_usage; /* in mW */ unsigned int target_residency; /* in US */ + bool disabled; /* disabled on all CPUs */ int (*enter) (struct cpuidle_device *dev, struct cpuidle_driver *drv, diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 30f794eb3826..2febe31d2675 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -15,6 +15,7 @@ #include #include #include +#include enum gpd_status { GPD_STATE_ACTIVE = 0, /* PM domain is active */ @@ -45,6 +46,11 @@ struct gpd_dev_ops { bool (*active_wakeup)(struct device *dev); }; +struct gpd_cpu_data { + unsigned int saved_exit_latency; + struct cpuidle_state *idle_state; +}; + struct generic_pm_domain { struct dev_pm_domain domain; /* PM domain operations */ struct list_head gpd_list_node; /* Node in the global PM domains list */ @@ -75,6 +81,7 @@ struct generic_pm_domain { bool max_off_time_changed; bool cached_power_down_ok; struct device_node *of_node; /* Node in device tree */ + struct gpd_cpu_data *cpu_data; }; static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd) @@ -155,6 +162,8 @@ extern int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops, struct gpd_timing_data *td); extern int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td); +extern int genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state); +extern int genpd_detach_cpuidle(struct generic_pm_domain *genpd); extern void pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off); @@ -211,6 +220,14 @@ static inline int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) { return -ENOSYS; } +static inline int genpd_attach_cpuidle(struct generic_pm_domain *genpd, int st) +{ + return -ENOSYS; +} +static inline int genpd_detach_cpuidle(struct generic_pm_domain *genpd) +{ + return -ENOSYS; +} static inline void pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off) { -- cgit v1.2.3-59-g8ed1b From 36c1ed821bd11fb9a3f99a060b1553c114dc2d07 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 15 Jun 2012 15:07:24 -0400 Subject: KVM: Guard mmu_notifier specific code with CONFIG_MMU_NOTIFIER In order to avoid compilation failure when KVM is not compiled in, guard the mmu_notifier specific sections with both CONFIG_MMU_NOTIFIER and KVM_ARCH_WANT_MMU_NOTIFIER, like it is being done in the rest of the KVM code. Signed-off-by: Marc Zyngier Signed-off-by: Christoffer Dall Signed-off-by: Marcelo Tosatti --- include/linux/kvm_host.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index c7f77876c9b3..e3c86f8c86c9 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -306,7 +306,7 @@ struct kvm { struct hlist_head irq_ack_notifier_list; #endif -#ifdef KVM_ARCH_WANT_MMU_NOTIFIER +#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER) struct mmu_notifier mmu_notifier; unsigned long mmu_notifier_seq; long mmu_notifier_count; @@ -780,7 +780,7 @@ struct kvm_stats_debugfs_item { extern struct kvm_stats_debugfs_item debugfs_entries[]; extern struct dentry *kvm_debugfs_dir; -#ifdef KVM_ARCH_WANT_MMU_NOTIFIER +#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER) static inline int mmu_notifier_retry(struct kvm_vcpu *vcpu, unsigned long mmu_seq) { if (unlikely(vcpu->kvm->mmu_notifier_count)) -- cgit v1.2.3-59-g8ed1b From 79511ed3225a64f6b7fc749f4f9c1ed82f24f729 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 27 Jun 2012 14:23:10 +0100 Subject: regulator: core: Allow fixed enable_time to be set in the regulator_desc Many regulators have a fixed specification for their enable time. Allow this to be set in the regulator_desc as a number to save them having to implement an explicit operation. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 2 +- include/linux/regulator/driver.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index aa82c0465f4f..6e488aa6f1e8 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1189,7 +1189,7 @@ overflow_err: static int _regulator_get_enable_time(struct regulator_dev *rdev) { if (!rdev->desc->ops->enable_time) - return 0; + return rdev->desc->enable_time; return rdev->desc->ops->enable_time(rdev); } diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 84f999ed394b..176bd4335581 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -180,6 +180,8 @@ enum regulator_type { * @vsel_mask: Mask for register bitfield used for selector * @enable_reg: Register for control when using regmap enable/disable ops * @enable_mask: Mask for control when using regmap enable/disable ops + * + * @enable_time: Time taken for initial enable of regulator (in uS). */ struct regulator_desc { const char *name; @@ -201,6 +203,8 @@ struct regulator_desc { unsigned int vsel_mask; unsigned int enable_reg; unsigned int enable_mask; + + unsigned int enable_time; }; /** -- cgit v1.2.3-59-g8ed1b From 65f735082de35aa4d44e8d0afe862798d0e09e29 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 27 Jun 2012 14:14:38 +0100 Subject: regulator: core: Add core support for GPIO controlled enable lines It is very common for regulators to support having their enable signal controlled by a GPIO. Since there are a bunch of fiddly things to get right like handling the operations when the enable signal is tied to a rail and it's just replicated code add support for this to the core. Drivers should set ena_gpio in their config if they have a GPIO control, using ena_gpio_flags to specify any flags (including GPIOF_OUT_INIT_ for the initial state) and ena_gpio_invert if the GPIO is active low. The core will then override any enable and disable operations the driver has and instead control the specified GPIO. This will in the future also allow us to further extend the core by identifying when several enable signals have been tied together and handling this properly. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 35 ++++++++++++++++++++++++++++++++++- include/linux/regulator/driver.h | 11 +++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 82650a16a975..8d81bafcb721 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1491,7 +1492,11 @@ static int _regulator_do_enable(struct regulator_dev *rdev) trace_regulator_enable(rdev_get_name(rdev)); - if (rdev->desc->ops->enable) { + if (rdev->ena_gpio) { + gpio_set_value_cansleep(rdev->ena_gpio, + !rdev->ena_gpio_invert); + rdev->ena_gpio_state = 1; + } else if (rdev->desc->ops->enable) { ret = rdev->desc->ops->enable(rdev); if (ret < 0) return ret; @@ -1846,6 +1851,10 @@ EXPORT_SYMBOL_GPL(regulator_disable_regmap); static int _regulator_is_enabled(struct regulator_dev *rdev) { + /* A GPIO control always takes precedence */ + if (rdev->ena_gpio) + return rdev->ena_gpio_state; + /* If we don't know then assume that the regulator is always on */ if (!rdev->desc->ops->is_enabled) return 1; @@ -3243,6 +3252,26 @@ regulator_register(const struct regulator_desc *regulator_desc, dev_set_drvdata(&rdev->dev, rdev); + if (config->ena_gpio) { + ret = gpio_request_one(config->ena_gpio, + GPIOF_DIR_OUT | config->ena_gpio_flags, + rdev_get_name(rdev)); + if (ret != 0) { + rdev_err(rdev, "Failed to request enable GPIO%d: %d\n", + config->ena_gpio, ret); + goto clean; + } + + rdev->ena_gpio = config->ena_gpio; + rdev->ena_gpio_invert = config->ena_gpio_invert; + + if (config->ena_gpio_flags & GPIOF_OUT_INIT_HIGH) + rdev->ena_gpio_state = 1; + + if (rdev->ena_gpio_invert) + rdev->ena_gpio_state = !rdev->ena_gpio_state; + } + /* set regulator constraints */ if (init_data) constraints = &init_data->constraints; @@ -3311,6 +3340,8 @@ unset_supplies: scrub: if (rdev->supply) regulator_put(rdev->supply); + if (rdev->ena_gpio) + gpio_free(rdev->ena_gpio); kfree(rdev->constraints); device_unregister(&rdev->dev); /* device core frees rdev */ @@ -3344,6 +3375,8 @@ void regulator_unregister(struct regulator_dev *rdev) unset_regulator_supplies(rdev); list_del(&rdev->list); kfree(rdev->constraints); + if (rdev->ena_gpio) + gpio_free(rdev->ena_gpio); device_unregister(&rdev->dev); mutex_unlock(®ulator_list_mutex); } diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 176bd4335581..b1b7b8b43ece 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -220,6 +220,9 @@ struct regulator_desc { * @of_node: OpenFirmware node to parse for device tree bindings (may be * NULL). * @regmap: regmap to use for core regmap helpers + * @ena_gpio: GPIO controlling regulator enable. + * @ena_gpio_invert: Sense for GPIO enable control. + * @ena_gpio_flags: Flags to use when calling gpio_request_one() */ struct regulator_config { struct device *dev; @@ -227,6 +230,10 @@ struct regulator_config { void *driver_data; struct device_node *of_node; struct regmap *regmap; + + int ena_gpio; + unsigned int ena_gpio_invert:1; + unsigned int ena_gpio_flags; }; /* @@ -265,6 +272,10 @@ struct regulator_dev { void *reg_data; /* regulator_dev data */ struct dentry *debugfs; + + int ena_gpio; + unsigned int ena_gpio_invert:1; + unsigned int ena_gpio_state:1; }; struct regulator_dev * -- cgit v1.2.3-59-g8ed1b From 5d589b092ab212bbcc27828167e1c036e7fc77d2 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 23 May 2012 21:22:40 +0800 Subject: pinctrl: remove pinctrl_remove_gpio_range The gpio ranges will be automatically removed when the pinctrl driver is unregistered. Acked-by: Stephen Warren Signed-off-by: Dong Aisheng Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 19 +++++-------------- drivers/pinctrl/pinctrl-tegra.c | 1 - drivers/pinctrl/pinctrl-u300.c | 2 -- include/linux/pinctrl/pinctrl.h | 2 -- 4 files changed, 5 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 50d9c289cffd..902428dfb37e 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -332,20 +332,6 @@ void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, } EXPORT_SYMBOL_GPL(pinctrl_add_gpio_range); -/** - * pinctrl_remove_gpio_range() - remove a range of GPIOs fro a pin controller - * @pctldev: pin controller device to remove the range from - * @range: the GPIO range to remove - */ -void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, - struct pinctrl_gpio_range *range) -{ - mutex_lock(&pinctrl_mutex); - list_del(&range->node); - mutex_unlock(&pinctrl_mutex); -} -EXPORT_SYMBOL_GPL(pinctrl_remove_gpio_range); - /** * pinctrl_get_group_selector() - returns the group selector for a group * @pctldev: the pin controller handling the group @@ -1480,6 +1466,7 @@ EXPORT_SYMBOL_GPL(pinctrl_register); */ void pinctrl_unregister(struct pinctrl_dev *pctldev) { + struct pinctrl_gpio_range *range, *n; if (pctldev == NULL) return; @@ -1495,6 +1482,10 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) /* Destroy descriptor tree */ pinctrl_free_pindescs(pctldev, pctldev->desc->pins, pctldev->desc->npins); + /* remove gpio ranges map */ + list_for_each_entry_safe(range, n, &pctldev->gpio_ranges, node) + list_del(&range->node); + kfree(pctldev); mutex_unlock(&pinctrl_mutex); diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c index b6934867d8d3..07228b17a370 100644 --- a/drivers/pinctrl/pinctrl-tegra.c +++ b/drivers/pinctrl/pinctrl-tegra.c @@ -764,7 +764,6 @@ int __devexit tegra_pinctrl_remove(struct platform_device *pdev) { struct tegra_pmx *pmx = platform_get_drvdata(pdev); - pinctrl_remove_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range); pinctrl_unregister(pmx->pctl); return 0; diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c index 6cd697a079f9..5f43f9ae36d3 100644 --- a/drivers/pinctrl/pinctrl-u300.c +++ b/drivers/pinctrl/pinctrl-u300.c @@ -1175,8 +1175,6 @@ static int __devexit u300_pmx_remove(struct platform_device *pdev) struct u300_pmx *upmx = platform_get_drvdata(pdev); int i; - for (i = 0; i < ARRAY_SIZE(u300_gpio_ranges); i++) - pinctrl_remove_gpio_range(upmx->pctl, &u300_gpio_ranges[i]); pinctrl_unregister(upmx->pctl); iounmap(upmx->virtbase); release_mem_region(upmx->phybase, upmx->physize); diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 3b894a668d32..170a588a55b6 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -131,8 +131,6 @@ extern void pinctrl_unregister(struct pinctrl_dev *pctldev); extern bool pin_is_valid(struct pinctrl_dev *pctldev, int pin); extern void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range); -extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, - struct pinctrl_gpio_range *range); extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev); extern void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev); #else -- cgit v1.2.3-59-g8ed1b From 3e5e00b654997aa2c3998d30f7213b9611eb23d7 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 23 May 2012 21:22:41 +0800 Subject: pinctrl: add pinctrl_add_gpio_ranges function Often GPIO ranges are added in batch, so create a special function for that. Acked-by: Stephen Warren Signed-off-by: Dong Aisheng Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 11 +++++++++++ include/linux/pinctrl/pinctrl.h | 3 +++ 2 files changed, 14 insertions(+) (limited to 'include/linux') diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 902428dfb37e..fb7f3bebdc69 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -332,6 +332,17 @@ void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, } EXPORT_SYMBOL_GPL(pinctrl_add_gpio_range); +void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *ranges, + unsigned nranges) +{ + int i; + + for (i = 0; i < nranges; i++) + pinctrl_add_gpio_range(pctldev, &ranges[i]); +} +EXPORT_SYMBOL_GPL(pinctrl_add_gpio_ranges); + /** * pinctrl_get_group_selector() - returns the group selector for a group * @pctldev: the pin controller handling the group diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 170a588a55b6..69393a662532 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -131,6 +131,9 @@ extern void pinctrl_unregister(struct pinctrl_dev *pctldev); extern bool pin_is_valid(struct pinctrl_dev *pctldev, int pin); extern void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range); +extern void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *ranges, + unsigned nranges); extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev); extern void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev); #else -- cgit v1.2.3-59-g8ed1b From 5a081caa0414b9bbb82c17ffab9d6fe66edbb72f Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Wed, 6 Jun 2012 10:09:25 +0300 Subject: rpmsg: avoid premature deallocation of endpoints When an inbound message arrives, the rpmsg core looks up its associated endpoint and invokes the registered callback. If a message arrives while its endpoint is being removed (because the rpmsg driver was removed, or a recovery of a remote processor has kicked in) we must ensure atomicity, i.e.: - Either the ept is removed before it is found or - The ept is found but will not be freed until the callback returns This is achieved by maintaining a per-ept reference count, which, when drops to zero, will trigger deallocation of the ept. With this in hand, it is now forbidden to directly deallocate epts once they have been added to the endpoints idr. Cc: stable Reported-by: Fernando Guzman Lugo Signed-off-by: Ohad Ben-Cohen --- drivers/rpmsg/virtio_rpmsg_bus.c | 36 ++++++++++++++++++++++++++++++++++-- include/linux/rpmsg.h | 3 +++ 2 files changed, 37 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 75506ec2840e..9623327ba509 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -188,6 +188,26 @@ static int rpmsg_uevent(struct device *dev, struct kobj_uevent_env *env) rpdev->id.name); } +/** + * __ept_release() - deallocate an rpmsg endpoint + * @kref: the ept's reference count + * + * This function deallocates an ept, and is invoked when its @kref refcount + * drops to zero. + * + * Never invoke this function directly! + */ +static void __ept_release(struct kref *kref) +{ + struct rpmsg_endpoint *ept = container_of(kref, struct rpmsg_endpoint, + refcount); + /* + * At this point no one holds a reference to ept anymore, + * so we can directly free it + */ + kfree(ept); +} + /* for more info, see below documentation of rpmsg_create_ept() */ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, struct rpmsg_channel *rpdev, rpmsg_rx_cb_t cb, @@ -206,6 +226,8 @@ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, return NULL; } + kref_init(&ept->refcount); + ept->rpdev = rpdev; ept->cb = cb; ept->priv = priv; @@ -238,7 +260,7 @@ rem_idr: idr_remove(&vrp->endpoints, request); free_ept: mutex_unlock(&vrp->endpoints_lock); - kfree(ept); + kref_put(&ept->refcount, __ept_release); return NULL; } @@ -306,7 +328,7 @@ __rpmsg_destroy_ept(struct virtproc_info *vrp, struct rpmsg_endpoint *ept) idr_remove(&vrp->endpoints, ept->addr); mutex_unlock(&vrp->endpoints_lock); - kfree(ept); + kref_put(&ept->refcount, __ept_release); } /** @@ -790,7 +812,13 @@ static void rpmsg_recv_done(struct virtqueue *rvq) /* use the dst addr to fetch the callback of the appropriate user */ mutex_lock(&vrp->endpoints_lock); + ept = idr_find(&vrp->endpoints, msg->dst); + + /* let's make sure no one deallocates ept while we use it */ + if (ept) + kref_get(&ept->refcount); + mutex_unlock(&vrp->endpoints_lock); if (ept && ept->cb) @@ -798,6 +826,10 @@ static void rpmsg_recv_done(struct virtqueue *rvq) else dev_warn(dev, "msg received with no recepient\n"); + /* farewell, ept, we don't need you anymore */ + if (ept) + kref_put(&ept->refcount, __ept_release); + /* publish the real size of the buffer */ sg_init_one(&sg, msg, RPMSG_BUF_SIZE); diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index a8e50e44203c..195f373590b8 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h @@ -38,6 +38,7 @@ #include #include #include +#include /* The feature bitmap for virtio rpmsg */ #define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */ @@ -120,6 +121,7 @@ typedef void (*rpmsg_rx_cb_t)(struct rpmsg_channel *, void *, int, void *, u32); /** * struct rpmsg_endpoint - binds a local rpmsg address to its user * @rpdev: rpmsg channel device + * @refcount: when this drops to zero, the ept is deallocated * @cb: rx callback handler * @addr: local rpmsg address * @priv: private data for the driver's use @@ -140,6 +142,7 @@ typedef void (*rpmsg_rx_cb_t)(struct rpmsg_channel *, void *, int, void *, u32); */ struct rpmsg_endpoint { struct rpmsg_channel *rpdev; + struct kref refcount; rpmsg_rx_cb_t cb; u32 addr; void *priv; -- cgit v1.2.3-59-g8ed1b From 15fd943af50dbc5f7f4de33835795c72595f7bf4 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Thu, 7 Jun 2012 15:39:35 +0300 Subject: rpmsg: make sure inflight messages don't invoke just-removed callbacks When inbound messages arrive, rpmsg core looks up their associated endpoint (by destination address) and then invokes their callback. We've made sure that endpoints will never be de-allocated after they were found by rpmsg core, but we also need to protect against the (rare) scenario where the rpmsg driver was just removed, and its callback function isn't available anymore. This is achieved by introducing a callback mutex, which must be taken before the callback is invoked, and, obviously, before it is removed. Cc: stable Reported-by: Fernando Guzman Lugo Signed-off-by: Ohad Ben-Cohen --- drivers/rpmsg/virtio_rpmsg_bus.c | 25 +++++++++++++++++++------ include/linux/rpmsg.h | 3 +++ 2 files changed, 22 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 9623327ba509..39d3aa41adda 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -227,6 +227,7 @@ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, } kref_init(&ept->refcount); + mutex_init(&ept->cb_lock); ept->rpdev = rpdev; ept->cb = cb; @@ -324,10 +325,16 @@ EXPORT_SYMBOL(rpmsg_create_ept); static void __rpmsg_destroy_ept(struct virtproc_info *vrp, struct rpmsg_endpoint *ept) { + /* make sure new inbound messages can't find this ept anymore */ mutex_lock(&vrp->endpoints_lock); idr_remove(&vrp->endpoints, ept->addr); mutex_unlock(&vrp->endpoints_lock); + /* make sure in-flight inbound messages won't invoke cb anymore */ + mutex_lock(&ept->cb_lock); + ept->cb = NULL; + mutex_unlock(&ept->cb_lock); + kref_put(&ept->refcount, __ept_release); } @@ -821,14 +828,20 @@ static void rpmsg_recv_done(struct virtqueue *rvq) mutex_unlock(&vrp->endpoints_lock); - if (ept && ept->cb) - ept->cb(ept->rpdev, msg->data, msg->len, ept->priv, msg->src); - else - dev_warn(dev, "msg received with no recepient\n"); + if (ept) { + /* make sure ept->cb doesn't go away while we use it */ + mutex_lock(&ept->cb_lock); - /* farewell, ept, we don't need you anymore */ - if (ept) + if (ept->cb) + ept->cb(ept->rpdev, msg->data, msg->len, ept->priv, + msg->src); + + mutex_unlock(&ept->cb_lock); + + /* farewell, ept, we don't need you anymore */ kref_put(&ept->refcount, __ept_release); + } else + dev_warn(dev, "msg received with no recepient\n"); /* publish the real size of the buffer */ sg_init_one(&sg, msg, RPMSG_BUF_SIZE); diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index 195f373590b8..82a673905edb 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h @@ -39,6 +39,7 @@ #include #include #include +#include /* The feature bitmap for virtio rpmsg */ #define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */ @@ -123,6 +124,7 @@ typedef void (*rpmsg_rx_cb_t)(struct rpmsg_channel *, void *, int, void *, u32); * @rpdev: rpmsg channel device * @refcount: when this drops to zero, the ept is deallocated * @cb: rx callback handler + * @cb_lock: must be taken before accessing/changing @cb * @addr: local rpmsg address * @priv: private data for the driver's use * @@ -144,6 +146,7 @@ struct rpmsg_endpoint { struct rpmsg_channel *rpdev; struct kref refcount; rpmsg_rx_cb_t cb; + struct mutex cb_lock; u32 addr; void *priv; }; -- cgit v1.2.3-59-g8ed1b From f057bbb6f9ed0fb61ea11105c9ef0ed5ac1a354d Mon Sep 17 00:00:00 2001 From: Rostislav Lisovy Date: Wed, 4 Jul 2012 05:32:03 +0200 Subject: net: em_canid: Ematch rule to match CAN frames according to their identifiers This ematch makes it possible to classify CAN frames (AF_CAN) according to their identifiers. This functionality can not be easily achieved with existing classifiers, such as u32, because CAN identifier is always stored in native endianness, whereas u32 expects Network byte order. Signed-off-by: Rostislav Lisovy Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/linux/can.h | 3 + include/linux/pkt_cls.h | 5 +- net/sched/Kconfig | 10 ++ net/sched/Makefile | 1 + net/sched/em_canid.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 257 insertions(+), 2 deletions(-) create mode 100644 net/sched/em_canid.c (limited to 'include/linux') diff --git a/include/linux/can.h b/include/linux/can.h index 1a66cf6112ae..018055efc034 100644 --- a/include/linux/can.h +++ b/include/linux/can.h @@ -38,6 +38,9 @@ */ typedef __u32 canid_t; +#define CAN_SFF_ID_BITS 11 +#define CAN_EFF_ID_BITS 29 + /* * Controller Area Network Error Message Frame Mask structure * diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h index defbde203d07..38fbd4bc20ab 100644 --- a/include/linux/pkt_cls.h +++ b/include/linux/pkt_cls.h @@ -451,8 +451,9 @@ enum { #define TCF_EM_U32 3 #define TCF_EM_META 4 #define TCF_EM_TEXT 5 -#define TCF_EM_VLAN 6 -#define TCF_EM_MAX 6 +#define TCF_EM_VLAN 6 +#define TCF_EM_CANID 7 +#define TCF_EM_MAX 7 enum { TCF_EM_PROG_TC diff --git a/net/sched/Kconfig b/net/sched/Kconfig index e7a8976bf25c..4a5d2bd4f789 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -507,6 +507,16 @@ config NET_EMATCH_TEXT To compile this code as a module, choose M here: the module will be called em_text. +config NET_EMATCH_CANID + tristate "CAN Identifier" + depends on NET_EMATCH && CAN + ---help--- + Say Y here if you want to be able to classify CAN frames based + on CAN Identifier. + + To compile this code as a module, choose M here: the + module will be called em_canid. + config NET_CLS_ACT bool "Actions" ---help--- diff --git a/net/sched/Makefile b/net/sched/Makefile index 5940a1992f0d..bcada751b4ef 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -55,3 +55,4 @@ obj-$(CONFIG_NET_EMATCH_NBYTE) += em_nbyte.o obj-$(CONFIG_NET_EMATCH_U32) += em_u32.o obj-$(CONFIG_NET_EMATCH_META) += em_meta.o obj-$(CONFIG_NET_EMATCH_TEXT) += em_text.o +obj-$(CONFIG_NET_EMATCH_CANID) += em_canid.o diff --git a/net/sched/em_canid.c b/net/sched/em_canid.c new file mode 100644 index 000000000000..bfd34e4c1afc --- /dev/null +++ b/net/sched/em_canid.c @@ -0,0 +1,240 @@ +/* + * em_canid.c Ematch rule to match CAN frames according to their CAN IDs + * + * This program is free software; you can distribute 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. + * + * Idea: Oliver Hartkopp + * Copyright: (c) 2011 Czech Technical University in Prague + * (c) 2011 Volkswagen Group Research + * Authors: Michal Sojka + * Pavel Pisa + * Rostislav Lisovy + * Funded by: Volkswagen Group Research + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define EM_CAN_RULES_MAX 500 + +struct canid_match { + /* For each SFF CAN ID (11 bit) there is one record in this bitfield */ + DECLARE_BITMAP(match_sff, (1 << CAN_SFF_ID_BITS)); + + int rules_count; + int sff_rules_count; + int eff_rules_count; + + /* + * Raw rules copied from netlink message; Used for sending + * information to userspace (when 'tc filter show' is invoked) + * AND when matching EFF frames + */ + struct can_filter rules_raw[]; +}; + +/** + * em_canid_get_id() - Extracts Can ID out of the sk_buff structure. + */ +static canid_t em_canid_get_id(struct sk_buff *skb) +{ + /* CAN ID is stored within the data field */ + struct can_frame *cf = (struct can_frame *)skb->data; + + return cf->can_id; +} + +static void em_canid_sff_match_add(struct canid_match *cm, u32 can_id, + u32 can_mask) +{ + int i; + + /* + * Limit can_mask and can_id to SFF range to + * protect against write after end of array + */ + can_mask &= CAN_SFF_MASK; + can_id &= can_mask; + + /* Single frame */ + if (can_mask == CAN_SFF_MASK) { + set_bit(can_id, cm->match_sff); + return; + } + + /* All frames */ + if (can_mask == 0) { + bitmap_fill(cm->match_sff, (1 << CAN_SFF_ID_BITS)); + return; + } + + /* + * Individual frame filter. + * Add record (set bit to 1) for each ID that + * conforms particular rule + */ + for (i = 0; i < (1 << CAN_SFF_ID_BITS); i++) { + if ((i & can_mask) == can_id) + set_bit(i, cm->match_sff); + } +} + +static inline struct canid_match *em_canid_priv(struct tcf_ematch *m) +{ + return (struct canid_match *)m->data; +} + +static int em_canid_match(struct sk_buff *skb, struct tcf_ematch *m, + struct tcf_pkt_info *info) +{ + struct canid_match *cm = em_canid_priv(m); + canid_t can_id; + int match = 0; + int i; + const struct can_filter *lp; + + can_id = em_canid_get_id(skb); + + if (can_id & CAN_EFF_FLAG) { + for (i = 0, lp = cm->rules_raw; + i < cm->eff_rules_count; i++, lp++) { + if (!(((lp->can_id ^ can_id) & lp->can_mask))) { + match = 1; + break; + } + } + } else { /* SFF */ + can_id &= CAN_SFF_MASK; + match = (test_bit(can_id, cm->match_sff) ? 1 : 0); + } + + return match; +} + +static int em_canid_change(struct tcf_proto *tp, void *data, int len, + struct tcf_ematch *m) +{ + struct can_filter *conf = data; /* Array with rules */ + struct canid_match *cm; + struct canid_match *cm_old = (struct canid_match *)m->data; + int i; + + if (!len) + return -EINVAL; + + if (len % sizeof(struct can_filter)) + return -EINVAL; + + if (len > sizeof(struct can_filter) * EM_CAN_RULES_MAX) + return -EINVAL; + + cm = kzalloc(sizeof(struct canid_match) + len, GFP_KERNEL); + if (!cm) + return -ENOMEM; + + cm->rules_count = len / sizeof(struct can_filter); + + /* + * We need two for() loops for copying rules into two contiguous + * areas in rules_raw to process all eff rules with a simple loop. + * NB: The configuration interface supports sff and eff rules. + * We do not support filters here that match for the same can_id + * provided in a SFF and EFF frame (e.g. 0x123 / 0x80000123). + * For this (unusual case) two filters have to be specified. The + * SFF/EFF separation is done with the CAN_EFF_FLAG in the can_id. + */ + + /* Fill rules_raw with EFF rules first */ + for (i = 0; i < cm->rules_count; i++) { + if (conf[i].can_id & CAN_EFF_FLAG) { + memcpy(cm->rules_raw + cm->eff_rules_count, + &conf[i], + sizeof(struct can_filter)); + + cm->eff_rules_count++; + } + } + + /* append SFF frame rules */ + for (i = 0; i < cm->rules_count; i++) { + if (!(conf[i].can_id & CAN_EFF_FLAG)) { + memcpy(cm->rules_raw + + cm->eff_rules_count + + cm->sff_rules_count, + &conf[i], sizeof(struct can_filter)); + + cm->sff_rules_count++; + + em_canid_sff_match_add(cm, + conf[i].can_id, conf[i].can_mask); + } + } + + m->datalen = sizeof(struct canid_match) + len; + m->data = (unsigned long)cm; + + if (cm_old != NULL) { + pr_err("canid: Configuring an existing ematch!\n"); + kfree(cm_old); + } + + return 0; +} + +static void em_canid_destroy(struct tcf_proto *tp, struct tcf_ematch *m) +{ + struct canid_match *cm = em_canid_priv(m); + + kfree(cm); +} + +static int em_canid_dump(struct sk_buff *skb, struct tcf_ematch *m) +{ + struct canid_match *cm = em_canid_priv(m); + + /* + * When configuring this ematch 'rules_count' is set not to exceed + * 'rules_raw' array size + */ + if (nla_put_nohdr(skb, sizeof(struct can_filter) * cm->rules_count, + &cm->rules_raw) < 0) + return -EMSGSIZE; + + return 0; +} + +static struct tcf_ematch_ops em_canid_ops = { + .kind = TCF_EM_CANID, + .change = em_canid_change, + .match = em_canid_match, + .destroy = em_canid_destroy, + .dump = em_canid_dump, + .owner = THIS_MODULE, + .link = LIST_HEAD_INIT(em_canid_ops.link) +}; + +static int __init init_em_canid(void) +{ + return tcf_em_register(&em_canid_ops); +} + +static void __exit exit_em_canid(void) +{ + tcf_em_unregister(&em_canid_ops); +} + +MODULE_LICENSE("GPL"); + +module_init(init_em_canid); +module_exit(exit_em_canid); + +MODULE_ALIAS_TCF_EMATCH(TCF_EM_CANID); -- cgit v1.2.3-59-g8ed1b From 46ba5a25f521e3c50d7bb81b1abb977769047456 Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Wed, 27 Jun 2012 00:59:56 +0000 Subject: netfilter: nfnetlink_queue: do not allow to set unsupported flag bits Allow setting of only supported flag bits in queue->flags. Signed-off-by: Krishna Kumar Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nfnetlink_queue.h | 1 + net/netfilter/nfnetlink_queue_core.c | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h index e0d8fd8d4d24..3b1c1360aedf 100644 --- a/include/linux/netfilter/nfnetlink_queue.h +++ b/include/linux/netfilter/nfnetlink_queue.h @@ -95,5 +95,6 @@ enum nfqnl_attr_config { /* Flags for NFQA_CFG_FLAGS */ #define NFQA_CFG_F_FAIL_OPEN (1 << 0) #define NFQA_CFG_F_CONNTRACK (1 << 1) +#define NFQA_CFG_F_MAX (1 << 2) #endif /* _NFNETLINK_QUEUE_H */ diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index a0b64920039d..c0496a55ad0c 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -910,6 +910,11 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, flags = ntohl(nla_get_be32(nfqa[NFQA_CFG_FLAGS])); mask = ntohl(nla_get_be32(nfqa[NFQA_CFG_MASK])); + if (flags >= NFQA_CFG_F_MAX) { + ret = -EOPNOTSUPP; + goto err_out_unlock; + } + spin_lock_bh(&queue->lock); queue->flags &= ~mask; queue->flags |= flags & mask; -- cgit v1.2.3-59-g8ed1b From 16917b87a23b429226527f393270047069d665e9 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Sun, 1 Jul 2012 03:18:50 +0000 Subject: net-next: Add netif_get_num_default_rss_queues Most multi-queue networking driver consider the number of online cpus when configuring RSS queues. This patch adds a wrapper to the number of cpus, setting an upper limit on the number of cpus a driver should consider (by default) when allocating resources for his queues. Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 +++ net/core/dev.c | 11 +++++++++++ 2 files changed, 14 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 2c2ecea28a1b..ab0251d541ab 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2119,6 +2119,9 @@ static inline int netif_copy_real_num_queues(struct net_device *to_dev, #endif } +#define DEFAULT_MAX_NUM_RSS_QUEUES (8) +extern int netif_get_num_default_rss_queues(void); + /* Use this variant when it is known for sure that it * is executing from hardware interrupt context or with hardware interrupts * disabled. diff --git a/net/core/dev.c b/net/core/dev.c index ed674e212b7a..69f7a1a393d8 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1793,6 +1793,17 @@ int netif_set_real_num_rx_queues(struct net_device *dev, unsigned int rxq) EXPORT_SYMBOL(netif_set_real_num_rx_queues); #endif +/* netif_get_num_default_rss_queues - default number of RSS queues + * + * This routine should set an upper limit on the number of RSS queues + * used by default by multiqueue devices. + */ +int netif_get_num_default_rss_queues() +{ + return min_t(int, DEFAULT_MAX_NUM_RSS_QUEUES, num_online_cpus()); +} +EXPORT_SYMBOL(netif_get_num_default_rss_queues); + static inline void __netif_reschedule(struct Qdisc *q) { struct softnet_data *sd; -- cgit v1.2.3-59-g8ed1b From 6be5bfc3bf0d31a70745a52e69f7f46de974193f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 5 Jul 2012 18:12:01 +0530 Subject: regulator: fixed: dt: support for input supply Add support for input supply in DT parsing of node. The input supply will be provided by the property "vin-supply" in the regulator node. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/fixed-regulator.txt | 2 ++ drivers/regulator/fixed.c | 19 ++++++++++++++++++- include/linux/regulator/fixed.h | 2 ++ 3 files changed, 22 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/regulator/fixed-regulator.txt b/Documentation/devicetree/bindings/regulator/fixed-regulator.txt index 2f5b6b1ba15f..4fae41d54798 100644 --- a/Documentation/devicetree/bindings/regulator/fixed-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/fixed-regulator.txt @@ -10,6 +10,7 @@ Optional properties: If this property is missing, the default assumed is Active low. - gpio-open-drain: GPIO is open drain type. If this property is missing then default assumption is false. +-vin-supply: Input supply name. Any property defined as part of the core regulator binding, defined in regulator.txt, can also be used. @@ -29,4 +30,5 @@ Example: enable-active-high; regulator-boot-on; gpio-open-drain; + vin-supply = <&parent_reg>; }; diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 1a0b17e8d8a9..185468c4d38f 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -102,6 +102,9 @@ of_get_fixed_voltage_config(struct device *dev) if (of_find_property(np, "gpio-open-drain", NULL)) config->gpio_is_open_drain = true; + if (of_find_property(np, "vin-supply", NULL)) + config->input_supply = "vin"; + return config; } @@ -169,6 +172,17 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) drvdata->desc.enable_time = config->startup_delay; + if (config->input_supply) { + drvdata->desc.supply_name = kstrdup(config->input_supply, + GFP_KERNEL); + if (!drvdata->desc.supply_name) { + dev_err(&pdev->dev, + "Failed to allocate input supply\n"); + ret = -ENOMEM; + goto err_name; + } + } + if (config->microvolts) drvdata->desc.n_voltages = 1; @@ -202,7 +216,7 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) if (IS_ERR(drvdata->dev)) { ret = PTR_ERR(drvdata->dev); dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); - goto err_name; + goto err_input; } platform_set_drvdata(pdev, drvdata); @@ -212,6 +226,8 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) return 0; +err_input: + kfree(drvdata->desc.supply_name); err_name: kfree(drvdata->desc.name); err: @@ -223,6 +239,7 @@ static int __devexit reg_fixed_voltage_remove(struct platform_device *pdev) struct fixed_voltage_data *drvdata = platform_get_drvdata(pdev); regulator_unregister(drvdata->dev); + kfree(drvdata->desc.supply_name); kfree(drvdata->desc.name); return 0; diff --git a/include/linux/regulator/fixed.h b/include/linux/regulator/fixed.h index f83f7440b488..d6c24c53adf4 100644 --- a/include/linux/regulator/fixed.h +++ b/include/linux/regulator/fixed.h @@ -22,6 +22,7 @@ struct regulator_init_data; /** * struct fixed_voltage_config - fixed_voltage_config structure * @supply_name: Name of the regulator supply + * @input_supply: Name of the input regulator supply * @microvolts: Output voltage of regulator * @gpio: GPIO to use for enable control * set to -EINVAL if not used @@ -46,6 +47,7 @@ struct regulator_init_data; */ struct fixed_voltage_config { const char *supply_name; + const char *input_supply; int microvolts; int gpio; unsigned startup_delay; -- cgit v1.2.3-59-g8ed1b From f567fde24640cf6f2d6416196bfc8b3fefc8e433 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 20 Jun 2012 14:14:05 +0530 Subject: gpio: fix bits conflict for gpio flags The bit 2 and 3 in GPIO flag are allocated for the flag OPEN_DRAIN/OPEN_SOURCE. These bits are reused for the flag EXPORT/EXPORT_CHANGEABLE and so creating conflict. Fix this conflict by assigning bit 4 and 5 for the flag EXPORT/EXPORT_CHANGEABLE. Signed-off-by: Laxman Dewangan Signed-off-by: Linus Walleij --- include/linux/gpio.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/gpio.h b/include/linux/gpio.h index f07fc2d08159..2e31e8b3a190 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -22,8 +22,8 @@ /* Gpio pin is open source */ #define GPIOF_OPEN_SOURCE (1 << 3) -#define GPIOF_EXPORT (1 << 2) -#define GPIOF_EXPORT_CHANGEABLE (1 << 3) +#define GPIOF_EXPORT (1 << 4) +#define GPIOF_EXPORT_CHANGEABLE (1 << 5) #define GPIOF_EXPORT_DIR_FIXED (GPIOF_EXPORT) #define GPIOF_EXPORT_DIR_CHANGEABLE (GPIOF_EXPORT | GPIOF_EXPORT_CHANGEABLE) -- cgit v1.2.3-59-g8ed1b From 8eb41c8dfb9e2396d2452ada9023a83d610b9051 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 5 Jul 2012 14:25:49 +0300 Subject: {nl,cfg}80211: support high bitrates Until now, a u16 value was used to represent bitrate value. With VHT bitrates this becomes too small. Introduce a new 32-bit bitrate attribute. nl80211 will report both the new and the old attribute, unless the bitrate doesn't fit into the old u16 attribute in which case only the new one will be reported. User space tools encouraged to prefer the 32-bit attribute, if available (since it won't be available on older kernels.) Signed-off-by: Vladimir Kondratiev [reword commit message and comments a bit] Signed-off-by: Johannes Berg --- include/linux/nl80211.h | 9 +++++++++ include/net/cfg80211.h | 2 +- net/wireless/nl80211.c | 9 +++++++-- net/wireless/util.c | 2 +- 4 files changed, 18 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 74cc55c1bf28..db961a59247f 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1638,12 +1638,20 @@ struct nl80211_sta_flag_update { * * These attribute types are used with %NL80211_STA_INFO_TXRATE * when getting information about the bitrate of a station. + * There are 2 attributes for bitrate, a legacy one that represents + * a 16-bit value, and new one that represents a 32-bit value. + * If the rate value fits into 16 bit, both attributes are reported + * with the same value. If the rate is too high to fit into 16 bits + * (>6.5535Gbps) only 32-bit attribute is included. + * User space tools encouraged to use the 32-bit attribute and fall + * back to the 16-bit one for compatibility with older kernels. * * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval + * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s) * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined * @__NL80211_RATE_INFO_AFTER_LAST: internal use */ @@ -1653,6 +1661,7 @@ enum nl80211_rate_info { NL80211_RATE_INFO_MCS, NL80211_RATE_INFO_40_MHZ_WIDTH, NL80211_RATE_INFO_SHORT_GI, + NL80211_RATE_INFO_BITRATE32, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0b564e83a24b..8837efc368f9 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3487,7 +3487,7 @@ void cfg80211_ch_switch_notify(struct net_device *dev, int freq, * * return 0 if MCS index >= 32 */ -u16 cfg80211_calculate_bitrate(struct rate_info *rate); +u32 cfg80211_calculate_bitrate(struct rate_info *rate); /* Logging, debugging and troubleshooting/diagnostic helpers. */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 77102e66f1ea..2a5cdb60bc6e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2618,7 +2618,8 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr) { struct nlattr *rate; - u16 bitrate; + u32 bitrate; + u16 bitrate_compat; rate = nla_nest_start(msg, attr); if (!rate) @@ -2626,8 +2627,12 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ bitrate = cfg80211_calculate_bitrate(info); + /* report 16-bit bitrate only if we can */ + bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0; if ((bitrate > 0 && - nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate)) || + nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate)) || + (bitrate_compat > 0 && + nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat)) || ((info->flags & RATE_INFO_FLAGS_MCS) && nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs)) || ((info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) && diff --git a/net/wireless/util.c b/net/wireless/util.c index 0228c64e73d8..6e52726f7fe3 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -900,7 +900,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, return err; } -u16 cfg80211_calculate_bitrate(struct rate_info *rate) +u32 cfg80211_calculate_bitrate(struct rate_info *rate) { int modulation, streams, bitrate; -- cgit v1.2.3-59-g8ed1b From 5167e8d5417bf5c322a703d2927daec727ea40dd Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 22 Jun 2012 15:52:09 +0200 Subject: sched/nohz: Rewrite and fix load-avg computation -- again Thanks to Charles Wang for spotting the defects in the current code: - If we go idle during the sample window -- after sampling, we get a negative bias because we can negate our own sample. - If we wake up during the sample window we get a positive bias because we push the sample to a known active period. So rewrite the entire nohz load-avg muck once again, now adding copious documentation to the code. Reported-and-tested-by: Doug Smythies Reported-and-tested-by: Charles Wang Signed-off-by: Peter Zijlstra Cc: Linus Torvalds Cc: Andrew Morton Cc: stable@kernel.org Link: http://lkml.kernel.org/r/1340373782.18025.74.camel@twins [ minor edits ] Signed-off-by: Ingo Molnar --- include/linux/sched.h | 8 ++ kernel/sched/core.c | 275 ++++++++++++++++++++++++++++++++++------------- kernel/sched/idle_task.c | 1 - kernel/sched/sched.h | 2 - kernel/time/tick-sched.c | 2 + 5 files changed, 213 insertions(+), 75 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 4059c0f33f07..20cb7497c59c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1909,6 +1909,14 @@ static inline int set_cpus_allowed_ptr(struct task_struct *p, } #endif +#ifdef CONFIG_NO_HZ +void calc_load_enter_idle(void); +void calc_load_exit_idle(void); +#else +static inline void calc_load_enter_idle(void) { } +static inline void calc_load_exit_idle(void) { } +#endif /* CONFIG_NO_HZ */ + #ifndef CONFIG_CPUMASK_OFFSTACK static inline int set_cpus_allowed(struct task_struct *p, cpumask_t new_mask) { diff --git a/kernel/sched/core.c b/kernel/sched/core.c index d5594a4268d4..bb840405335d 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2161,11 +2161,73 @@ unsigned long this_cpu_load(void) } +/* + * Global load-average calculations + * + * We take a distributed and async approach to calculating the global load-avg + * in order to minimize overhead. + * + * The global load average is an exponentially decaying average of nr_running + + * nr_uninterruptible. + * + * Once every LOAD_FREQ: + * + * nr_active = 0; + * for_each_possible_cpu(cpu) + * nr_active += cpu_of(cpu)->nr_running + cpu_of(cpu)->nr_uninterruptible; + * + * avenrun[n] = avenrun[0] * exp_n + nr_active * (1 - exp_n) + * + * Due to a number of reasons the above turns in the mess below: + * + * - for_each_possible_cpu() is prohibitively expensive on machines with + * serious number of cpus, therefore we need to take a distributed approach + * to calculating nr_active. + * + * \Sum_i x_i(t) = \Sum_i x_i(t) - x_i(t_0) | x_i(t_0) := 0 + * = \Sum_i { \Sum_j=1 x_i(t_j) - x_i(t_j-1) } + * + * So assuming nr_active := 0 when we start out -- true per definition, we + * can simply take per-cpu deltas and fold those into a global accumulate + * to obtain the same result. See calc_load_fold_active(). + * + * Furthermore, in order to avoid synchronizing all per-cpu delta folding + * across the machine, we assume 10 ticks is sufficient time for every + * cpu to have completed this task. + * + * This places an upper-bound on the IRQ-off latency of the machine. Then + * again, being late doesn't loose the delta, just wrecks the sample. + * + * - cpu_rq()->nr_uninterruptible isn't accurately tracked per-cpu because + * this would add another cross-cpu cacheline miss and atomic operation + * to the wakeup path. Instead we increment on whatever cpu the task ran + * when it went into uninterruptible state and decrement on whatever cpu + * did the wakeup. This means that only the sum of nr_uninterruptible over + * all cpus yields the correct result. + * + * This covers the NO_HZ=n code, for extra head-aches, see the comment below. + */ + /* Variables and functions for calc_load */ static atomic_long_t calc_load_tasks; static unsigned long calc_load_update; unsigned long avenrun[3]; -EXPORT_SYMBOL(avenrun); +EXPORT_SYMBOL(avenrun); /* should be removed */ + +/** + * get_avenrun - get the load average array + * @loads: pointer to dest load array + * @offset: offset to add + * @shift: shift count to shift the result left + * + * These values are estimates at best, so no need for locking. + */ +void get_avenrun(unsigned long *loads, unsigned long offset, int shift) +{ + loads[0] = (avenrun[0] + offset) << shift; + loads[1] = (avenrun[1] + offset) << shift; + loads[2] = (avenrun[2] + offset) << shift; +} static long calc_load_fold_active(struct rq *this_rq) { @@ -2182,6 +2244,9 @@ static long calc_load_fold_active(struct rq *this_rq) return delta; } +/* + * a1 = a0 * e + a * (1 - e) + */ static unsigned long calc_load(unsigned long load, unsigned long exp, unsigned long active) { @@ -2193,30 +2258,118 @@ calc_load(unsigned long load, unsigned long exp, unsigned long active) #ifdef CONFIG_NO_HZ /* - * For NO_HZ we delay the active fold to the next LOAD_FREQ update. + * Handle NO_HZ for the global load-average. + * + * Since the above described distributed algorithm to compute the global + * load-average relies on per-cpu sampling from the tick, it is affected by + * NO_HZ. + * + * The basic idea is to fold the nr_active delta into a global idle-delta upon + * entering NO_HZ state such that we can include this as an 'extra' cpu delta + * when we read the global state. + * + * Obviously reality has to ruin such a delightfully simple scheme: + * + * - When we go NO_HZ idle during the window, we can negate our sample + * contribution, causing under-accounting. + * + * We avoid this by keeping two idle-delta counters and flipping them + * when the window starts, thus separating old and new NO_HZ load. + * + * The only trick is the slight shift in index flip for read vs write. + * + * 0s 5s 10s 15s + * +10 +10 +10 +10 + * |-|-----------|-|-----------|-|-----------|-| + * r:0 0 1 1 0 0 1 1 0 + * w:0 1 1 0 0 1 1 0 0 + * + * This ensures we'll fold the old idle contribution in this window while + * accumlating the new one. + * + * - When we wake up from NO_HZ idle during the window, we push up our + * contribution, since we effectively move our sample point to a known + * busy state. + * + * This is solved by pushing the window forward, and thus skipping the + * sample, for this cpu (effectively using the idle-delta for this cpu which + * was in effect at the time the window opened). This also solves the issue + * of having to deal with a cpu having been in NOHZ idle for multiple + * LOAD_FREQ intervals. * * When making the ILB scale, we should try to pull this in as well. */ -static atomic_long_t calc_load_tasks_idle; +static atomic_long_t calc_load_idle[2]; +static int calc_load_idx; -void calc_load_account_idle(struct rq *this_rq) +static inline int calc_load_write_idx(void) { + int idx = calc_load_idx; + + /* + * See calc_global_nohz(), if we observe the new index, we also + * need to observe the new update time. + */ + smp_rmb(); + + /* + * If the folding window started, make sure we start writing in the + * next idle-delta. + */ + if (!time_before(jiffies, calc_load_update)) + idx++; + + return idx & 1; +} + +static inline int calc_load_read_idx(void) +{ + return calc_load_idx & 1; +} + +void calc_load_enter_idle(void) +{ + struct rq *this_rq = this_rq(); long delta; + /* + * We're going into NOHZ mode, if there's any pending delta, fold it + * into the pending idle delta. + */ delta = calc_load_fold_active(this_rq); - if (delta) - atomic_long_add(delta, &calc_load_tasks_idle); + if (delta) { + int idx = calc_load_write_idx(); + atomic_long_add(delta, &calc_load_idle[idx]); + } } -static long calc_load_fold_idle(void) +void calc_load_exit_idle(void) { - long delta = 0; + struct rq *this_rq = this_rq(); + + /* + * If we're still before the sample window, we're done. + */ + if (time_before(jiffies, this_rq->calc_load_update)) + return; /* - * Its got a race, we don't care... + * We woke inside or after the sample window, this means we're already + * accounted through the nohz accounting, so skip the entire deal and + * sync up for the next window. */ - if (atomic_long_read(&calc_load_tasks_idle)) - delta = atomic_long_xchg(&calc_load_tasks_idle, 0); + this_rq->calc_load_update = calc_load_update; + if (time_before(jiffies, this_rq->calc_load_update + 10)) + this_rq->calc_load_update += LOAD_FREQ; +} + +static long calc_load_fold_idle(void) +{ + int idx = calc_load_read_idx(); + long delta = 0; + + if (atomic_long_read(&calc_load_idle[idx])) + delta = atomic_long_xchg(&calc_load_idle[idx], 0); return delta; } @@ -2302,66 +2455,39 @@ static void calc_global_nohz(void) { long delta, active, n; - /* - * If we crossed a calc_load_update boundary, make sure to fold - * any pending idle changes, the respective CPUs might have - * missed the tick driven calc_load_account_active() update - * due to NO_HZ. - */ - delta = calc_load_fold_idle(); - if (delta) - atomic_long_add(delta, &calc_load_tasks); - - /* - * It could be the one fold was all it took, we done! - */ - if (time_before(jiffies, calc_load_update + 10)) - return; - - /* - * Catch-up, fold however many we are behind still - */ - delta = jiffies - calc_load_update - 10; - n = 1 + (delta / LOAD_FREQ); + if (!time_before(jiffies, calc_load_update + 10)) { + /* + * Catch-up, fold however many we are behind still + */ + delta = jiffies - calc_load_update - 10; + n = 1 + (delta / LOAD_FREQ); - active = atomic_long_read(&calc_load_tasks); - active = active > 0 ? active * FIXED_1 : 0; + active = atomic_long_read(&calc_load_tasks); + active = active > 0 ? active * FIXED_1 : 0; - avenrun[0] = calc_load_n(avenrun[0], EXP_1, active, n); - avenrun[1] = calc_load_n(avenrun[1], EXP_5, active, n); - avenrun[2] = calc_load_n(avenrun[2], EXP_15, active, n); + avenrun[0] = calc_load_n(avenrun[0], EXP_1, active, n); + avenrun[1] = calc_load_n(avenrun[1], EXP_5, active, n); + avenrun[2] = calc_load_n(avenrun[2], EXP_15, active, n); - calc_load_update += n * LOAD_FREQ; -} -#else -void calc_load_account_idle(struct rq *this_rq) -{ -} + calc_load_update += n * LOAD_FREQ; + } -static inline long calc_load_fold_idle(void) -{ - return 0; + /* + * Flip the idle index... + * + * Make sure we first write the new time then flip the index, so that + * calc_load_write_idx() will see the new time when it reads the new + * index, this avoids a double flip messing things up. + */ + smp_wmb(); + calc_load_idx++; } +#else /* !CONFIG_NO_HZ */ -static void calc_global_nohz(void) -{ -} -#endif +static inline long calc_load_fold_idle(void) { return 0; } +static inline void calc_global_nohz(void) { } -/** - * get_avenrun - get the load average array - * @loads: pointer to dest load array - * @offset: offset to add - * @shift: shift count to shift the result left - * - * These values are estimates at best, so no need for locking. - */ -void get_avenrun(unsigned long *loads, unsigned long offset, int shift) -{ - loads[0] = (avenrun[0] + offset) << shift; - loads[1] = (avenrun[1] + offset) << shift; - loads[2] = (avenrun[2] + offset) << shift; -} +#endif /* CONFIG_NO_HZ */ /* * calc_load - update the avenrun load estimates 10 ticks after the @@ -2369,11 +2495,18 @@ void get_avenrun(unsigned long *loads, unsigned long offset, int shift) */ void calc_global_load(unsigned long ticks) { - long active; + long active, delta; if (time_before(jiffies, calc_load_update + 10)) return; + /* + * Fold the 'old' idle-delta to include all NO_HZ cpus. + */ + delta = calc_load_fold_idle(); + if (delta) + atomic_long_add(delta, &calc_load_tasks); + active = atomic_long_read(&calc_load_tasks); active = active > 0 ? active * FIXED_1 : 0; @@ -2384,12 +2517,7 @@ void calc_global_load(unsigned long ticks) calc_load_update += LOAD_FREQ; /* - * Account one period with whatever state we found before - * folding in the nohz state and ageing the entire idle period. - * - * This avoids loosing a sample when we go idle between - * calc_load_account_active() (10 ticks ago) and now and thus - * under-accounting. + * In case we idled for multiple LOAD_FREQ intervals, catch up in bulk. */ calc_global_nohz(); } @@ -2406,13 +2534,16 @@ static void calc_load_account_active(struct rq *this_rq) return; delta = calc_load_fold_active(this_rq); - delta += calc_load_fold_idle(); if (delta) atomic_long_add(delta, &calc_load_tasks); this_rq->calc_load_update += LOAD_FREQ; } +/* + * End of global load-average stuff + */ + /* * The exact cpuload at various idx values, calculated at every tick would be * load = (2^idx - 1) / 2^idx * load + 1 / 2^idx * cur_load diff --git a/kernel/sched/idle_task.c b/kernel/sched/idle_task.c index b44d604b35d1..b6baf370cae9 100644 --- a/kernel/sched/idle_task.c +++ b/kernel/sched/idle_task.c @@ -25,7 +25,6 @@ static void check_preempt_curr_idle(struct rq *rq, struct task_struct *p, int fl static struct task_struct *pick_next_task_idle(struct rq *rq) { schedstat_inc(rq, sched_goidle); - calc_load_account_idle(rq); return rq->idle; } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 6d52cea7f33d..55844f24435a 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -942,8 +942,6 @@ static inline u64 sched_avg_period(void) return (u64)sysctl_sched_time_avg * NSEC_PER_MSEC / 2; } -void calc_load_account_idle(struct rq *this_rq); - #ifdef CONFIG_SCHED_HRTICK /* diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 869997833928..4a08472c3ca7 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -406,6 +406,7 @@ static void tick_nohz_stop_sched_tick(struct tick_sched *ts) */ if (!ts->tick_stopped) { select_nohz_load_balancer(1); + calc_load_enter_idle(); ts->idle_tick = hrtimer_get_expires(&ts->sched_timer); ts->tick_stopped = 1; @@ -597,6 +598,7 @@ void tick_nohz_idle_exit(void) account_idle_ticks(ticks); #endif + calc_load_exit_idle(); touch_softlockup_watchdog(); /* * Cancel the scheduled timer and restore the tick -- cgit v1.2.3-59-g8ed1b From 1d5fcfec22ce5f69db0d29284d2b65ff8ab1bfaa Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 5 Jul 2012 22:12:32 +0200 Subject: PM / Domains: Add device domain data reference counter Add a mechanism for counting references to the struct generic_pm_domain_data object pointed to by dev->power.subsys_data->domain_data if the device in question belongs to a generic PM domain. This change is necessary for a subsequent patch making it possible to allocate that object from within pm_genpd_add_callbacks(), so that drivers can attach their PM domain device callbacks to devices before those devices are added to PM domains. This patch has been tested on the SH7372 Mackerel board. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 83 +++++++++++++++++++++++++++++++-------------- include/linux/pm_domain.h | 1 + 2 files changed, 58 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 4b5f090fccb6..45eb3b155b6d 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -297,7 +297,7 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, pdd = dev->power.subsys_data ? dev->power.subsys_data->domain_data : NULL; - if (pdd) { + if (pdd && pdd->dev) { to_gpd_data(pdd)->td.constraint_changed = true; genpd = dev_to_genpd(dev); } else { @@ -1266,6 +1266,27 @@ static void pm_genpd_complete(struct device *dev) #endif /* CONFIG_PM_SLEEP */ +static struct generic_pm_domain_data *__pm_genpd_alloc_dev_data(struct device *dev) +{ + struct generic_pm_domain_data *gpd_data; + + gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); + if (!gpd_data) + return NULL; + + mutex_init(&gpd_data->lock); + gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; + dev_pm_qos_add_notifier(dev, &gpd_data->nb); + return gpd_data; +} + +static void __pm_genpd_free_dev_data(struct device *dev, + struct generic_pm_domain_data *gpd_data) +{ + dev_pm_qos_remove_notifier(dev, &gpd_data->nb); + kfree(gpd_data); +} + /** * __pm_genpd_add_device - Add a device to an I/O PM domain. * @genpd: PM domain to add the device to. @@ -1275,7 +1296,7 @@ static void pm_genpd_complete(struct device *dev) int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, struct gpd_timing_data *td) { - struct generic_pm_domain_data *gpd_data; + struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; struct pm_domain_data *pdd; int ret = 0; @@ -1284,14 +1305,10 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) return -EINVAL; - gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); - if (!gpd_data) + gpd_data_new = __pm_genpd_alloc_dev_data(dev); + if (!gpd_data_new) return -ENOMEM; - mutex_init(&gpd_data->lock); - gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; - dev_pm_qos_add_notifier(dev, &gpd_data->nb); - genpd_acquire_lock(genpd); if (genpd->prepared_count > 0) { @@ -1305,35 +1322,42 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, goto out; } + ret = dev_pm_get_subsys_data(dev); + if (ret) + goto out; + genpd->device_count++; genpd->max_off_time_changed = true; - dev_pm_get_subsys_data(dev); - - mutex_lock(&gpd_data->lock); spin_lock_irq(&dev->power.lock); + dev->pm_domain = &genpd->domain; - dev->power.subsys_data->domain_data = &gpd_data->base; - gpd_data->base.dev = dev; - list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); - gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF; + if (dev->power.subsys_data->domain_data) { + gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); + } else { + gpd_data = gpd_data_new; + dev->power.subsys_data->domain_data = &gpd_data->base; + } + gpd_data->refcount++; if (td) gpd_data->td = *td; + spin_unlock_irq(&dev->power.lock); + + mutex_lock(&gpd_data->lock); + gpd_data->base.dev = dev; + list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); + gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF; gpd_data->td.constraint_changed = true; gpd_data->td.effective_constraint_ns = -1; - spin_unlock_irq(&dev->power.lock); mutex_unlock(&gpd_data->lock); - genpd_release_lock(genpd); - - return 0; - out: genpd_release_lock(genpd); - dev_pm_qos_remove_notifier(dev, &gpd_data->nb); - kfree(gpd_data); + if (gpd_data != gpd_data_new) + __pm_genpd_free_dev_data(dev, gpd_data_new); + return ret; } @@ -1379,6 +1403,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, { struct generic_pm_domain_data *gpd_data; struct pm_domain_data *pdd; + bool remove = false; int ret = 0; dev_dbg(dev, "%s()\n", __func__); @@ -1399,22 +1424,28 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, genpd->max_off_time_changed = true; spin_lock_irq(&dev->power.lock); + dev->pm_domain = NULL; pdd = dev->power.subsys_data->domain_data; list_del_init(&pdd->list_node); - dev->power.subsys_data->domain_data = NULL; + gpd_data = to_gpd_data(pdd); + if (--gpd_data->refcount == 0) { + dev->power.subsys_data->domain_data = NULL; + remove = true; + } + spin_unlock_irq(&dev->power.lock); - gpd_data = to_gpd_data(pdd); mutex_lock(&gpd_data->lock); pdd->dev = NULL; mutex_unlock(&gpd_data->lock); genpd_release_lock(genpd); - dev_pm_qos_remove_notifier(dev, &gpd_data->nb); - kfree(gpd_data); dev_pm_put_subsys_data(dev); + if (remove) + __pm_genpd_free_dev_data(dev, gpd_data); + return 0; out: diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 2febe31d2675..a7d6172922d4 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -112,6 +112,7 @@ struct generic_pm_domain_data { struct gpd_timing_data td; struct notifier_block nb; struct mutex lock; + unsigned int refcount; bool need_restore; bool always_on; }; -- cgit v1.2.3-59-g8ed1b From 25ac77613aa8fca131599705e3d7da2a0eaa06a0 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Thu, 5 Jul 2012 15:23:25 +0200 Subject: ACPI: intel_idle : break dependency between modules When the system is booted with some cpus offline, the idle driver is not initialized. When a cpu is set online, the acpi code call the intel idle init function. Unfortunately this code introduce a dependency between intel_idle and acpi. This patch is intended to remove this dependency by using the notifier of intel_idle. This patch has the benefit of encapsulating the intel_idle driver and remove some exported functions. Signed-off-by: Daniel Lezcano Acked-by: Srivatsa S. Bhat Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_driver.c | 7 ------- drivers/idle/intel_idle.c | 41 +++++++++++++++++++++++++++-------------- include/linux/cpuidle.h | 7 ------- 3 files changed, 27 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 0734086537b8..8648b29f6eec 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -427,18 +427,11 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb, * Initialize missing things */ if (pr->flags.need_hotplug_init) { - struct cpuidle_driver *idle_driver = - cpuidle_get_driver(); - printk(KERN_INFO "Will online and init hotplugged " "CPU: %d\n", pr->id); WARN(acpi_processor_start(pr), "Failed to start CPU:" " %d\n", pr->id); pr->flags.need_hotplug_init = 0; - if (idle_driver && !strcmp(idle_driver->name, - "intel_idle")) { - intel_idle_cpu_init(pr->id); - } /* Normal CPU soft online event */ } else { acpi_processor_ppc_has_changed(pr, 0); diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index d0f59c3f87ef..fe95d5464a02 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -96,6 +96,7 @@ static const struct idle_cpu *icpu; static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; static int intel_idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); +static int intel_idle_cpu_init(int cpu); static struct cpuidle_state *cpuidle_state_table; @@ -302,22 +303,35 @@ static void __setup_broadcast_timer(void *arg) clockevents_notify(reason, &cpu); } -static int setup_broadcast_cpuhp_notify(struct notifier_block *n, - unsigned long action, void *hcpu) +static int cpu_hotplug_notify(struct notifier_block *n, + unsigned long action, void *hcpu) { int hotcpu = (unsigned long)hcpu; + struct cpuidle_device *dev; switch (action & 0xf) { case CPU_ONLINE: - smp_call_function_single(hotcpu, __setup_broadcast_timer, - (void *)true, 1); + + if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) + smp_call_function_single(hotcpu, __setup_broadcast_timer, + (void *)true, 1); + + /* + * Some systems can hotplug a cpu at runtime after + * the kernel has booted, we have to initialize the + * driver in this case + */ + dev = per_cpu_ptr(intel_idle_cpuidle_devices, hotcpu); + if (!dev->registered) + intel_idle_cpu_init(hotcpu); + break; } return NOTIFY_OK; } -static struct notifier_block setup_broadcast_notifier = { - .notifier_call = setup_broadcast_cpuhp_notify, +static struct notifier_block cpu_hotplug_notifier = { + .notifier_call = cpu_hotplug_notify, }; static void auto_demotion_disable(void *dummy) @@ -405,10 +419,10 @@ static int intel_idle_probe(void) if (boot_cpu_has(X86_FEATURE_ARAT)) /* Always Reliable APIC Timer */ lapic_timer_reliable_states = LAPIC_TIMER_ALWAYS_RELIABLE; - else { + else on_each_cpu(__setup_broadcast_timer, (void *)true, 1); - register_cpu_notifier(&setup_broadcast_notifier); - } + + register_cpu_notifier(&cpu_hotplug_notifier); pr_debug(PREFIX "v" INTEL_IDLE_VERSION " model 0x%X\n", boot_cpu_data.x86_model); @@ -494,7 +508,7 @@ static int intel_idle_cpuidle_driver_init(void) * allocate, initialize, register cpuidle_devices * @cpu: cpu/core to initialize */ -int intel_idle_cpu_init(int cpu) +static int intel_idle_cpu_init(int cpu) { int cstate; struct cpuidle_device *dev; @@ -539,7 +553,6 @@ int intel_idle_cpu_init(int cpu) return 0; } -EXPORT_SYMBOL_GPL(intel_idle_cpu_init); static int __init intel_idle_init(void) { @@ -581,10 +594,10 @@ static void __exit intel_idle_exit(void) intel_idle_cpuidle_devices_uninit(); cpuidle_unregister_driver(&intel_idle_driver); - if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) { + + if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) on_each_cpu(__setup_broadcast_timer, (void *)false, 1); - unregister_cpu_notifier(&setup_broadcast_notifier); - } + unregister_cpu_notifier(&cpu_hotplug_notifier); return; } diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 27cfced7b57b..524bb6f3b6c4 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -206,14 +206,7 @@ struct cpuidle_governor { extern int cpuidle_register_governor(struct cpuidle_governor *gov); extern void cpuidle_unregister_governor(struct cpuidle_governor *gov); -#ifdef CONFIG_INTEL_IDLE -extern int intel_idle_cpu_init(int cpu); #else -static inline int intel_idle_cpu_init(int cpu) { return -1; } -#endif - -#else -static inline int intel_idle_cpu_init(int cpu) { return -1; } static inline int cpuidle_register_governor(struct cpuidle_governor *gov) {return 0;} -- cgit v1.2.3-59-g8ed1b From 326f418b4c74ab4a6066f38b6144bc6461f9447b Mon Sep 17 00:00:00 2001 From: Jason Baron Date: Thu, 28 Jun 2012 15:04:57 -0400 Subject: tracepoint: Use static_key_false(), since static_branch() is deprecated Convert the last user of static_branch() -> static_key_false(). Signed-off-by: Jason Baron Cc: rostedt@goodmis.org Cc: mathieu.desnoyers@efficios.com Cc: a.p.zijlstra@chello.nl Link: http://lkml.kernel.org/r/5fffcd40a6c063769badcdd74a7d90980500dbcb.1340909155.git.jbaron@redhat.com Signed-off-by: Ingo Molnar --- include/linux/tracepoint.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index bd96ecd0e05c..802de56c41e8 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -153,7 +153,7 @@ static inline void tracepoint_synchronize_unregister(void) } \ static inline void trace_##name##_rcuidle(proto) \ { \ - if (static_branch(&__tracepoint_##name.key)) \ + if (static_key_false(&__tracepoint_##name.key)) \ __DO_TRACE(&__tracepoint_##name, \ TP_PROTO(data_proto), \ TP_ARGS(data_args), \ -- cgit v1.2.3-59-g8ed1b From 47fbc518a4b5c9a949f7cab8b14a00d3549bf138 Mon Sep 17 00:00:00 2001 From: Jason Baron Date: Thu, 28 Jun 2012 15:05:02 -0400 Subject: jump label: Remove static_branch() Remove the obsolete static_branch() interface, since the supported interface is now static_key_false()/true() - which is used by all in-tree code. See commit: c5905afb0e ("static keys: Introduce 'struct static_key', static_key_true()/false() and static_key_slow_[inc|dec]()"). Signed-off-by: Jason Baron Cc: rostedt@goodmis.org Cc: mathieu.desnoyers@efficios.com Cc: Linus Torvalds Cc: Andrew Morton Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/199332c47eef8005d5a5bf1018a80d25929a5746.1340909155.git.jbaron@redhat.com Signed-off-by: Ingo Molnar --- include/linux/jump_label.h | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index c513a40510f5..0976fc46d1e0 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h @@ -42,8 +42,7 @@ * allowed. * * Not initializing the key (static data is initialized to 0s anyway) is the - * same as using STATIC_KEY_INIT_FALSE and static_key_false() is - * equivalent with static_branch(). + * same as using STATIC_KEY_INIT_FALSE. * */ @@ -107,12 +106,6 @@ static __always_inline bool static_key_true(struct static_key *key) return !static_key_false(key); } -/* Deprecated. Please use 'static_key_false() instead. */ -static __always_inline bool static_branch(struct static_key *key) -{ - return arch_static_branch(key); -} - extern struct jump_entry __start___jump_table[]; extern struct jump_entry __stop___jump_table[]; @@ -166,14 +159,6 @@ static __always_inline bool static_key_true(struct static_key *key) return false; } -/* Deprecated. Please use 'static_key_false() instead. */ -static __always_inline bool static_branch(struct static_key *key) -{ - if (unlikely(atomic_read(&key->enabled)) > 0) - return true; - return false; -} - static inline void static_key_slow_inc(struct static_key *key) { atomic_inc(&key->enabled); -- cgit v1.2.3-59-g8ed1b From 74a7f08448adea6cb47cd9b260c98ff168117e92 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 15 Jun 2012 11:50:25 -0600 Subject: devicetree: add helper inline for retrieving a node's full name The pattern (np ? np->full_name : "") is rather common in the kernel, but can also make for quite long lines. This patch adds a new inline function, of_node_full_name() so that the test for a valid node pointer doesn't need to be open coded at all call sites. Signed-off-by: Grant Likely Cc: Paul Mundt Cc: Benjamin Herrenschmidt Cc: Thomas Gleixner Signed-off-by: Rob Herring --- arch/microblaze/pci/pci-common.c | 6 ++---- arch/powerpc/kernel/pci-common.c | 6 ++---- arch/powerpc/kernel/vio.c | 5 ++--- arch/powerpc/platforms/cell/iommu.c | 3 +-- arch/powerpc/platforms/pseries/iommu.c | 2 +- arch/sparc/kernel/of_device_64.c | 2 +- drivers/of/base.c | 2 +- drivers/of/irq.c | 2 +- include/linux/of.h | 10 ++++++++++ kernel/irq/irqdomain.c | 8 ++++---- 10 files changed, 25 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index ed22bfc5db14..ca8f6e769960 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -249,8 +249,7 @@ int pci_read_irq_line(struct pci_dev *pci_dev) } else { pr_debug(" Got one, spec %d cells (0x%08x 0x%08x...) on %s\n", oirq.size, oirq.specifier[0], oirq.specifier[1], - oirq.controller ? oirq.controller->full_name : - ""); + of_node_full_name(oirq.controller)); virq = irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size); @@ -1493,8 +1492,7 @@ static void __devinit pcibios_scan_phb(struct pci_controller *hose) struct pci_bus *bus; struct device_node *node = hose->dn; - pr_debug("PCI: Scanning PHB %s\n", - node ? node->full_name : ""); + pr_debug("PCI: Scanning PHB %s\n", of_node_full_name(node)); pcibios_setup_phb_resources(hose, &resources); diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 8e78e93c8185..886c254fd565 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -248,8 +248,7 @@ static int pci_read_irq_line(struct pci_dev *pci_dev) } else { pr_debug(" Got one, spec %d cells (0x%08x 0x%08x...) on %s\n", oirq.size, oirq.specifier[0], oirq.specifier[1], - oirq.controller ? oirq.controller->full_name : - ""); + of_node_full_name(oirq.controller)); virq = irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size); @@ -1628,8 +1627,7 @@ void __devinit pcibios_scan_phb(struct pci_controller *hose) struct device_node *node = hose->dn; int mode; - pr_debug("PCI: Scanning PHB %s\n", - node ? node->full_name : ""); + pr_debug("PCI: Scanning PHB %s\n", of_node_full_name(node)); /* Get some IO space for the new PHB */ pcibios_setup_phb_io_space(hose); diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index cb87301ccd55..63f72ede4341 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -1296,8 +1296,7 @@ static void __devinit vio_dev_release(struct device *dev) struct iommu_table *tbl = get_iommu_table_base(dev); if (tbl) - iommu_free_table(tbl, dev->of_node ? - dev->of_node->full_name : dev_name(dev)); + iommu_free_table(tbl, of_node_full_name(dev->of_node)); of_node_put(dev->of_node); kfree(to_vio_dev(dev)); } @@ -1509,7 +1508,7 @@ static ssize_t devspec_show(struct device *dev, { struct device_node *of_node = dev->of_node; - return sprintf(buf, "%s\n", of_node ? of_node->full_name : "none"); + return sprintf(buf, "%s\n", of_node_full_name(of_node)); } static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index b9f509a34c01..b6732004c882 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -552,8 +552,7 @@ static struct iommu_table *cell_get_iommu_table(struct device *dev) iommu = cell_iommu_for_node(dev_to_node(dev)); if (iommu == NULL || list_empty(&iommu->windows)) { printk(KERN_ERR "iommu: missing iommu for %s (node %d)\n", - dev->of_node ? dev->of_node->full_name : "?", - dev_to_node(dev)); + of_node_full_name(dev->of_node), dev_to_node(dev)); return NULL; } window = list_entry(iommu->windows.next, struct iommu_window, list); diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 2d311c0caf8e..6b58a395dff6 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -1051,7 +1051,7 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev) if (!pdn || !PCI_DN(pdn)) { printk(KERN_WARNING "pci_dma_dev_setup_pSeriesLP: " "no DMA window found for pci dev=%s dn=%s\n", - pci_name(dev), dn? dn->full_name : ""); + pci_name(dev), of_node_full_name(dn)); return; } pr_debug(" parent is %s\n", pdn->full_name); diff --git a/arch/sparc/kernel/of_device_64.c b/arch/sparc/kernel/of_device_64.c index 7a3be6f6737a..7bbdc26d9512 100644 --- a/arch/sparc/kernel/of_device_64.c +++ b/arch/sparc/kernel/of_device_64.c @@ -580,7 +580,7 @@ static unsigned int __init build_one_device_irq(struct platform_device *op, printk("%s: Apply [%s:%x] imap --> [%s:%x]\n", op->dev.of_node->full_name, pp->full_name, this_orig_irq, - (iret ? iret->full_name : "NULL"), irq); + of_node_full_name(iret), irq); if (!iret) break; diff --git a/drivers/of/base.c b/drivers/of/base.c index 85757952f12d..9ec0a2f1b028 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1173,7 +1173,7 @@ static void of_alias_add(struct alias_prop *ap, struct device_node *np, ap->stem[stem_len] = 0; list_add_tail(&ap->link, &aliases_lookup); pr_debug("adding DT alias:%s: stem=%s id=%i node=%s\n", - ap->alias, ap->stem, ap->id, np ? np->full_name : NULL); + ap->alias, ap->stem, ap->id, of_node_full_name(np)); } /** diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 9cf00602f566..ff8ab7b27373 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -255,7 +255,7 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, skiplevel: /* Iterate again with new parent */ - pr_debug(" -> new parent: %s\n", newpar ? newpar->full_name : "<>"); + pr_debug(" -> new parent: %s\n", of_node_full_name(newpar)); of_node_put(ipar); ipar = newpar; newpar = NULL; diff --git a/include/linux/of.h b/include/linux/of.h index 2ec1083af7ff..1012377cae92 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -163,6 +163,11 @@ static inline int of_node_to_nid(struct device_node *np) { return -1; } #define of_node_to_nid of_node_to_nid #endif +static inline const char* of_node_full_name(struct device_node *np) +{ + return np ? np->full_name : ""; +} + extern struct device_node *of_find_node_by_name(struct device_node *from, const char *name); #define for_each_node_by_name(dn, name) \ @@ -303,6 +308,11 @@ const char *of_prop_next_string(struct property *prop, const char *cur); #else /* CONFIG_OF */ +static inline const char* of_node_full_name(struct device_node *np) +{ + return ""; +} + static inline bool of_have_populated_dt(void) { return false; diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 41c1564103f1..38c5eb839c92 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -448,7 +448,7 @@ unsigned int irq_create_mapping(struct irq_domain *domain, } pr_debug("irq %lu on domain %s mapped to virtual irq %u\n", - hwirq, domain->of_node ? domain->of_node->full_name : "null", virq); + hwirq, of_node_full_name(domain->of_node), virq); return virq; } @@ -477,7 +477,7 @@ unsigned int irq_create_of_mapping(struct device_node *controller, return intspec[0]; #endif pr_warning("no irq domain found for %s !\n", - controller->full_name); + of_node_full_name(controller)); return 0; } @@ -725,8 +725,8 @@ static int virq_debug_show(struct seq_file *m, void *private) data = irq_desc_get_chip_data(desc); seq_printf(m, data ? "0x%p " : " %p ", data); - if (desc->irq_data.domain && desc->irq_data.domain->of_node) - p = desc->irq_data.domain->of_node->full_name; + if (desc->irq_data.domain) + p = of_node_full_name(desc->irq_data.domain->of_node); else p = none; seq_printf(m, "%s\n", p); -- cgit v1.2.3-59-g8ed1b From 5cf05ad758c30d17ff23c2be346b5de982bc2121 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 17 May 2012 15:12:45 -0700 Subject: rcu: Fix broken strings in RCU's source code. Although the C language allows you to break strings across lines, doing this makes it hard for people to find the Linux kernel code corresponding to a given console message. This commit therefore fixes broken strings throughout RCU's source code. Suggested-by: Josh Triplett Suggested-by: Ingo Molnar Signed-off-by: Paul E. McKenney --- include/linux/rcupdate.h | 3 +-- kernel/rcutorture.c | 33 ++++++++++++++------------------- kernel/rcutree_trace.c | 25 ++++++++++++------------- lib/list_debug.c | 6 ++---- 4 files changed, 29 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index c2c0d86dd3ac..115ead2b5155 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -427,8 +427,7 @@ extern int rcu_my_thread_group_empty(void); static inline void rcu_preempt_sleep_check(void) { rcu_lockdep_assert(!lock_is_held(&rcu_lock_map), - "Illegal context switch in RCU read-side " - "critical section"); + "Illegal context switch in RCU read-side critical section"); } #else /* #ifdef CONFIG_PROVE_RCU */ static inline void rcu_preempt_sleep_check(void) diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index 155fb129b641..25b15033c61f 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c @@ -49,8 +49,7 @@ #include MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Paul E. McKenney and " - "Josh Triplett "); +MODULE_AUTHOR("Paul E. McKenney and Josh Triplett "); static int nreaders = -1; /* # reader threads, defaults to 2*ncpus */ static int nfakewriters = 4; /* # fake writer threads */ @@ -1200,27 +1199,27 @@ rcu_torture_printk(char *page) } 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 " - "rtmbe: %d rtbke: %ld rtbre: %ld " - "rtbf: %ld rtb: %ld nt: %ld " - "onoff: %ld/%ld:%ld/%ld " - "barrier: %ld/%ld:%ld", + "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), + 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, + 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, + n_rcu_torture_timers); + cnt += sprintf(&page[cnt], "onoff: %ld/%ld:%ld/%ld ", n_online_successes, n_online_attempts, n_offline_successes, - n_offline_attempts, + n_offline_attempts); + cnt += sprintf(&page[cnt], "barrier: %ld/%ld:%ld", n_barrier_successes, n_barrier_attempts, n_rcu_torture_barrier_error); @@ -1462,8 +1461,7 @@ rcu_torture_shutdown(void *arg) delta = shutdown_time - jiffies_snap; if (verbose) printk(KERN_ALERT "%s" TORTURE_FLAG - "rcu_torture_shutdown task: %lu " - "jiffies remaining\n", + "rcu_torture_shutdown task: %lu jiffies remaining\n", torture_type, delta); schedule_timeout_interruptible(delta); jiffies_snap = ACCESS_ONCE(jiffies); @@ -1515,8 +1513,7 @@ rcu_torture_onoff(void *arg) if (cpu_down(cpu) == 0) { if (verbose) printk(KERN_ALERT "%s" TORTURE_FLAG - "rcu_torture_onoff task: " - "offlined %d\n", + "rcu_torture_onoff task: offlined %d\n", torture_type, cpu); n_offline_successes++; } @@ -1529,8 +1526,7 @@ rcu_torture_onoff(void *arg) if (cpu_up(cpu) == 0) { if (verbose) printk(KERN_ALERT "%s" TORTURE_FLAG - "rcu_torture_onoff task: " - "onlined %d\n", + "rcu_torture_onoff task: onlined %d\n", torture_type, cpu); n_online_successes++; } @@ -1952,8 +1948,7 @@ rcu_torture_init(void) return -EINVAL; } if (cur_ops->fqs == NULL && fqs_duration != 0) { - printk(KERN_ALERT "rcu-torture: ->fqs NULL and non-zero " - "fqs_duration, fqs disabled.\n"); + printk(KERN_ALERT "rcu-torture: ->fqs NULL and non-zero fqs_duration, fqs disabled.\n"); fqs_duration = 0; } if (cur_ops->init) diff --git a/kernel/rcutree_trace.c b/kernel/rcutree_trace.c index a16ddbd6fdc4..abffb486e94e 100644 --- a/kernel/rcutree_trace.c +++ b/kernel/rcutree_trace.c @@ -218,8 +218,7 @@ static const struct file_operations rcudata_csv_fops = { 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 " - "j=%04x bt=%04x\n", + 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], @@ -227,11 +226,11 @@ static void print_one_rcu_node_boost(struct seq_file *m, struct rcu_node *rnp) "B."[!rnp->boost_tasks], convert_kthread_status(rnp->boost_kthread_status), rnp->n_tasks_boosted, rnp->n_exp_boosts, - rnp->n_normal_boosts, + rnp->n_normal_boosts); + seq_printf(m, "j=%04x bt=%04x\n", (int)(jiffies & 0xffff), (int)(rnp->boost_time & 0xffff)); - seq_printf(m, "%s: nt=%lu egt=%lu bt=%lu nb=%lu ny=%lu nos=%lu\n", - " balk", + 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, @@ -287,11 +286,11 @@ static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp) struct rcu_node *rnp; gpnum = rsp->gpnum; - seq_printf(m, "%s: c=%lu g=%lu s=%d jfq=%ld j=%x " - "nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu oqlen=%ld/%ld\n", + seq_printf(m, "%s: c=%lu g=%lu s=%d jfq=%ld j=%x ", rsp->name, rsp->completed, gpnum, rsp->fqs_state, (long)(rsp->jiffies_force_qs - jiffies), - (int)(jiffies & 0xffff), + (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); @@ -378,16 +377,16 @@ static const struct file_operations rcugp_fops = { static void print_one_rcu_pending(struct seq_file *m, struct rcu_data *rdp) { - seq_printf(m, "%3d%cnp=%ld " - "qsp=%ld rpq=%ld cbr=%ld cng=%ld " - "gpc=%ld gps=%ld nf=%ld nn=%ld\n", + seq_printf(m, "%3d%cnp=%ld ", rdp->cpu, cpu_is_offline(rdp->cpu) ? '!' : ' ', - rdp->n_rcu_pending, + 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, + rdp->n_rp_cpu_needs_gp); + seq_printf(m, "gpc=%ld gps=%ld nf=%ld nn=%ld\n", rdp->n_rp_gp_completed, rdp->n_rp_gp_started, rdp->n_rp_need_fqs, diff --git a/lib/list_debug.c b/lib/list_debug.c index 23a5e031cd8b..c24c2f7e296f 100644 --- a/lib/list_debug.c +++ b/lib/list_debug.c @@ -87,12 +87,10 @@ void __list_add_rcu(struct list_head *new, struct list_head *prev, struct list_head *next) { WARN(next->prev != prev, - "list_add_rcu corruption. next->prev should be " - "prev (%p), but was %p. (next=%p).\n", + "list_add_rcu corruption. next->prev should be prev (%p), but was %p. (next=%p).\n", prev, next->prev, next); WARN(prev->next != next, - "list_add_rcu corruption. prev->next should be " - "next (%p), but was %p. (prev=%p).\n", + "list_add_rcu corruption. prev->next should be next (%p), but was %p. (prev=%p).\n", next, prev->next, prev); new->next = next; new->prev = prev; -- cgit v1.2.3-59-g8ed1b From 19228a6a59250d414824ae07e06ad057a404ea3e Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 6 Jul 2012 14:13:12 +0530 Subject: regulator: tps65910: add support for input supply There is multiple voltage input pins on device which takes the voltage input for different voltage regulator. Support to configure the voltage input supplied by different regulator for each regulators. Signed-off-by: Laxman Dewangan Acked-by: Stephen Warren Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/mfd/tps65910.txt | 25 +++++++++++++++ drivers/regulator/tps65910-regulator.c | 36 ++++++++++++++++++++++ include/linux/mfd/tps65910.h | 2 ++ 3 files changed, 63 insertions(+) (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/mfd/tps65910.txt b/Documentation/devicetree/bindings/mfd/tps65910.txt index b51d2066599c..31be5a3d9f76 100644 --- a/Documentation/devicetree/bindings/mfd/tps65910.txt +++ b/Documentation/devicetree/bindings/mfd/tps65910.txt @@ -32,6 +32,28 @@ Optional properties: comparator. (see VMBCH_VSEL in TPS65910 datasheet) - ti,en-gpio-sleep: enable sleep control for gpios There should be 9 entries here, one for each gpio. +- xxx-supply: Input voltage supply regulator. + Missing of these properties will be assume as there is no supply regulator + for that input pins and always powered on. + The valid input supply properties are: + tps65910: + vcc1-supply: VDD1 input. + vcc2-supply: VDD2 input. + vcc3-supply: VAUX33 and VMMC input. + vcc4-supply: VAUX1 and VAUX2 input. + vcc5-supply: VPLL and VDAC input. + vcc6-supply: VDIG1 and VDIG2 input. + vcc7-supply: VRTC input. + vccio-supply: VIO input. + tps65911: + vcc1-supply: VDD1 input. + vcc2-supply: VDD2 input. + vcc3-supply: LDO6, LDO7 and LDO8 input. + vcc4-supply: LDO5 input. + vcc5-supply: LDO3 and LDO4 input. + vcc6-supply: LDO1 and LDO2 input. + vcc7-supply: VRTC input. + vccio-supply: VIO input. Regulator Optional properties: - ti,regulator-ext-sleep-control: enable external sleep @@ -57,6 +79,9 @@ Example: ti,en-gpio-sleep = <0 0 1 0 0 0 0 0 0>; + vcc7-supply = <®_parent>; + vcc1-supply = <®_parent>; + regulators { #address-cells = <1>; #size-cells = <0>; diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index a534e0872006..e319d963fee6 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -85,6 +85,7 @@ static const unsigned int VMMC_VSEL_table[] = { struct tps_info { const char *name; + const char *vin_name; u8 n_voltages; const unsigned int *voltage_table; int enable_time_us; @@ -93,20 +94,24 @@ struct tps_info { static struct tps_info tps65910_regs[] = { { .name = "vrtc", + .vin_name = "vcc7", .enable_time_us = 2200, }, { .name = "vio", + .vin_name = "vccio", .n_voltages = ARRAY_SIZE(VIO_VSEL_table), .voltage_table = VIO_VSEL_table, .enable_time_us = 350, }, { .name = "vdd1", + .vin_name = "vcc1", .enable_time_us = 350, }, { .name = "vdd2", + .vin_name = "vcc2", .enable_time_us = 350, }, { @@ -117,48 +122,56 @@ static struct tps_info tps65910_regs[] = { }, { .name = "vdig1", + .vin_name = "vcc6", .n_voltages = ARRAY_SIZE(VDIG1_VSEL_table), .voltage_table = VDIG1_VSEL_table, .enable_time_us = 100, }, { .name = "vdig2", + .vin_name = "vcc6", .n_voltages = ARRAY_SIZE(VDIG2_VSEL_table), .voltage_table = VDIG2_VSEL_table, .enable_time_us = 100, }, { .name = "vpll", + .vin_name = "vcc5", .n_voltages = ARRAY_SIZE(VPLL_VSEL_table), .voltage_table = VPLL_VSEL_table, .enable_time_us = 100, }, { .name = "vdac", + .vin_name = "vcc5", .n_voltages = ARRAY_SIZE(VDAC_VSEL_table), .voltage_table = VDAC_VSEL_table, .enable_time_us = 100, }, { .name = "vaux1", + .vin_name = "vcc4", .n_voltages = ARRAY_SIZE(VAUX1_VSEL_table), .voltage_table = VAUX1_VSEL_table, .enable_time_us = 100, }, { .name = "vaux2", + .vin_name = "vcc4", .n_voltages = ARRAY_SIZE(VAUX2_VSEL_table), .voltage_table = VAUX2_VSEL_table, .enable_time_us = 100, }, { .name = "vaux33", + .vin_name = "vcc3", .n_voltages = ARRAY_SIZE(VAUX33_VSEL_table), .voltage_table = VAUX33_VSEL_table, .enable_time_us = 100, }, { .name = "vmmc", + .vin_name = "vcc3", .n_voltages = ARRAY_SIZE(VMMC_VSEL_table), .voltage_table = VMMC_VSEL_table, .enable_time_us = 100, @@ -168,21 +181,25 @@ static struct tps_info tps65910_regs[] = { static struct tps_info tps65911_regs[] = { { .name = "vrtc", + .vin_name = "vcc7", .enable_time_us = 2200, }, { .name = "vio", + .vin_name = "vccio", .n_voltages = ARRAY_SIZE(VIO_VSEL_table), .voltage_table = VIO_VSEL_table, .enable_time_us = 350, }, { .name = "vdd1", + .vin_name = "vcc1", .n_voltages = 73, .enable_time_us = 350, }, { .name = "vdd2", + .vin_name = "vcc2", .n_voltages = 73, .enable_time_us = 350, }, @@ -193,41 +210,49 @@ static struct tps_info tps65911_regs[] = { }, { .name = "ldo1", + .vin_name = "vcc6", .n_voltages = 47, .enable_time_us = 420, }, { .name = "ldo2", + .vin_name = "vcc6", .n_voltages = 47, .enable_time_us = 420, }, { .name = "ldo3", + .vin_name = "vcc5", .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo4", + .vin_name = "vcc5", .n_voltages = 47, .enable_time_us = 230, }, { .name = "ldo5", + .vin_name = "vcc4", .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo6", + .vin_name = "vcc3", .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo7", + .vin_name = "vcc3", .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo8", + .vin_name = "vcc3", .n_voltages = 24, .enable_time_us = 230, }, @@ -1013,6 +1038,9 @@ static struct tps65910_board *tps65910_parse_dt_reg_data( *tps65910_reg_matches = matches; for (idx = 0; idx < count; idx++) { + struct tps_info *info = matches[idx].driver_data; + char in_supply[32]; /* 32 is max size of property name */ + if (!matches[idx].init_data || !matches[idx].of_node) continue; @@ -1023,6 +1051,13 @@ static struct tps65910_board *tps65910_parse_dt_reg_data( "ti,regulator-ext-sleep-control", &prop); if (!ret) pmic_plat_data->regulator_ext_sleep_control[idx] = prop; + + if (info->vin_name) { + snprintf(in_supply, 32, "%s-supply", info->vin_name); + if (of_find_property(np, in_supply, 0)) + pmic_plat_data->input_supply[idx] = + info->vin_name; + } } return pmic_plat_data; @@ -1126,6 +1161,7 @@ static __devinit int tps65910_probe(struct platform_device *pdev) pmic->info[i] = info; pmic->desc[i].name = info->name; + pmic->desc[i].supply_name = pmic_plat_data->input_supply[i]; pmic->desc[i].id = i; pmic->desc[i].n_voltages = info->n_voltages; pmic->desc[i].enable_time = info->enable_time_us; diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h index dd8dc0a6c462..c5f806011b32 100644 --- a/include/linux/mfd/tps65910.h +++ b/include/linux/mfd/tps65910.h @@ -799,6 +799,7 @@ struct tps65910_sleep_keepon_data { /** * struct tps65910_board * Board platform data may be used to initialize regulators. + * @input_supply: Name of input supply regulator. */ struct tps65910_board { @@ -811,6 +812,7 @@ struct tps65910_board { struct tps65910_sleep_keepon_data *slp_keepon; bool en_gpio_sleep[TPS6591X_MAX_NUM_GPIO]; unsigned long regulator_ext_sleep_control[TPS65910_NUM_REGS]; + const char *input_supply[TPS65910_NUM_REGS]; struct regulator_init_data *tps65910_pmic_init_data[TPS65910_NUM_REGS]; }; -- cgit v1.2.3-59-g8ed1b From c96d97f4d127b61def87b3ee056bec20cfc265d1 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 5 Jul 2012 04:03:44 +0000 Subject: net/mlx4: Set steering mode according to device capabilities Instead of checking the firmware supported steering mode in various places in the code, add a dedicated field in the mlx4 device capabilities structure which is written once during the initialization flow and read across the code. This also set the grounds for add new steering modes. Currently two modes are supported, and are named after the ConnectX HW versions A0 and B0. A0 steering uses mac_index, vlan_index and priority to steer traffic into pre-defined range of QPs. B0 steering uses Ethernet L2 hashing rules and is enabled only if the firmware supports both unicast and multicast B0 steering, The current steering modes are relevant for Ethernet traffic only, such that Infiniband steering remains untouched. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 108 ++++++++++++++++--------- drivers/net/ethernet/mellanox/mlx4/fw.c | 2 +- drivers/net/ethernet/mellanox/mlx4/main.c | 16 +++- drivers/net/ethernet/mellanox/mlx4/mcg.c | 70 ++++++++-------- drivers/net/ethernet/mellanox/mlx4/port.c | 9 +-- include/linux/mlx4/device.h | 24 ++++++ 6 files changed, 148 insertions(+), 81 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index bedcbb30d38f..44ff7cdb15e5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -265,7 +265,7 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) struct mlx4_en_mc_list *mclist, *tmp; u64 mcast_addr = 0; u8 mc_list[16] = {0}; - int err; + int err = 0; mutex_lock(&mdev->state_lock); if (!mdev->device_up) { @@ -300,16 +300,36 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) priv->flags |= MLX4_EN_FLAG_PROMISC; /* Enable promiscouos mode */ - if (!(mdev->dev->caps.flags & - MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) - err = mlx4_SET_PORT_qpn_calc(mdev->dev, priv->port, - priv->base_qpn, 1); - else - err = mlx4_unicast_promisc_add(mdev->dev, priv->base_qpn, + switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_B0: + err = mlx4_unicast_promisc_add(mdev->dev, + priv->base_qpn, priv->port); - if (err) - en_err(priv, "Failed enabling " - "promiscuous mode\n"); + if (err) + en_err(priv, "Failed enabling unicast promiscuous mode\n"); + + /* Add the default qp number as multicast + * promisc + */ + if (!(priv->flags & MLX4_EN_FLAG_MC_PROMISC)) { + err = mlx4_multicast_promisc_add(mdev->dev, + priv->base_qpn, + priv->port); + if (err) + en_err(priv, "Failed enabling multicast promiscuous mode\n"); + priv->flags |= MLX4_EN_FLAG_MC_PROMISC; + } + break; + + case MLX4_STEERING_MODE_A0: + err = mlx4_SET_PORT_qpn_calc(mdev->dev, + priv->port, + priv->base_qpn, + 1); + if (err) + en_err(priv, "Failed enabling promiscuous mode\n"); + break; + } /* Disable port multicast filter (unconditionally) */ err = mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0, @@ -318,15 +338,6 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) en_err(priv, "Failed disabling " "multicast filter\n"); - /* Add the default qp number as multicast promisc */ - if (!(priv->flags & MLX4_EN_FLAG_MC_PROMISC)) { - err = mlx4_multicast_promisc_add(mdev->dev, priv->base_qpn, - priv->port); - if (err) - en_err(priv, "Failed entering multicast promisc mode\n"); - priv->flags |= MLX4_EN_FLAG_MC_PROMISC; - } - /* Disable port VLAN filter */ err = mlx4_SET_VLAN_FLTR(mdev->dev, priv); if (err) @@ -345,22 +356,31 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) priv->flags &= ~MLX4_EN_FLAG_PROMISC; /* Disable promiscouos mode */ - if (!(mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) - err = mlx4_SET_PORT_qpn_calc(mdev->dev, priv->port, - priv->base_qpn, 0); - else - err = mlx4_unicast_promisc_remove(mdev->dev, priv->base_qpn, + switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_B0: + err = mlx4_unicast_promisc_remove(mdev->dev, + priv->base_qpn, priv->port); - if (err) - en_err(priv, "Failed disabling promiscuous mode\n"); + if (err) + en_err(priv, "Failed disabling unicast promiscuous mode\n"); + /* Disable Multicast promisc */ + if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) { + err = mlx4_multicast_promisc_remove(mdev->dev, + priv->base_qpn, + priv->port); + if (err) + en_err(priv, "Failed disabling multicast promiscuous mode\n"); + priv->flags &= ~MLX4_EN_FLAG_MC_PROMISC; + } + break; - /* Disable Multicast promisc */ - if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) { - err = mlx4_multicast_promisc_remove(mdev->dev, priv->base_qpn, - priv->port); + case MLX4_STEERING_MODE_A0: + err = mlx4_SET_PORT_qpn_calc(mdev->dev, + priv->port, + priv->base_qpn, 0); if (err) - en_err(priv, "Failed disabling multicast promiscuous mode\n"); - priv->flags &= ~MLX4_EN_FLAG_MC_PROMISC; + en_err(priv, "Failed disabling promiscuous mode\n"); + break; } /* Enable port VLAN filter */ @@ -378,8 +398,16 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) /* Add the default qp number as multicast promisc */ if (!(priv->flags & MLX4_EN_FLAG_MC_PROMISC)) { - err = mlx4_multicast_promisc_add(mdev->dev, priv->base_qpn, - priv->port); + switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_B0: + err = mlx4_multicast_promisc_add(mdev->dev, + priv->base_qpn, + priv->port); + break; + + case MLX4_STEERING_MODE_A0: + break; + } if (err) en_err(priv, "Failed entering multicast promisc mode\n"); priv->flags |= MLX4_EN_FLAG_MC_PROMISC; @@ -387,8 +415,16 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) } else { /* Disable Multicast promisc */ if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) { - err = mlx4_multicast_promisc_remove(mdev->dev, priv->base_qpn, - priv->port); + switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_B0: + err = mlx4_multicast_promisc_remove(mdev->dev, + priv->base_qpn, + priv->port); + break; + + case MLX4_STEERING_MODE_A0: + break; + } if (err) en_err(priv, "Failed disabling multicast promiscuous mode\n"); priv->flags &= ~MLX4_EN_FLAG_MC_PROMISC; diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 9c83bb8151ea..40e048bac024 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -1124,7 +1124,7 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) MLX4_PUT(inbox, param->mc_base, INIT_HCA_MC_BASE_OFFSET); MLX4_PUT(inbox, param->log_mc_entry_sz, INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET); MLX4_PUT(inbox, param->log_mc_hash_sz, INIT_HCA_LOG_MC_HASH_SZ_OFFSET); - if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) + if (dev->caps.steering_mode == MLX4_STEERING_MODE_B0) MLX4_PUT(inbox, (u8) (1 << 3), INIT_HCA_UC_STEERING_OFFSET); MLX4_PUT(inbox, param->log_mc_table_sz, INIT_HCA_LOG_MC_TABLE_SZ_OFFSET); diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 14d9c762b60f..f8125a82c0cb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -244,7 +244,6 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.reserved_srqs = dev_cap->reserved_srqs; dev->caps.max_sq_desc_sz = dev_cap->max_sq_desc_sz; dev->caps.max_rq_desc_sz = dev_cap->max_rq_desc_sz; - dev->caps.num_qp_per_mgm = mlx4_get_qp_per_mgm(dev); /* * Subtract 1 from the limit because we need to allocate a * spare CQE so the HCA HW can tell the difference between an @@ -275,6 +274,21 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.max_gso_sz = dev_cap->max_gso_sz; dev->caps.max_rss_tbl_sz = dev_cap->max_rss_tbl_sz; + if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER && + dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) { + dev->caps.steering_mode = MLX4_STEERING_MODE_B0; + } else { + dev->caps.steering_mode = MLX4_STEERING_MODE_A0; + + if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER || + dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) + mlx4_warn(dev, "Must have UC_STEER and MC_STEER flags " + "set to use B0 steering. Falling back to A0 steering mode.\n"); + } + mlx4_dbg(dev, "Steering mode is: %s\n", + mlx4_steering_mode_str(dev->caps.steering_mode)); + dev->caps.num_qp_per_mgm = mlx4_get_qp_per_mgm(dev); + /* Sense port always allowed on supported devices for ConnectX1 and 2 */ if (dev->pdev->device != 0x1003) dev->caps.flags |= MLX4_DEV_CAP_FLAG_SENSE_SUPPORT; diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index f4a8f98e402a..319c9d45d59a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -868,36 +868,50 @@ static int mlx4_QP_ATTACH(struct mlx4_dev *dev, struct mlx4_qp *qp, int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], int block_mcast_loopback, enum mlx4_protocol prot) { - if (prot == MLX4_PROT_ETH && - !(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER)) - return 0; - if (prot == MLX4_PROT_ETH) - gid[7] |= (MLX4_MC_STEER << 1); + switch (dev->caps.steering_mode) { + case MLX4_STEERING_MODE_A0: + if (prot == MLX4_PROT_ETH) + return 0; - if (mlx4_is_mfunc(dev)) - return mlx4_QP_ATTACH(dev, qp, gid, 1, - block_mcast_loopback, prot); + case MLX4_STEERING_MODE_B0: + if (prot == MLX4_PROT_ETH) + gid[7] |= (MLX4_MC_STEER << 1); - return mlx4_qp_attach_common(dev, qp, gid, block_mcast_loopback, - prot, MLX4_MC_STEER); + if (mlx4_is_mfunc(dev)) + return mlx4_QP_ATTACH(dev, qp, gid, 1, + block_mcast_loopback, prot); + return mlx4_qp_attach_common(dev, qp, gid, + block_mcast_loopback, prot, + MLX4_MC_STEER); + + default: + return -EINVAL; + } } EXPORT_SYMBOL_GPL(mlx4_multicast_attach); int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], enum mlx4_protocol prot) { - if (prot == MLX4_PROT_ETH && - !(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER)) - return 0; + switch (dev->caps.steering_mode) { + case MLX4_STEERING_MODE_A0: + if (prot == MLX4_PROT_ETH) + return 0; - if (prot == MLX4_PROT_ETH) - gid[7] |= (MLX4_MC_STEER << 1); + case MLX4_STEERING_MODE_B0: + if (prot == MLX4_PROT_ETH) + gid[7] |= (MLX4_MC_STEER << 1); - if (mlx4_is_mfunc(dev)) - return mlx4_QP_ATTACH(dev, qp, gid, 0, 0, prot); + if (mlx4_is_mfunc(dev)) + return mlx4_QP_ATTACH(dev, qp, gid, 0, 0, prot); + + return mlx4_qp_detach_common(dev, qp, gid, prot, + MLX4_MC_STEER); - return mlx4_qp_detach_common(dev, qp, gid, prot, MLX4_MC_STEER); + default: + return -EINVAL; + } } EXPORT_SYMBOL_GPL(mlx4_multicast_detach); @@ -905,10 +919,6 @@ int mlx4_unicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], int block_mcast_loopback, enum mlx4_protocol prot) { - if (prot == MLX4_PROT_ETH && - !(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) - return 0; - if (prot == MLX4_PROT_ETH) gid[7] |= (MLX4_UC_STEER << 1); @@ -924,10 +934,6 @@ EXPORT_SYMBOL_GPL(mlx4_unicast_attach); int mlx4_unicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], enum mlx4_protocol prot) { - if (prot == MLX4_PROT_ETH && - !(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) - return 0; - if (prot == MLX4_PROT_ETH) gid[7] |= (MLX4_UC_STEER << 1); @@ -968,9 +974,6 @@ static int mlx4_PROMISC(struct mlx4_dev *dev, u32 qpn, int mlx4_multicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port) { - if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER)) - return 0; - if (mlx4_is_mfunc(dev)) return mlx4_PROMISC(dev, qpn, MLX4_MC_STEER, 1, port); @@ -980,9 +983,6 @@ EXPORT_SYMBOL_GPL(mlx4_multicast_promisc_add); int mlx4_multicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port) { - if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER)) - return 0; - if (mlx4_is_mfunc(dev)) return mlx4_PROMISC(dev, qpn, MLX4_MC_STEER, 0, port); @@ -992,9 +992,6 @@ EXPORT_SYMBOL_GPL(mlx4_multicast_promisc_remove); int mlx4_unicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port) { - if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) - return 0; - if (mlx4_is_mfunc(dev)) return mlx4_PROMISC(dev, qpn, MLX4_UC_STEER, 1, port); @@ -1004,9 +1001,6 @@ EXPORT_SYMBOL_GPL(mlx4_unicast_promisc_add); int mlx4_unicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port) { - if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) - return 0; - if (mlx4_is_mfunc(dev)) return mlx4_PROMISC(dev, qpn, MLX4_UC_STEER, 0, port); diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index a8fb52992c64..58de7237f57a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -155,7 +155,7 @@ int mlx4_get_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn) return err; } - if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) { + if (dev->caps.steering_mode == MLX4_STEERING_MODE_A0) { *qpn = info->base_qpn + index; return 0; } @@ -206,7 +206,7 @@ void mlx4_put_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int qpn) (unsigned long long) mac); mlx4_unregister_mac(dev, port, mac); - if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER) { + if (dev->caps.steering_mode != MLX4_STEERING_MODE_A0) { entry = radix_tree_lookup(&info->mac_tree, qpn); if (entry) { mlx4_dbg(dev, "Releasing qp: port %d, mac 0x%llx," @@ -359,7 +359,7 @@ int mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac) int index = qpn - info->base_qpn; int err = 0; - if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER) { + if (dev->caps.steering_mode != MLX4_STEERING_MODE_A0) { entry = radix_tree_lookup(&info->mac_tree, qpn); if (!entry) return -EINVAL; @@ -803,8 +803,7 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, u32 m_promisc = (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) ? MCAST_DIRECT : MCAST_DEFAULT; - if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER && - dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER) + if (dev->caps.steering_mode != MLX4_STEERING_MODE_A0) return 0; mailbox = mlx4_alloc_cmd_mailbox(dev); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 6a8f002b8ed3..7f5c9ee42f96 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -70,6 +70,29 @@ enum { MLX4_MFUNC_EQE_MASK = (MLX4_MFUNC_MAX_EQES - 1) }; +/* Driver supports 2 diffrent device methods to manage traffic steering: + * - B0 steering mode - Common low level API for ib and (if supported) eth. + * - A0 steering mode - Limited low level API for eth. In case of IB, + * B0 mode is in use. + */ +enum { + MLX4_STEERING_MODE_A0, + MLX4_STEERING_MODE_B0 +}; + +static inline const char *mlx4_steering_mode_str(int steering_mode) +{ + switch (steering_mode) { + case MLX4_STEERING_MODE_A0: + return "A0 steering"; + + case MLX4_STEERING_MODE_B0: + return "B0 steering"; + default: + return "Unrecognize steering mode"; + } +} + enum { MLX4_DEV_CAP_FLAG_RC = 1LL << 0, MLX4_DEV_CAP_FLAG_UC = 1LL << 1, @@ -295,6 +318,7 @@ struct mlx4_caps { int num_amgms; int reserved_mcgs; int num_qp_per_mgm; + int steering_mode; int num_pds; int reserved_pds; int max_xrcds; -- cgit v1.2.3-59-g8ed1b From 8fcfb4db74352d3d447b7a559ad54f7577074d19 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 5 Jul 2012 04:03:45 +0000 Subject: net/mlx4_core: Add firmware commands to support device managed flow steering Add support for firmware commands to attach/detach a new device managed steering mode. Such network steering rules allow the user to provide an L2/L3/L4 flow specification to the firmware and have the device to steer traffic that matches that specification to the provided QP. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 19 ++++++++++++++ drivers/net/ethernet/mellanox/mlx4/mcg.c | 29 ++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/mlx4.h | 10 ++++++++ .../net/ethernet/mellanox/mlx4/resource_tracker.c | 24 ++++++++++++++++++ include/linux/mlx4/cmd.h | 4 +++ 5 files changed, 86 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 842c8ce9494e..7e94987d030c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -1080,6 +1080,25 @@ static struct mlx4_cmd_info cmd_info[] = { .verify = NULL, .wrapper = NULL }, + /* flow steering commands */ + { + .opcode = MLX4_QP_FLOW_STEERING_ATTACH, + .has_inbox = true, + .has_outbox = false, + .out_is_imm = true, + .encode_slave_id = false, + .verify = NULL, + .wrapper = mlx4_QP_FLOW_STEERING_ATTACH_wrapper + }, + { + .opcode = MLX4_QP_FLOW_STEERING_DETACH, + .has_inbox = false, + .has_outbox = false, + .out_is_imm = false, + .encode_slave_id = false, + .verify = NULL, + .wrapper = mlx4_QP_FLOW_STEERING_DETACH_wrapper + }, }; static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave, diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index 319c9d45d59a..3c59a33a98a5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -62,6 +62,35 @@ int mlx4_get_qp_per_mgm(struct mlx4_dev *dev) return 4 * (mlx4_get_mgm_entry_size(dev) / 16 - 2); } +static int mlx4_QP_FLOW_STEERING_ATTACH(struct mlx4_dev *dev, + struct mlx4_cmd_mailbox *mailbox, + u32 size, + u64 *reg_id) +{ + u64 imm; + int err = 0; + + err = mlx4_cmd_imm(dev, mailbox->dma, &imm, size, 0, + MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + if (err) + return err; + *reg_id = imm; + + return err; +} + +static int mlx4_QP_FLOW_STEERING_DETACH(struct mlx4_dev *dev, u64 regid) +{ + int err = 0; + + err = mlx4_cmd(dev, regid, 0, 0, + MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + + return err; +} + static int mlx4_READ_ENTRY(struct mlx4_dev *dev, int index, struct mlx4_cmd_mailbox *mailbox) { diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index a425a984758f..c07e882e8369 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -1118,6 +1118,16 @@ int mlx4_QUERY_IF_STAT_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_cmd_mailbox *inbox, struct mlx4_cmd_mailbox *outbox, struct mlx4_cmd_info *cmd); +int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd); +int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd); int mlx4_get_mgm_entry_size(struct mlx4_dev *dev); int mlx4_get_qp_per_mgm(struct mlx4_dev *dev); diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 6bdac2955f8b..a8ca960f4620 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -2738,6 +2738,30 @@ ex_put: return err; } +int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd) +{ + return mlx4_cmd_imm(dev, inbox->dma, &vhcr->out_param, + vhcr->in_modifier, 0, + MLX4_QP_FLOW_STEERING_ATTACH, + MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); +} + +int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd) +{ + return mlx4_cmd(dev, vhcr->in_param, 0, 0, + MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); +} + enum { BUSY_MAX_RETRIES = 10 }; diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h index 1f3860a8a109..260695186256 100644 --- a/include/linux/mlx4/cmd.h +++ b/include/linux/mlx4/cmd.h @@ -154,6 +154,10 @@ enum { /* set port opcode modifiers */ MLX4_SET_PORT_PRIO2TC = 0x8, MLX4_SET_PORT_SCHEDULER = 0x9, + + /* register/delete flow steering network rules */ + MLX4_QP_FLOW_STEERING_ATTACH = 0x65, + MLX4_QP_FLOW_STEERING_DETACH = 0x66, }; enum { -- cgit v1.2.3-59-g8ed1b From 0ff1fb654bec0cff62ddf81a8a8edec4263604a0 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 5 Jul 2012 04:03:46 +0000 Subject: {NET, IB}/mlx4: Add device managed flow steering firmware API The driver is modified to support three operation modes. If supported by firmware use the device managed flow steering API, that which we call device managed steering mode. Else, if the firmware supports the B0 steering mode use it, and finally, if none of the above, use the A0 steering mode. When the steering mode is device managed, the code is modified such that L2 based rules set by the mlx4_en driver for Ethernet unicast and multicast, and the IB stack multicast attach calls done through the mlx4_ib driver are all routed to use the device managed API. When attaching rule using device managed flow steering API, the firmware returns a 64 bit registration id, which is to be provided during detach. Currently the firmware is always programmed during HCA initialization to use standard L2 hashing. Future work should be done to allow configuring the flow-steering hash function with common, non proprietary means. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx4/main.c | 62 +++- drivers/infiniband/hw/mlx4/mlx4_ib.h | 1 + drivers/infiniband/hw/mlx4/qp.c | 1 + drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 21 +- drivers/net/ethernet/mellanox/mlx4/fw.c | 91 ++++- drivers/net/ethernet/mellanox/mlx4/fw.h | 3 + drivers/net/ethernet/mellanox/mlx4/main.c | 56 +++- drivers/net/ethernet/mellanox/mlx4/mcg.c | 365 ++++++++++++++++++++- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 13 + drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 2 + drivers/net/ethernet/mellanox/mlx4/port.c | 98 ++++-- drivers/net/ethernet/mellanox/mlx4/profile.c | 12 +- .../net/ethernet/mellanox/mlx4/resource_tracker.c | 6 + include/linux/mlx4/device.h | 108 +++++- 14 files changed, 758 insertions(+), 81 deletions(-) (limited to 'include/linux') diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 3530c41fcd1f..8a3a2037b005 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -718,26 +718,53 @@ int mlx4_ib_add_mc(struct mlx4_ib_dev *mdev, struct mlx4_ib_qp *mqp, return ret; } +struct mlx4_ib_steering { + struct list_head list; + u64 reg_id; + union ib_gid gid; +}; + static int mlx4_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) { int err; struct mlx4_ib_dev *mdev = to_mdev(ibqp->device); struct mlx4_ib_qp *mqp = to_mqp(ibqp); + u64 reg_id; + struct mlx4_ib_steering *ib_steering = NULL; + + if (mdev->dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) { + ib_steering = kmalloc(sizeof(*ib_steering), GFP_KERNEL); + if (!ib_steering) + return -ENOMEM; + } - err = mlx4_multicast_attach(mdev->dev, &mqp->mqp, gid->raw, - !!(mqp->flags & MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK), - MLX4_PROT_IB_IPV6); + err = mlx4_multicast_attach(mdev->dev, &mqp->mqp, gid->raw, mqp->port, + !!(mqp->flags & + MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK), + MLX4_PROT_IB_IPV6, ®_id); if (err) - return err; + goto err_malloc; err = add_gid_entry(ibqp, gid); if (err) goto err_add; + if (ib_steering) { + memcpy(ib_steering->gid.raw, gid->raw, 16); + ib_steering->reg_id = reg_id; + mutex_lock(&mqp->mutex); + list_add(&ib_steering->list, &mqp->steering_rules); + mutex_unlock(&mqp->mutex); + } return 0; err_add: - mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw, MLX4_PROT_IB_IPV6); + mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw, + MLX4_PROT_IB_IPV6, reg_id); +err_malloc: + kfree(ib_steering); + return err; } @@ -765,9 +792,30 @@ static int mlx4_ib_mcg_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) u8 mac[6]; struct net_device *ndev; struct mlx4_ib_gid_entry *ge; + u64 reg_id = 0; + + if (mdev->dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) { + struct mlx4_ib_steering *ib_steering; + + mutex_lock(&mqp->mutex); + list_for_each_entry(ib_steering, &mqp->steering_rules, list) { + if (!memcmp(ib_steering->gid.raw, gid->raw, 16)) { + list_del(&ib_steering->list); + break; + } + } + mutex_unlock(&mqp->mutex); + if (&ib_steering->list == &mqp->steering_rules) { + pr_err("Couldn't find reg_id for mgid. Steering rule is left attached\n"); + return -EINVAL; + } + reg_id = ib_steering->reg_id; + kfree(ib_steering); + } - err = mlx4_multicast_detach(mdev->dev, - &mqp->mqp, gid->raw, MLX4_PROT_IB_IPV6); + err = mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw, + MLX4_PROT_IB_IPV6, reg_id); if (err) return err; diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index ff36655d23d3..42df4f7a6a5b 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -163,6 +163,7 @@ struct mlx4_ib_qp { u8 state; int mlx_type; struct list_head gid_list; + struct list_head steering_rules; }; struct mlx4_ib_srq { diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 8d4ed24aef93..6af19f6c2b11 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -495,6 +495,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, spin_lock_init(&qp->sq.lock); spin_lock_init(&qp->rq.lock); INIT_LIST_HEAD(&qp->gid_list); + INIT_LIST_HEAD(&qp->steering_rules); qp->state = IB_QPS_RESET; if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 44ff7cdb15e5..eb5ed8e39873 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -463,7 +463,8 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) err = mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, mc_list, - MLX4_PROT_ETH); + MLX4_PROT_ETH, + mclist->reg_id); if (err) en_err(priv, "Fail to detach multicast address\n"); @@ -475,11 +476,14 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) if (mclist->action == MCLIST_ADD) { /* attach the address */ memcpy(&mc_list[10], mclist->addr, ETH_ALEN); + /* needed for B0 steering support */ mc_list[5] = priv->port; err = mlx4_multicast_attach(mdev->dev, &priv->rss_map.indir_qp, - mc_list, 0, - MLX4_PROT_ETH); + mc_list, + priv->port, 0, + MLX4_PROT_ETH, + &mclist->reg_id); if (err) en_err(priv, "Fail to attach multicast address\n"); @@ -827,9 +831,10 @@ int mlx4_en_start_port(struct net_device *dev) /* Attach rx QP to bradcast address */ memset(&mc_list[10], 0xff, ETH_ALEN); - mc_list[5] = priv->port; + mc_list[5] = priv->port; /* needed for B0 steering support */ if (mlx4_multicast_attach(mdev->dev, &priv->rss_map.indir_qp, mc_list, - 0, MLX4_PROT_ETH)) + priv->port, 0, MLX4_PROT_ETH, + &priv->broadcast_id)) mlx4_warn(mdev, "Failed Attaching Broadcast\n"); /* Must redo promiscuous mode setup. */ @@ -886,14 +891,14 @@ void mlx4_en_stop_port(struct net_device *dev) /* Detach All multicasts */ memset(&mc_list[10], 0xff, ETH_ALEN); - mc_list[5] = priv->port; + mc_list[5] = priv->port; /* needed for B0 steering support */ mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, mc_list, - MLX4_PROT_ETH); + MLX4_PROT_ETH, priv->broadcast_id); list_for_each_entry(mclist, &priv->curr_list, list) { memcpy(&mc_list[10], mclist->addr, ETH_ALEN); mc_list[5] = priv->port; mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, - mc_list, MLX4_PROT_ETH); + mc_list, MLX4_PROT_ETH, mclist->reg_id); } mlx4_en_clear_list(dev); list_for_each_entry_safe(mclist, tmp, &priv->curr_list, list) { diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 40e048bac024..1d70657058a5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -123,7 +123,8 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) static const char * const fname[] = { [0] = "RSS support", [1] = "RSS Toeplitz Hash Function support", - [2] = "RSS XOR Hash Function support" + [2] = "RSS XOR Hash Function support", + [3] = "Device manage flow steering support" }; int i; @@ -391,6 +392,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) #define QUERY_DEV_CAP_RSVD_XRC_OFFSET 0x66 #define QUERY_DEV_CAP_MAX_XRC_OFFSET 0x67 #define QUERY_DEV_CAP_MAX_COUNTERS_OFFSET 0x68 +#define QUERY_DEV_CAP_FLOW_STEERING_RANGE_EN_OFFSET 0x76 +#define QUERY_DEV_CAP_FLOW_STEERING_MAX_QP_OFFSET 0x77 #define QUERY_DEV_CAP_RDMARC_ENTRY_SZ_OFFSET 0x80 #define QUERY_DEV_CAP_QPC_ENTRY_SZ_OFFSET 0x82 #define QUERY_DEV_CAP_AUX_ENTRY_SZ_OFFSET 0x84 @@ -474,6 +477,12 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev_cap->num_ports = field & 0xf; MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_MSG_SZ_OFFSET); dev_cap->max_msg_sz = 1 << (field & 0x1f); + MLX4_GET(field, outbox, QUERY_DEV_CAP_FLOW_STEERING_RANGE_EN_OFFSET); + if (field & 0x80) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_FS_EN; + dev_cap->fs_log_max_ucast_qp_range_size = field & 0x1f; + MLX4_GET(field, outbox, QUERY_DEV_CAP_FLOW_STEERING_MAX_QP_OFFSET); + dev_cap->fs_max_num_qp_per_entry = field; MLX4_GET(stat_rate, outbox, QUERY_DEV_CAP_RATE_SUPPORT_OFFSET); dev_cap->stat_rate_support = stat_rate; MLX4_GET(ext_flags, outbox, QUERY_DEV_CAP_EXT_FLAGS_OFFSET); @@ -1061,6 +1070,15 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) #define INIT_HCA_LOG_MC_HASH_SZ_OFFSET (INIT_HCA_MCAST_OFFSET + 0x16) #define INIT_HCA_UC_STEERING_OFFSET (INIT_HCA_MCAST_OFFSET + 0x18) #define INIT_HCA_LOG_MC_TABLE_SZ_OFFSET (INIT_HCA_MCAST_OFFSET + 0x1b) +#define INIT_HCA_DEVICE_MANAGED_FLOW_STEERING_EN 0x6 +#define INIT_HCA_FS_PARAM_OFFSET 0x1d0 +#define INIT_HCA_FS_BASE_OFFSET (INIT_HCA_FS_PARAM_OFFSET + 0x00) +#define INIT_HCA_FS_LOG_ENTRY_SZ_OFFSET (INIT_HCA_FS_PARAM_OFFSET + 0x12) +#define INIT_HCA_FS_LOG_TABLE_SZ_OFFSET (INIT_HCA_FS_PARAM_OFFSET + 0x1b) +#define INIT_HCA_FS_ETH_BITS_OFFSET (INIT_HCA_FS_PARAM_OFFSET + 0x21) +#define INIT_HCA_FS_ETH_NUM_ADDRS_OFFSET (INIT_HCA_FS_PARAM_OFFSET + 0x22) +#define INIT_HCA_FS_IB_BITS_OFFSET (INIT_HCA_FS_PARAM_OFFSET + 0x25) +#define INIT_HCA_FS_IB_NUM_ADDRS_OFFSET (INIT_HCA_FS_PARAM_OFFSET + 0x26) #define INIT_HCA_TPT_OFFSET 0x0f0 #define INIT_HCA_DMPT_BASE_OFFSET (INIT_HCA_TPT_OFFSET + 0x00) #define INIT_HCA_LOG_MPT_SZ_OFFSET (INIT_HCA_TPT_OFFSET + 0x0b) @@ -1119,14 +1137,44 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) MLX4_PUT(inbox, param->rdmarc_base, INIT_HCA_RDMARC_BASE_OFFSET); MLX4_PUT(inbox, param->log_rd_per_qp, INIT_HCA_LOG_RD_OFFSET); - /* multicast attributes */ - - MLX4_PUT(inbox, param->mc_base, INIT_HCA_MC_BASE_OFFSET); - MLX4_PUT(inbox, param->log_mc_entry_sz, INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET); - MLX4_PUT(inbox, param->log_mc_hash_sz, INIT_HCA_LOG_MC_HASH_SZ_OFFSET); - if (dev->caps.steering_mode == MLX4_STEERING_MODE_B0) - MLX4_PUT(inbox, (u8) (1 << 3), INIT_HCA_UC_STEERING_OFFSET); - MLX4_PUT(inbox, param->log_mc_table_sz, INIT_HCA_LOG_MC_TABLE_SZ_OFFSET); + /* steering attributes */ + if (dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) { + *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= + cpu_to_be32(1 << + INIT_HCA_DEVICE_MANAGED_FLOW_STEERING_EN); + + MLX4_PUT(inbox, param->mc_base, INIT_HCA_FS_BASE_OFFSET); + MLX4_PUT(inbox, param->log_mc_entry_sz, + INIT_HCA_FS_LOG_ENTRY_SZ_OFFSET); + MLX4_PUT(inbox, param->log_mc_table_sz, + INIT_HCA_FS_LOG_TABLE_SZ_OFFSET); + /* Enable Ethernet flow steering + * with udp unicast and tcp unicast + */ + MLX4_PUT(inbox, param->fs_hash_enable_bits, + INIT_HCA_FS_ETH_BITS_OFFSET); + MLX4_PUT(inbox, (u16) MLX4_FS_NUM_OF_L2_ADDR, + INIT_HCA_FS_ETH_NUM_ADDRS_OFFSET); + /* Enable IPoIB flow steering + * with udp unicast and tcp unicast + */ + MLX4_PUT(inbox, param->fs_hash_enable_bits, + INIT_HCA_FS_IB_BITS_OFFSET); + MLX4_PUT(inbox, (u16) MLX4_FS_NUM_OF_L2_ADDR, + INIT_HCA_FS_IB_NUM_ADDRS_OFFSET); + } else { + MLX4_PUT(inbox, param->mc_base, INIT_HCA_MC_BASE_OFFSET); + MLX4_PUT(inbox, param->log_mc_entry_sz, + INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET); + MLX4_PUT(inbox, param->log_mc_hash_sz, + INIT_HCA_LOG_MC_HASH_SZ_OFFSET); + MLX4_PUT(inbox, param->log_mc_table_sz, + INIT_HCA_LOG_MC_TABLE_SZ_OFFSET); + if (dev->caps.steering_mode == MLX4_STEERING_MODE_B0) + MLX4_PUT(inbox, (u8) (1 << 3), + INIT_HCA_UC_STEERING_OFFSET); + } /* TPT attributes */ @@ -1188,15 +1236,24 @@ int mlx4_QUERY_HCA(struct mlx4_dev *dev, MLX4_GET(param->rdmarc_base, outbox, INIT_HCA_RDMARC_BASE_OFFSET); MLX4_GET(param->log_rd_per_qp, outbox, INIT_HCA_LOG_RD_OFFSET); - /* multicast attributes */ + /* steering attributes */ + if (dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) { - MLX4_GET(param->mc_base, outbox, INIT_HCA_MC_BASE_OFFSET); - MLX4_GET(param->log_mc_entry_sz, outbox, - INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET); - MLX4_GET(param->log_mc_hash_sz, outbox, - INIT_HCA_LOG_MC_HASH_SZ_OFFSET); - MLX4_GET(param->log_mc_table_sz, outbox, - INIT_HCA_LOG_MC_TABLE_SZ_OFFSET); + MLX4_GET(param->mc_base, outbox, INIT_HCA_FS_BASE_OFFSET); + MLX4_GET(param->log_mc_entry_sz, outbox, + INIT_HCA_FS_LOG_ENTRY_SZ_OFFSET); + MLX4_GET(param->log_mc_table_sz, outbox, + INIT_HCA_FS_LOG_TABLE_SZ_OFFSET); + } else { + MLX4_GET(param->mc_base, outbox, INIT_HCA_MC_BASE_OFFSET); + MLX4_GET(param->log_mc_entry_sz, outbox, + INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET); + MLX4_GET(param->log_mc_hash_sz, outbox, + INIT_HCA_LOG_MC_HASH_SZ_OFFSET); + MLX4_GET(param->log_mc_table_sz, outbox, + INIT_HCA_LOG_MC_TABLE_SZ_OFFSET); + } /* TPT attributes */ diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h index 64c0399e4b78..83fcbbf1b169 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw.h @@ -78,6 +78,8 @@ struct mlx4_dev_cap { u16 wavelength[MLX4_MAX_PORTS + 1]; u64 trans_code[MLX4_MAX_PORTS + 1]; u16 stat_rate_support; + int fs_log_max_ucast_qp_range_size; + int fs_max_num_qp_per_entry; u64 flags; u64 flags2; int reserved_uars; @@ -165,6 +167,7 @@ struct mlx4_init_hca_param { u8 log_mpt_sz; u8 log_uar_sz; u8 uar_page_sz; /* log pg sz in 4k chunks */ + u8 fs_hash_enable_bits; }; struct mlx4_init_ib_param { diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index f8125a82c0cb..42645166bae2 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -91,7 +91,9 @@ module_param_named(log_num_mgm_entry_size, MODULE_PARM_DESC(log_num_mgm_entry_size, "log mgm size, that defines the num" " of qp per mcg, for example:" " 10 gives 248.range: 9<=" - " log_num_mgm_entry_size <= 12"); + " log_num_mgm_entry_size <= 12." + " Not in use with device managed" + " flow steering"); #define MLX4_VF (1 << 0) @@ -274,20 +276,27 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.max_gso_sz = dev_cap->max_gso_sz; dev->caps.max_rss_tbl_sz = dev_cap->max_rss_tbl_sz; - if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER && - dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) { - dev->caps.steering_mode = MLX4_STEERING_MODE_B0; + if (dev_cap->flags2 & MLX4_DEV_CAP_FLAG2_FS_EN) { + dev->caps.steering_mode = MLX4_STEERING_MODE_DEVICE_MANAGED; + dev->caps.num_qp_per_mgm = dev_cap->fs_max_num_qp_per_entry; + dev->caps.fs_log_max_ucast_qp_range_size = + dev_cap->fs_log_max_ucast_qp_range_size; } else { - dev->caps.steering_mode = MLX4_STEERING_MODE_A0; + if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER && + dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) { + dev->caps.steering_mode = MLX4_STEERING_MODE_B0; + } else { + dev->caps.steering_mode = MLX4_STEERING_MODE_A0; - if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER || - dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) - mlx4_warn(dev, "Must have UC_STEER and MC_STEER flags " - "set to use B0 steering. Falling back to A0 steering mode.\n"); + if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER || + dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) + mlx4_warn(dev, "Must have UC_STEER and MC_STEER flags " + "set to use B0 steering. Falling back to A0 steering mode.\n"); + } + dev->caps.num_qp_per_mgm = mlx4_get_qp_per_mgm(dev); } mlx4_dbg(dev, "Steering mode is: %s\n", mlx4_steering_mode_str(dev->caps.steering_mode)); - dev->caps.num_qp_per_mgm = mlx4_get_qp_per_mgm(dev); /* Sense port always allowed on supported devices for ConnectX1 and 2 */ if (dev->pdev->device != 0x1003) @@ -982,9 +991,11 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap, } /* - * It's not strictly required, but for simplicity just map the - * whole multicast group table now. The table isn't very big - * and it's a lot easier than trying to track ref counts. + * For flow steering device managed mode it is required to use + * mlx4_init_icm_table. For B0 steering mode it's not strictly + * required, but for simplicity just map the whole multicast + * group table now. The table isn't very big and it's a lot + * easier than trying to track ref counts. */ err = mlx4_init_icm_table(dev, &priv->mcg_table.table, init_hca->mc_base, @@ -1220,7 +1231,26 @@ static int mlx4_init_hca(struct mlx4_dev *dev) goto err_stop_fw; } + priv->fs_hash_mode = MLX4_FS_L2_HASH; + + switch (priv->fs_hash_mode) { + case MLX4_FS_L2_HASH: + init_hca.fs_hash_enable_bits = 0; + break; + + case MLX4_FS_L2_L3_L4_HASH: + /* Enable flow steering with + * udp unicast and tcp unicast + */ + init_hca.fs_hash_enable_bits = + MLX4_FS_UDP_UC_EN | MLX4_FS_TCP_UC_EN; + break; + } + profile = default_profile; + if (dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) + profile.num_mcg = MLX4_FS_NUM_MCG; icm_size = mlx4_make_profile(dev, &profile, &dev_cap, &init_hca); diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index 3c59a33a98a5..768a2a4530e8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -41,6 +41,7 @@ #define MGM_QPN_MASK 0x00FFFFFF #define MGM_BLCK_LB_BIT 30 +#define MLX4_MAC_MASK 0xffffffffffffULL static const u8 zero_gid[16]; /* automatically initialized to 0 */ @@ -54,7 +55,12 @@ struct mlx4_mgm { int mlx4_get_mgm_entry_size(struct mlx4_dev *dev) { - return min((1 << mlx4_log_num_mgm_entry_size), MLX4_MAX_MGM_ENTRY_SIZE); + if (dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) + return 1 << MLX4_FS_MGM_LOG_ENTRY_SIZE; + else + return min((1 << mlx4_log_num_mgm_entry_size), + MLX4_MAX_MGM_ENTRY_SIZE); } int mlx4_get_qp_per_mgm(struct mlx4_dev *dev) @@ -643,6 +649,311 @@ static int find_entry(struct mlx4_dev *dev, u8 port, return err; } +struct mlx4_net_trans_rule_hw_ctrl { + __be32 ctrl; + __be32 vf_vep_port; + __be32 qpn; + __be32 reserved; +}; + +static void trans_rule_ctrl_to_hw(struct mlx4_net_trans_rule *ctrl, + struct mlx4_net_trans_rule_hw_ctrl *hw) +{ + static const u8 __promisc_mode[] = { + [MLX4_FS_PROMISC_NONE] = 0x0, + [MLX4_FS_PROMISC_UPLINK] = 0x1, + [MLX4_FS_PROMISC_FUNCTION_PORT] = 0x2, + [MLX4_FS_PROMISC_ALL_MULTI] = 0x3, + }; + + u32 dw = 0; + + dw = ctrl->queue_mode == MLX4_NET_TRANS_Q_LIFO ? 1 : 0; + dw |= ctrl->exclusive ? (1 << 2) : 0; + dw |= ctrl->allow_loopback ? (1 << 3) : 0; + dw |= __promisc_mode[ctrl->promisc_mode] << 8; + dw |= ctrl->priority << 16; + + hw->ctrl = cpu_to_be32(dw); + hw->vf_vep_port = cpu_to_be32(ctrl->port); + hw->qpn = cpu_to_be32(ctrl->qpn); +} + +struct mlx4_net_trans_rule_hw_ib { + u8 size; + u8 rsvd1; + __be16 id; + u32 rsvd2; + __be32 qpn; + __be32 qpn_mask; + u8 dst_gid[16]; + u8 dst_gid_msk[16]; +} __packed; + +struct mlx4_net_trans_rule_hw_eth { + u8 size; + u8 rsvd; + __be16 id; + u8 rsvd1[6]; + u8 dst_mac[6]; + u16 rsvd2; + u8 dst_mac_msk[6]; + u16 rsvd3; + u8 src_mac[6]; + u16 rsvd4; + u8 src_mac_msk[6]; + u8 rsvd5; + u8 ether_type_enable; + __be16 ether_type; + __be16 vlan_id_msk; + __be16 vlan_id; +} __packed; + +struct mlx4_net_trans_rule_hw_tcp_udp { + u8 size; + u8 rsvd; + __be16 id; + __be16 rsvd1[3]; + __be16 dst_port; + __be16 rsvd2; + __be16 dst_port_msk; + __be16 rsvd3; + __be16 src_port; + __be16 rsvd4; + __be16 src_port_msk; +} __packed; + +struct mlx4_net_trans_rule_hw_ipv4 { + u8 size; + u8 rsvd; + __be16 id; + __be32 rsvd1; + __be32 dst_ip; + __be32 dst_ip_msk; + __be32 src_ip; + __be32 src_ip_msk; +} __packed; + +struct _rule_hw { + union { + struct { + u8 size; + u8 rsvd; + __be16 id; + }; + struct mlx4_net_trans_rule_hw_eth eth; + struct mlx4_net_trans_rule_hw_ib ib; + struct mlx4_net_trans_rule_hw_ipv4 ipv4; + struct mlx4_net_trans_rule_hw_tcp_udp tcp_udp; + }; +}; + +static int parse_trans_rule(struct mlx4_dev *dev, struct mlx4_spec_list *spec, + struct _rule_hw *rule_hw) +{ + static const u16 __sw_id_hw[] = { + [MLX4_NET_TRANS_RULE_ID_ETH] = 0xE001, + [MLX4_NET_TRANS_RULE_ID_IB] = 0xE005, + [MLX4_NET_TRANS_RULE_ID_IPV6] = 0xE003, + [MLX4_NET_TRANS_RULE_ID_IPV4] = 0xE002, + [MLX4_NET_TRANS_RULE_ID_TCP] = 0xE004, + [MLX4_NET_TRANS_RULE_ID_UDP] = 0xE006 + }; + + static const size_t __rule_hw_sz[] = { + [MLX4_NET_TRANS_RULE_ID_ETH] = + sizeof(struct mlx4_net_trans_rule_hw_eth), + [MLX4_NET_TRANS_RULE_ID_IB] = + sizeof(struct mlx4_net_trans_rule_hw_ib), + [MLX4_NET_TRANS_RULE_ID_IPV6] = 0, + [MLX4_NET_TRANS_RULE_ID_IPV4] = + sizeof(struct mlx4_net_trans_rule_hw_ipv4), + [MLX4_NET_TRANS_RULE_ID_TCP] = + sizeof(struct mlx4_net_trans_rule_hw_tcp_udp), + [MLX4_NET_TRANS_RULE_ID_UDP] = + sizeof(struct mlx4_net_trans_rule_hw_tcp_udp) + }; + if (spec->id > MLX4_NET_TRANS_RULE_NUM) { + mlx4_err(dev, "Invalid network rule id. id = %d\n", spec->id); + return -EINVAL; + } + memset(rule_hw, 0, __rule_hw_sz[spec->id]); + rule_hw->id = cpu_to_be16(__sw_id_hw[spec->id]); + rule_hw->size = __rule_hw_sz[spec->id] >> 2; + + switch (spec->id) { + case MLX4_NET_TRANS_RULE_ID_ETH: + memcpy(rule_hw->eth.dst_mac, spec->eth.dst_mac, ETH_ALEN); + memcpy(rule_hw->eth.dst_mac_msk, spec->eth.dst_mac_msk, + ETH_ALEN); + memcpy(rule_hw->eth.src_mac, spec->eth.src_mac, ETH_ALEN); + memcpy(rule_hw->eth.src_mac_msk, spec->eth.src_mac_msk, + ETH_ALEN); + if (spec->eth.ether_type_enable) { + rule_hw->eth.ether_type_enable = 1; + rule_hw->eth.ether_type = spec->eth.ether_type; + } + rule_hw->eth.vlan_id = spec->eth.vlan_id; + rule_hw->eth.vlan_id_msk = spec->eth.vlan_id_msk; + break; + + case MLX4_NET_TRANS_RULE_ID_IB: + rule_hw->ib.qpn = spec->ib.r_qpn; + rule_hw->ib.qpn_mask = spec->ib.qpn_msk; + memcpy(&rule_hw->ib.dst_gid, &spec->ib.dst_gid, 16); + memcpy(&rule_hw->ib.dst_gid_msk, &spec->ib.dst_gid_msk, 16); + break; + + case MLX4_NET_TRANS_RULE_ID_IPV6: + return -EOPNOTSUPP; + + case MLX4_NET_TRANS_RULE_ID_IPV4: + rule_hw->ipv4.src_ip = spec->ipv4.src_ip; + rule_hw->ipv4.src_ip_msk = spec->ipv4.src_ip_msk; + rule_hw->ipv4.dst_ip = spec->ipv4.dst_ip; + rule_hw->ipv4.dst_ip_msk = spec->ipv4.dst_ip_msk; + break; + + case MLX4_NET_TRANS_RULE_ID_TCP: + case MLX4_NET_TRANS_RULE_ID_UDP: + rule_hw->tcp_udp.dst_port = spec->tcp_udp.dst_port; + rule_hw->tcp_udp.dst_port_msk = spec->tcp_udp.dst_port_msk; + rule_hw->tcp_udp.src_port = spec->tcp_udp.src_port; + rule_hw->tcp_udp.src_port_msk = spec->tcp_udp.src_port_msk; + break; + + default: + return -EINVAL; + } + + return __rule_hw_sz[spec->id]; +} + +static void mlx4_err_rule(struct mlx4_dev *dev, char *str, + struct mlx4_net_trans_rule *rule) +{ +#define BUF_SIZE 256 + struct mlx4_spec_list *cur; + char buf[BUF_SIZE]; + int len = 0; + + mlx4_err(dev, "%s", str); + len += snprintf(buf + len, BUF_SIZE - len, + "port = %d prio = 0x%x qp = 0x%x ", + rule->port, rule->priority, rule->qpn); + + list_for_each_entry(cur, &rule->list, list) { + switch (cur->id) { + case MLX4_NET_TRANS_RULE_ID_ETH: + len += snprintf(buf + len, BUF_SIZE - len, + "dmac = %pM ", &cur->eth.dst_mac); + if (cur->eth.ether_type) + len += snprintf(buf + len, BUF_SIZE - len, + "ethertype = 0x%x ", + be16_to_cpu(cur->eth.ether_type)); + if (cur->eth.vlan_id) + len += snprintf(buf + len, BUF_SIZE - len, + "vlan-id = %d ", + be16_to_cpu(cur->eth.vlan_id)); + break; + + case MLX4_NET_TRANS_RULE_ID_IPV4: + if (cur->ipv4.src_ip) + len += snprintf(buf + len, BUF_SIZE - len, + "src-ip = %pI4 ", + &cur->ipv4.src_ip); + if (cur->ipv4.dst_ip) + len += snprintf(buf + len, BUF_SIZE - len, + "dst-ip = %pI4 ", + &cur->ipv4.dst_ip); + break; + + case MLX4_NET_TRANS_RULE_ID_TCP: + case MLX4_NET_TRANS_RULE_ID_UDP: + if (cur->tcp_udp.src_port) + len += snprintf(buf + len, BUF_SIZE - len, + "src-port = %d ", + be16_to_cpu(cur->tcp_udp.src_port)); + if (cur->tcp_udp.dst_port) + len += snprintf(buf + len, BUF_SIZE - len, + "dst-port = %d ", + be16_to_cpu(cur->tcp_udp.dst_port)); + break; + + case MLX4_NET_TRANS_RULE_ID_IB: + len += snprintf(buf + len, BUF_SIZE - len, + "dst-gid = %pI6\n", cur->ib.dst_gid); + len += snprintf(buf + len, BUF_SIZE - len, + "dst-gid-mask = %pI6\n", + cur->ib.dst_gid_msk); + break; + + case MLX4_NET_TRANS_RULE_ID_IPV6: + break; + + default: + break; + } + } + len += snprintf(buf + len, BUF_SIZE - len, "\n"); + mlx4_err(dev, "%s", buf); + + if (len >= BUF_SIZE) + mlx4_err(dev, "Network rule error message was truncated, print buffer is too small.\n"); +} + +int mlx4_flow_attach(struct mlx4_dev *dev, + struct mlx4_net_trans_rule *rule, u64 *reg_id) +{ + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_spec_list *cur; + u32 size = 0; + int ret; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + memset(mailbox->buf, 0, sizeof(struct mlx4_net_trans_rule_hw_ctrl)); + trans_rule_ctrl_to_hw(rule, mailbox->buf); + + size += sizeof(struct mlx4_net_trans_rule_hw_ctrl); + + list_for_each_entry(cur, &rule->list, list) { + ret = parse_trans_rule(dev, cur, mailbox->buf + size); + if (ret < 0) { + mlx4_free_cmd_mailbox(dev, mailbox); + return -EINVAL; + } + size += ret; + } + + ret = mlx4_QP_FLOW_STEERING_ATTACH(dev, mailbox, size >> 2, reg_id); + if (ret == -ENOMEM) + mlx4_err_rule(dev, + "mcg table is full. Fail to register network rule.\n", + rule); + else if (ret) + mlx4_err_rule(dev, "Fail to register network rule.\n", rule); + + mlx4_free_cmd_mailbox(dev, mailbox); + + return ret; +} +EXPORT_SYMBOL_GPL(mlx4_flow_attach); + +int mlx4_flow_detach(struct mlx4_dev *dev, u64 reg_id) +{ + int err; + + err = mlx4_QP_FLOW_STEERING_DETACH(dev, reg_id); + if (err) + mlx4_err(dev, "Fail to detach network rule. registration id = 0x%llx\n", + reg_id); + return err; +} +EXPORT_SYMBOL_GPL(mlx4_flow_detach); + int mlx4_qp_attach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], int block_mcast_loopback, enum mlx4_protocol prot, enum mlx4_steer_type steer) @@ -895,7 +1206,8 @@ static int mlx4_QP_ATTACH(struct mlx4_dev *dev, struct mlx4_qp *qp, } int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], - int block_mcast_loopback, enum mlx4_protocol prot) + u8 port, int block_mcast_loopback, + enum mlx4_protocol prot, u64 *reg_id) { switch (dev->caps.steering_mode) { @@ -914,6 +1226,42 @@ int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], block_mcast_loopback, prot, MLX4_MC_STEER); + case MLX4_STEERING_MODE_DEVICE_MANAGED: { + struct mlx4_spec_list spec = { {NULL} }; + __be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16); + + struct mlx4_net_trans_rule rule = { + .queue_mode = MLX4_NET_TRANS_Q_FIFO, + .exclusive = 0, + .promisc_mode = MLX4_FS_PROMISC_NONE, + .priority = MLX4_DOMAIN_NIC, + }; + + rule.allow_loopback = ~block_mcast_loopback; + rule.port = port; + rule.qpn = qp->qpn; + INIT_LIST_HEAD(&rule.list); + + switch (prot) { + case MLX4_PROT_ETH: + spec.id = MLX4_NET_TRANS_RULE_ID_ETH; + memcpy(spec.eth.dst_mac, &gid[10], ETH_ALEN); + memcpy(spec.eth.dst_mac_msk, &mac_mask, ETH_ALEN); + break; + + case MLX4_PROT_IB_IPV6: + spec.id = MLX4_NET_TRANS_RULE_ID_IB; + memcpy(spec.ib.dst_gid, gid, 16); + memset(&spec.ib.dst_gid_msk, 0xff, 16); + break; + default: + return -EINVAL; + } + list_add_tail(&spec.list, &rule.list); + + return mlx4_flow_attach(dev, &rule, reg_id); + } + default: return -EINVAL; } @@ -921,7 +1269,7 @@ int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], EXPORT_SYMBOL_GPL(mlx4_multicast_attach); int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], - enum mlx4_protocol prot) + enum mlx4_protocol prot, u64 reg_id) { switch (dev->caps.steering_mode) { case MLX4_STEERING_MODE_A0: @@ -938,6 +1286,9 @@ int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], return mlx4_qp_detach_common(dev, qp, gid, prot, MLX4_MC_STEER); + case MLX4_STEERING_MODE_DEVICE_MANAGED: + return mlx4_flow_detach(dev, reg_id); + default: return -EINVAL; } @@ -1042,6 +1393,10 @@ int mlx4_init_mcg_table(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); int err; + /* No need for mcg_table when fw managed the mcg table*/ + if (dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) + return 0; err = mlx4_bitmap_init(&priv->mcg_table.bitmap, dev->caps.num_amgms, dev->caps.num_amgms - 1, 0, 0); if (err) @@ -1054,5 +1409,7 @@ int mlx4_init_mcg_table(struct mlx4_dev *dev) void mlx4_cleanup_mcg_table(struct mlx4_dev *dev) { - mlx4_bitmap_cleanup(&mlx4_priv(dev)->mcg_table.bitmap); + if (dev->caps.steering_mode != + MLX4_STEERING_MODE_DEVICE_MANAGED) + mlx4_bitmap_cleanup(&mlx4_priv(dev)->mcg_table.bitmap); } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index c07e882e8369..0084967be19e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -54,6 +54,17 @@ #define DRV_VERSION "1.1" #define DRV_RELDATE "Dec, 2011" +#define MLX4_FS_UDP_UC_EN (1 << 1) +#define MLX4_FS_TCP_UC_EN (1 << 2) +#define MLX4_FS_NUM_OF_L2_ADDR 8 +#define MLX4_FS_MGM_LOG_ENTRY_SIZE 7 +#define MLX4_FS_NUM_MCG (1 << 17) + +enum { + MLX4_FS_L2_HASH = 0, + MLX4_FS_L2_L3_L4_HASH, +}; + #define MLX4_NUM_UP 8 #define MLX4_NUM_TC 8 #define MLX4_RATELIMIT_UNITS 3 /* 100 Mbps */ @@ -704,6 +715,7 @@ struct mlx4_set_port_rqp_calc_context { struct mlx4_mac_entry { u64 mac; + u64 reg_id; }; struct mlx4_port_info { @@ -777,6 +789,7 @@ struct mlx4_priv { struct mutex bf_mutex; struct io_mapping *bf_mapping; int reserved_mtts; + int fs_hash_mode; }; static inline struct mlx4_priv *mlx4_priv(struct mlx4_dev *dev) diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 1bb00cd22d42..2d6dabe7f55d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -414,6 +414,7 @@ struct mlx4_en_mc_list { struct list_head list; enum mlx4_en_mclist_act action; u8 addr[ETH_ALEN]; + u64 reg_id; }; struct mlx4_en_frag_info { @@ -503,6 +504,7 @@ struct mlx4_en_priv { u64 stats_bitmap; struct list_head mc_list; struct list_head curr_list; + u64 broadcast_id; struct mlx4_en_stat_out_mbox hw_stats; int vids[128]; bool wol; diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index 58de7237f57a..a51d1b9bf1d1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -75,21 +75,54 @@ void mlx4_init_vlan_table(struct mlx4_dev *dev, struct mlx4_vlan_table *table) table->total = 0; } -static int mlx4_uc_steer_add(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn) +static int mlx4_uc_steer_add(struct mlx4_dev *dev, u8 port, + u64 mac, int *qpn, u64 *reg_id) { - struct mlx4_qp qp; - u8 gid[16] = {0}; __be64 be_mac; int err; - qp.qpn = *qpn; - - mac &= 0xffffffffffffULL; + mac &= MLX4_MAC_MASK; be_mac = cpu_to_be64(mac << 16); - memcpy(&gid[10], &be_mac, ETH_ALEN); - gid[5] = port; - err = mlx4_unicast_attach(dev, &qp, gid, 0, MLX4_PROT_ETH); + switch (dev->caps.steering_mode) { + case MLX4_STEERING_MODE_B0: { + struct mlx4_qp qp; + u8 gid[16] = {0}; + + qp.qpn = *qpn; + memcpy(&gid[10], &be_mac, ETH_ALEN); + gid[5] = port; + + err = mlx4_unicast_attach(dev, &qp, gid, 0, MLX4_PROT_ETH); + break; + } + case MLX4_STEERING_MODE_DEVICE_MANAGED: { + struct mlx4_spec_list spec_eth = { {NULL} }; + __be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16); + + struct mlx4_net_trans_rule rule = { + .queue_mode = MLX4_NET_TRANS_Q_FIFO, + .exclusive = 0, + .allow_loopback = 1, + .promisc_mode = MLX4_FS_PROMISC_NONE, + .priority = MLX4_DOMAIN_NIC, + }; + + rule.port = port; + rule.qpn = *qpn; + INIT_LIST_HEAD(&rule.list); + + spec_eth.id = MLX4_NET_TRANS_RULE_ID_ETH; + memcpy(spec_eth.eth.dst_mac, &be_mac, ETH_ALEN); + memcpy(spec_eth.eth.dst_mac_msk, &mac_mask, ETH_ALEN); + list_add_tail(&spec_eth.list, &rule.list); + + err = mlx4_flow_attach(dev, &rule, reg_id); + break; + } + default: + return -EINVAL; + } if (err) mlx4_warn(dev, "Failed Attaching Unicast\n"); @@ -97,19 +130,30 @@ static int mlx4_uc_steer_add(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn) } static void mlx4_uc_steer_release(struct mlx4_dev *dev, u8 port, - u64 mac, int qpn) + u64 mac, int qpn, u64 reg_id) { - struct mlx4_qp qp; - u8 gid[16] = {0}; - __be64 be_mac; + switch (dev->caps.steering_mode) { + case MLX4_STEERING_MODE_B0: { + struct mlx4_qp qp; + u8 gid[16] = {0}; + __be64 be_mac; - qp.qpn = qpn; - mac &= 0xffffffffffffULL; - be_mac = cpu_to_be64(mac << 16); - memcpy(&gid[10], &be_mac, ETH_ALEN); - gid[5] = port; + qp.qpn = qpn; + mac &= MLX4_MAC_MASK; + be_mac = cpu_to_be64(mac << 16); + memcpy(&gid[10], &be_mac, ETH_ALEN); + gid[5] = port; - mlx4_unicast_detach(dev, &qp, gid, MLX4_PROT_ETH); + mlx4_unicast_detach(dev, &qp, gid, MLX4_PROT_ETH); + break; + } + case MLX4_STEERING_MODE_DEVICE_MANAGED: { + mlx4_flow_detach(dev, reg_id); + break; + } + default: + mlx4_err(dev, "Invalid steering mode.\n"); + } } static int validate_index(struct mlx4_dev *dev, @@ -144,6 +188,7 @@ int mlx4_get_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn) struct mlx4_mac_entry *entry; int index = 0; int err = 0; + u64 reg_id; mlx4_dbg(dev, "Registering MAC: 0x%llx for adding\n", (unsigned long long) mac); @@ -167,7 +212,7 @@ int mlx4_get_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn) goto qp_err; } - err = mlx4_uc_steer_add(dev, port, mac, qpn); + err = mlx4_uc_steer_add(dev, port, mac, qpn, ®_id); if (err) goto steer_err; @@ -177,6 +222,7 @@ int mlx4_get_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn) goto alloc_err; } entry->mac = mac; + entry->reg_id = reg_id; err = radix_tree_insert(&info->mac_tree, *qpn, entry); if (err) goto insert_err; @@ -186,7 +232,7 @@ insert_err: kfree(entry); alloc_err: - mlx4_uc_steer_release(dev, port, mac, *qpn); + mlx4_uc_steer_release(dev, port, mac, *qpn, reg_id); steer_err: mlx4_qp_release_range(dev, *qpn, 1); @@ -212,7 +258,8 @@ void mlx4_put_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int qpn) mlx4_dbg(dev, "Releasing qp: port %d, mac 0x%llx," " qpn %d\n", port, (unsigned long long) mac, qpn); - mlx4_uc_steer_release(dev, port, entry->mac, qpn); + mlx4_uc_steer_release(dev, port, entry->mac, + qpn, entry->reg_id); mlx4_qp_release_range(dev, qpn, 1); radix_tree_delete(&info->mac_tree, qpn); kfree(entry); @@ -363,11 +410,14 @@ int mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac) entry = radix_tree_lookup(&info->mac_tree, qpn); if (!entry) return -EINVAL; - mlx4_uc_steer_release(dev, port, entry->mac, qpn); + mlx4_uc_steer_release(dev, port, entry->mac, + qpn, entry->reg_id); mlx4_unregister_mac(dev, port, entry->mac); entry->mac = new_mac; + entry->reg_id = 0; mlx4_register_mac(dev, port, new_mac); - err = mlx4_uc_steer_add(dev, port, entry->mac, &qpn); + err = mlx4_uc_steer_add(dev, port, entry->mac, + &qpn, &entry->reg_id); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx4/profile.c b/drivers/net/ethernet/mellanox/mlx4/profile.c index b83bc928d52a..9ee4725363d5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/profile.c +++ b/drivers/net/ethernet/mellanox/mlx4/profile.c @@ -237,13 +237,19 @@ u64 mlx4_make_profile(struct mlx4_dev *dev, init_hca->mtt_base = profile[i].start; break; case MLX4_RES_MCG: - dev->caps.num_mgms = profile[i].num >> 1; - dev->caps.num_amgms = profile[i].num >> 1; init_hca->mc_base = profile[i].start; init_hca->log_mc_entry_sz = ilog2(mlx4_get_mgm_entry_size(dev)); init_hca->log_mc_table_sz = profile[i].log_num; - init_hca->log_mc_hash_sz = profile[i].log_num - 1; + if (dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) { + dev->caps.num_mgms = profile[i].num; + } else { + init_hca->log_mc_hash_sz = + profile[i].log_num - 1; + dev->caps.num_mgms = profile[i].num >> 1; + dev->caps.num_amgms = profile[i].num >> 1; + } break; default: break; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index a8ca960f4620..5a6f3555d806 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -2744,6 +2744,9 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_cmd_mailbox *outbox, struct mlx4_cmd_info *cmd) { + if (dev->caps.steering_mode != + MLX4_STEERING_MODE_DEVICE_MANAGED) + return -EOPNOTSUPP; return mlx4_cmd_imm(dev, inbox->dma, &vhcr->out_param, vhcr->in_modifier, 0, MLX4_QP_FLOW_STEERING_ATTACH, @@ -2757,6 +2760,9 @@ int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_cmd_mailbox *outbox, struct mlx4_cmd_info *cmd) { + if (dev->caps.steering_mode != + MLX4_STEERING_MODE_DEVICE_MANAGED) + return -EOPNOTSUPP; return mlx4_cmd(dev, vhcr->in_param, 0, 0, MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 7f5c9ee42f96..e45fc20bd01f 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -70,14 +70,17 @@ enum { MLX4_MFUNC_EQE_MASK = (MLX4_MFUNC_MAX_EQES - 1) }; -/* Driver supports 2 diffrent device methods to manage traffic steering: +/* Driver supports 3 diffrent device methods to manage traffic steering: + * -device managed - High level API for ib and eth flow steering. FW is + * managing flow steering tables. * - B0 steering mode - Common low level API for ib and (if supported) eth. * - A0 steering mode - Limited low level API for eth. In case of IB, * B0 mode is in use. */ enum { MLX4_STEERING_MODE_A0, - MLX4_STEERING_MODE_B0 + MLX4_STEERING_MODE_B0, + MLX4_STEERING_MODE_DEVICE_MANAGED }; static inline const char *mlx4_steering_mode_str(int steering_mode) @@ -88,6 +91,10 @@ static inline const char *mlx4_steering_mode_str(int steering_mode) case MLX4_STEERING_MODE_B0: return "B0 steering"; + + case MLX4_STEERING_MODE_DEVICE_MANAGED: + return "Device managed flow steering"; + default: return "Unrecognize steering mode"; } @@ -125,7 +132,8 @@ enum { enum { MLX4_DEV_CAP_FLAG2_RSS = 1LL << 0, MLX4_DEV_CAP_FLAG2_RSS_TOP = 1LL << 1, - MLX4_DEV_CAP_FLAG2_RSS_XOR = 1LL << 2 + MLX4_DEV_CAP_FLAG2_RSS_XOR = 1LL << 2, + MLX4_DEV_CAP_FLAG2_FS_EN = 1LL << 3 }; #define MLX4_ATTR_EXTENDED_PORT_INFO cpu_to_be16(0xff90) @@ -319,6 +327,7 @@ struct mlx4_caps { int reserved_mcgs; int num_qp_per_mgm; int steering_mode; + int fs_log_max_ucast_qp_range_size; int num_pds; int reserved_pds; int max_xrcds; @@ -647,9 +656,94 @@ int mlx4_unicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], int mlx4_unicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], enum mlx4_protocol prot); int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], - int block_mcast_loopback, enum mlx4_protocol protocol); + u8 port, int block_mcast_loopback, + enum mlx4_protocol protocol, u64 *reg_id); int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], - enum mlx4_protocol protocol); + enum mlx4_protocol protocol, u64 reg_id); + +enum { + MLX4_DOMAIN_UVERBS = 0x1000, + MLX4_DOMAIN_ETHTOOL = 0x2000, + MLX4_DOMAIN_RFS = 0x3000, + MLX4_DOMAIN_NIC = 0x5000, +}; + +enum mlx4_net_trans_rule_id { + MLX4_NET_TRANS_RULE_ID_ETH = 0, + MLX4_NET_TRANS_RULE_ID_IB, + MLX4_NET_TRANS_RULE_ID_IPV6, + MLX4_NET_TRANS_RULE_ID_IPV4, + MLX4_NET_TRANS_RULE_ID_TCP, + MLX4_NET_TRANS_RULE_ID_UDP, + MLX4_NET_TRANS_RULE_NUM, /* should be last */ +}; + +enum mlx4_net_trans_promisc_mode { + MLX4_FS_PROMISC_NONE = 0, + MLX4_FS_PROMISC_UPLINK, + MLX4_FS_PROMISC_FUNCTION_PORT, + MLX4_FS_PROMISC_ALL_MULTI, +}; + +struct mlx4_spec_eth { + u8 dst_mac[6]; + u8 dst_mac_msk[6]; + u8 src_mac[6]; + u8 src_mac_msk[6]; + u8 ether_type_enable; + __be16 ether_type; + __be16 vlan_id_msk; + __be16 vlan_id; +}; + +struct mlx4_spec_tcp_udp { + __be16 dst_port; + __be16 dst_port_msk; + __be16 src_port; + __be16 src_port_msk; +}; + +struct mlx4_spec_ipv4 { + __be32 dst_ip; + __be32 dst_ip_msk; + __be32 src_ip; + __be32 src_ip_msk; +}; + +struct mlx4_spec_ib { + __be32 r_qpn; + __be32 qpn_msk; + u8 dst_gid[16]; + u8 dst_gid_msk[16]; +}; + +struct mlx4_spec_list { + struct list_head list; + enum mlx4_net_trans_rule_id id; + union { + struct mlx4_spec_eth eth; + struct mlx4_spec_ib ib; + struct mlx4_spec_ipv4 ipv4; + struct mlx4_spec_tcp_udp tcp_udp; + }; +}; + +enum mlx4_net_trans_hw_rule_queue { + MLX4_NET_TRANS_Q_FIFO, + MLX4_NET_TRANS_Q_LIFO, +}; + +struct mlx4_net_trans_rule { + struct list_head list; + enum mlx4_net_trans_hw_rule_queue queue_mode; + bool exclusive; + bool allow_loopback; + enum mlx4_net_trans_promisc_mode promisc_mode; + u8 port; + u16 priority; + u32 qpn; +}; + int mlx4_multicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port); int mlx4_multicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port); int mlx4_unicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port); @@ -692,4 +786,8 @@ int mlx4_wol_write(struct mlx4_dev *dev, u64 config, int port); int mlx4_counter_alloc(struct mlx4_dev *dev, u32 *idx); void mlx4_counter_free(struct mlx4_dev *dev, u32 idx); +int mlx4_flow_attach(struct mlx4_dev *dev, + struct mlx4_net_trans_rule *rule, u64 *reg_id); +int mlx4_flow_detach(struct mlx4_dev *dev, u64 reg_id); + #endif /* MLX4_DEVICE_H */ -- cgit v1.2.3-59-g8ed1b From 592e49dda8122ab621cdc59cc429bdb968ee6364 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 5 Jul 2012 04:03:48 +0000 Subject: net/mlx4: Implement promiscuous mode with device managed flow-steering The device managed flow steering API has three promiscuous modes: 1. Uplink - captures all the packets that arrive to the port. 2. Allmulti - captures all multicast packets arriving to the port. 3. Function port - for future use, this mode is not implemented yet. Use these modes with the flow_attach and flow_detach firmware commands according to the promiscuous state of the netdevice. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 41 ++++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/mcg.c | 60 ++++++++++++++++++++++++++ include/linux/mlx4/device.h | 7 +++ 3 files changed, 108 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index eb5ed8e39873..b7945a80ad15 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -301,6 +301,16 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) /* Enable promiscouos mode */ switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_DEVICE_MANAGED: + err = mlx4_flow_steer_promisc_add(mdev->dev, + priv->port, + priv->base_qpn, + MLX4_FS_PROMISC_UPLINK); + if (err) + en_err(priv, "Failed enabling promiscuous mode\n"); + priv->flags |= MLX4_EN_FLAG_MC_PROMISC; + break; + case MLX4_STEERING_MODE_B0: err = mlx4_unicast_promisc_add(mdev->dev, priv->base_qpn, @@ -357,6 +367,15 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) /* Disable promiscouos mode */ switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_DEVICE_MANAGED: + err = mlx4_flow_steer_promisc_remove(mdev->dev, + priv->port, + MLX4_FS_PROMISC_UPLINK); + if (err) + en_err(priv, "Failed disabling promiscuous mode\n"); + priv->flags &= ~MLX4_EN_FLAG_MC_PROMISC; + break; + case MLX4_STEERING_MODE_B0: err = mlx4_unicast_promisc_remove(mdev->dev, priv->base_qpn, @@ -399,6 +418,13 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) /* Add the default qp number as multicast promisc */ if (!(priv->flags & MLX4_EN_FLAG_MC_PROMISC)) { switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_DEVICE_MANAGED: + err = mlx4_flow_steer_promisc_add(mdev->dev, + priv->port, + priv->base_qpn, + MLX4_FS_PROMISC_ALL_MULTI); + break; + case MLX4_STEERING_MODE_B0: err = mlx4_multicast_promisc_add(mdev->dev, priv->base_qpn, @@ -416,6 +442,12 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) /* Disable Multicast promisc */ if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) { switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_DEVICE_MANAGED: + err = mlx4_flow_steer_promisc_remove(mdev->dev, + priv->port, + MLX4_FS_PROMISC_ALL_MULTI); + break; + case MLX4_STEERING_MODE_B0: err = mlx4_multicast_promisc_remove(mdev->dev, priv->base_qpn, @@ -839,6 +871,15 @@ int mlx4_en_start_port(struct net_device *dev) /* Must redo promiscuous mode setup. */ priv->flags &= ~(MLX4_EN_FLAG_PROMISC | MLX4_EN_FLAG_MC_PROMISC); + if (mdev->dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) { + mlx4_flow_steer_promisc_remove(mdev->dev, + priv->port, + MLX4_FS_PROMISC_UPLINK); + mlx4_flow_steer_promisc_remove(mdev->dev, + priv->port, + MLX4_FS_PROMISC_ALL_MULTI); + } /* Schedule multicast task to populate multicast list */ queue_work(mdev->workqueue, &priv->mcast_task); diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index 768a2a4530e8..bc62f536ffae 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -1295,6 +1295,66 @@ int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], } EXPORT_SYMBOL_GPL(mlx4_multicast_detach); +int mlx4_flow_steer_promisc_add(struct mlx4_dev *dev, u8 port, + u32 qpn, enum mlx4_net_trans_promisc_mode mode) +{ + struct mlx4_net_trans_rule rule; + u64 *regid_p; + + switch (mode) { + case MLX4_FS_PROMISC_UPLINK: + case MLX4_FS_PROMISC_FUNCTION_PORT: + regid_p = &dev->regid_promisc_array[port]; + break; + case MLX4_FS_PROMISC_ALL_MULTI: + regid_p = &dev->regid_allmulti_array[port]; + break; + default: + return -1; + } + + if (*regid_p != 0) + return -1; + + rule.promisc_mode = mode; + rule.port = port; + rule.qpn = qpn; + INIT_LIST_HEAD(&rule.list); + mlx4_err(dev, "going promisc on %x\n", port); + + return mlx4_flow_attach(dev, &rule, regid_p); +} +EXPORT_SYMBOL_GPL(mlx4_flow_steer_promisc_add); + +int mlx4_flow_steer_promisc_remove(struct mlx4_dev *dev, u8 port, + enum mlx4_net_trans_promisc_mode mode) +{ + int ret; + u64 *regid_p; + + switch (mode) { + case MLX4_FS_PROMISC_UPLINK: + case MLX4_FS_PROMISC_FUNCTION_PORT: + regid_p = &dev->regid_promisc_array[port]; + break; + case MLX4_FS_PROMISC_ALL_MULTI: + regid_p = &dev->regid_allmulti_array[port]; + break; + default: + return -1; + } + + if (*regid_p == 0) + return -1; + + ret = mlx4_flow_detach(dev, *regid_p); + if (ret == 0) + *regid_p = 0; + + return ret; +} +EXPORT_SYMBOL_GPL(mlx4_flow_steer_promisc_remove); + int mlx4_unicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], int block_mcast_loopback, enum mlx4_protocol prot) diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index e45fc20bd01f..6f0d133cc7ad 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -542,6 +542,8 @@ struct mlx4_dev { u8 rev_id; char board_id[MLX4_BOARD_ID_LEN]; int num_vfs; + u64 regid_promisc_array[MLX4_MAX_PORTS + 1]; + u64 regid_allmulti_array[MLX4_MAX_PORTS + 1]; }; struct mlx4_init_port_param { @@ -681,6 +683,7 @@ enum mlx4_net_trans_rule_id { enum mlx4_net_trans_promisc_mode { MLX4_FS_PROMISC_NONE = 0, MLX4_FS_PROMISC_UPLINK, + /* For future use. Not implemented yet */ MLX4_FS_PROMISC_FUNCTION_PORT, MLX4_FS_PROMISC_ALL_MULTI, }; @@ -744,6 +747,10 @@ struct mlx4_net_trans_rule { u32 qpn; }; +int mlx4_flow_steer_promisc_add(struct mlx4_dev *dev, u8 port, u32 qpn, + enum mlx4_net_trans_promisc_mode mode); +int mlx4_flow_steer_promisc_remove(struct mlx4_dev *dev, u8 port, + enum mlx4_net_trans_promisc_mode mode); int mlx4_multicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port); int mlx4_multicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port); int mlx4_unicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port); -- cgit v1.2.3-59-g8ed1b From 752a50cab600c6d46c5a1921c6a6d2fb116c8a4b Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Tue, 19 Jun 2012 11:21:33 +0300 Subject: mlx4_core: Pass an invalid PCI id number to VFs Currently, VFs have 0 in their dev->caps.function field. This is a valid pci id (usually of the PF). Instead, pass an invalid PCI id to the VF via QUERY_FW, so that if the value gets accessed in the VF driver, we'll catch the problem. Signed-off-by: Jack Morgenstein Signed-off-by: Roland Dreier --- drivers/net/ethernet/mellanox/mlx4/fw.c | 10 +++++++--- include/linux/mlx4/device.h | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 9c83bb8151ea..4281ce09add8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -881,11 +881,12 @@ int mlx4_QUERY_FW(struct mlx4_dev *dev) ((fw_ver & 0xffff0000ull) >> 16) | ((fw_ver & 0x0000ffffull) << 16); + MLX4_GET(lg, outbox, QUERY_FW_PPF_ID); + dev->caps.function = lg; + if (mlx4_is_slave(dev)) goto out; - MLX4_GET(lg, outbox, QUERY_FW_PPF_ID); - dev->caps.function = lg; MLX4_GET(cmd_if_rev, outbox, QUERY_FW_CMD_IF_REV_OFFSET); if (cmd_if_rev < MLX4_COMMAND_INTERFACE_MIN_REV || @@ -966,9 +967,12 @@ int mlx4_QUERY_FW_wrapper(struct mlx4_dev *dev, int slave, if (err) return err; - /* for slaves, zero out everything except FW version */ + /* for slaves, set pci PPF ID to invalid and zero out everything + * else except FW version */ outbuf[0] = outbuf[1] = 0; memset(&outbuf[8], 0, QUERY_FW_OUT_SIZE - 8); + outbuf[QUERY_FW_PPF_ID] = MLX4_INVALID_SLAVE_ID; + return 0; } diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 6a8f002b8ed3..8eadf0f14cc5 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -534,6 +534,8 @@ struct mlx4_init_port_param { if (((dev)->caps.port_mask[port] == MLX4_PORT_TYPE_IB) || \ ((dev)->caps.flags & MLX4_DEV_CAP_FLAG_IBOE)) +#define MLX4_INVALID_SLAVE_ID 0xFF + static inline int mlx4_is_master(struct mlx4_dev *dev) { return dev->flags & MLX4_FLAG_MASTER; -- cgit v1.2.3-59-g8ed1b From d5bf9071e71a4db85a0eea6236ef94a29fc3eec9 Mon Sep 17 00:00:00 2001 From: Christian Hohnstaedt Date: Wed, 4 Jul 2012 05:44:34 +0000 Subject: phylib: Support registering a bunch of drivers If registering of one of them fails, all already registered drivers of this module will be unregistered. Use the new register/unregister functions in all drivers registering more than one driver. amd.c, realtek.c: Simplify: directly return registration result. Tested with broadcom.c All others compile-tested. Signed-off-by: Christian Hohnstaedt Signed-off-by: David S. Miller --- drivers/net/phy/amd.c | 8 +-- drivers/net/phy/bcm63xx.c | 31 ++++------- drivers/net/phy/bcm87xx.c | 24 +++------ drivers/net/phy/broadcom.c | 119 +++++++------------------------------------ drivers/net/phy/cicada.c | 35 ++++--------- drivers/net/phy/davicom.c | 41 ++++----------- drivers/net/phy/icplus.c | 31 ++++------- drivers/net/phy/lxt.c | 47 +++++------------ drivers/net/phy/marvell.c | 22 ++------ drivers/net/phy/micrel.c | 62 ++++------------------ drivers/net/phy/phy_device.c | 25 +++++++++ drivers/net/phy/realtek.c | 6 +-- drivers/net/phy/smsc.c | 64 ++++------------------- drivers/net/phy/ste10Xp.c | 21 +++----- drivers/net/phy/vitesse.c | 52 ++++++++----------- include/linux/phy.h | 2 + 16 files changed, 159 insertions(+), 431 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/phy/amd.c b/drivers/net/phy/amd.c index cfabd5fe5372..a3fb5ceb6487 100644 --- a/drivers/net/phy/amd.c +++ b/drivers/net/phy/amd.c @@ -77,13 +77,7 @@ static struct phy_driver am79c_driver = { static int __init am79c_init(void) { - int ret; - - ret = phy_driver_register(&am79c_driver); - if (ret) - return ret; - - return 0; + return phy_driver_register(&am79c_driver); } static void __exit am79c_exit(void) diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c index cd802eb25fd2..84c7a39b1c65 100644 --- a/drivers/net/phy/bcm63xx.c +++ b/drivers/net/phy/bcm63xx.c @@ -71,7 +71,8 @@ static int bcm63xx_config_intr(struct phy_device *phydev) return err; } -static struct phy_driver bcm63xx_1_driver = { +static struct phy_driver bcm63xx_driver[] = { +{ .phy_id = 0x00406000, .phy_id_mask = 0xfffffc00, .name = "Broadcom BCM63XX (1)", @@ -84,10 +85,8 @@ static struct phy_driver bcm63xx_1_driver = { .ack_interrupt = bcm63xx_ack_interrupt, .config_intr = bcm63xx_config_intr, .driver = { .owner = THIS_MODULE }, -}; - -/* same phy as above, with just a different OUI */ -static struct phy_driver bcm63xx_2_driver = { +}, { + /* same phy as above, with just a different OUI */ .phy_id = 0x002bdc00, .phy_id_mask = 0xfffffc00, .name = "Broadcom BCM63XX (2)", @@ -99,30 +98,18 @@ static struct phy_driver bcm63xx_2_driver = { .ack_interrupt = bcm63xx_ack_interrupt, .config_intr = bcm63xx_config_intr, .driver = { .owner = THIS_MODULE }, -}; +} }; static int __init bcm63xx_phy_init(void) { - int ret; - - ret = phy_driver_register(&bcm63xx_1_driver); - if (ret) - goto out_63xx_1; - ret = phy_driver_register(&bcm63xx_2_driver); - if (ret) - goto out_63xx_2; - return ret; - -out_63xx_2: - phy_driver_unregister(&bcm63xx_1_driver); -out_63xx_1: - return ret; + return phy_drivers_register(bcm63xx_driver, + ARRAY_SIZE(bcm63xx_driver)); } static void __exit bcm63xx_phy_exit(void) { - phy_driver_unregister(&bcm63xx_1_driver); - phy_driver_unregister(&bcm63xx_2_driver); + phy_drivers_unregister(bcm63xx_driver, + ARRAY_SIZE(bcm63xx_driver)); } module_init(bcm63xx_phy_init); diff --git a/drivers/net/phy/bcm87xx.c b/drivers/net/phy/bcm87xx.c index 9a90dcf31156..2167ce51818e 100644 --- a/drivers/net/phy/bcm87xx.c +++ b/drivers/net/phy/bcm87xx.c @@ -187,7 +187,8 @@ static int bcm8727_match_phy_device(struct phy_device *phydev) return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8727; } -static struct phy_driver bcm8706_driver = { +static struct phy_driver bcm87xx_driver[] = { +{ .phy_id = PHY_ID_BCM8706, .phy_id_mask = 0xffffffff, .name = "Broadcom BCM8706", @@ -200,9 +201,7 @@ static struct phy_driver bcm8706_driver = { .did_interrupt = bcm87xx_did_interrupt, .match_phy_device = bcm8706_match_phy_device, .driver = { .owner = THIS_MODULE }, -}; - -static struct phy_driver bcm8727_driver = { +}, { .phy_id = PHY_ID_BCM8727, .phy_id_mask = 0xffffffff, .name = "Broadcom BCM8727", @@ -215,25 +214,18 @@ static struct phy_driver bcm8727_driver = { .did_interrupt = bcm87xx_did_interrupt, .match_phy_device = bcm8727_match_phy_device, .driver = { .owner = THIS_MODULE }, -}; +} }; static int __init bcm87xx_init(void) { - int ret; - - ret = phy_driver_register(&bcm8706_driver); - if (ret) - goto err; - - ret = phy_driver_register(&bcm8727_driver); -err: - return ret; + return phy_drivers_register(bcm87xx_driver, + ARRAY_SIZE(bcm87xx_driver)); } module_init(bcm87xx_init); static void __exit bcm87xx_exit(void) { - phy_driver_unregister(&bcm8706_driver); - phy_driver_unregister(&bcm8727_driver); + phy_drivers_unregister(bcm87xx_driver, + ARRAY_SIZE(bcm87xx_driver)); } module_exit(bcm87xx_exit); diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 60338ff63092..f8c90ea75108 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -682,7 +682,8 @@ static int brcm_fet_config_intr(struct phy_device *phydev) return err; } -static struct phy_driver bcm5411_driver = { +static struct phy_driver broadcom_drivers[] = { +{ .phy_id = PHY_ID_BCM5411, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5411", @@ -695,9 +696,7 @@ static struct phy_driver bcm5411_driver = { .ack_interrupt = bcm54xx_ack_interrupt, .config_intr = bcm54xx_config_intr, .driver = { .owner = THIS_MODULE }, -}; - -static struct phy_driver bcm5421_driver = { +}, { .phy_id = PHY_ID_BCM5421, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5421", @@ -710,9 +709,7 @@ static struct phy_driver bcm5421_driver = { .ack_interrupt = bcm54xx_ack_interrupt, .config_intr = bcm54xx_config_intr, .driver = { .owner = THIS_MODULE }, -}; - -static struct phy_driver bcm5461_driver = { +}, { .phy_id = PHY_ID_BCM5461, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5461", @@ -725,9 +722,7 @@ static struct phy_driver bcm5461_driver = { .ack_interrupt = bcm54xx_ack_interrupt, .config_intr = bcm54xx_config_intr, .driver = { .owner = THIS_MODULE }, -}; - -static struct phy_driver bcm5464_driver = { +}, { .phy_id = PHY_ID_BCM5464, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5464", @@ -740,9 +735,7 @@ static struct phy_driver bcm5464_driver = { .ack_interrupt = bcm54xx_ack_interrupt, .config_intr = bcm54xx_config_intr, .driver = { .owner = THIS_MODULE }, -}; - -static struct phy_driver bcm5481_driver = { +}, { .phy_id = PHY_ID_BCM5481, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5481", @@ -755,9 +748,7 @@ static struct phy_driver bcm5481_driver = { .ack_interrupt = bcm54xx_ack_interrupt, .config_intr = bcm54xx_config_intr, .driver = { .owner = THIS_MODULE }, -}; - -static struct phy_driver bcm5482_driver = { +}, { .phy_id = PHY_ID_BCM5482, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5482", @@ -770,9 +761,7 @@ static struct phy_driver bcm5482_driver = { .ack_interrupt = bcm54xx_ack_interrupt, .config_intr = bcm54xx_config_intr, .driver = { .owner = THIS_MODULE }, -}; - -static struct phy_driver bcm50610_driver = { +}, { .phy_id = PHY_ID_BCM50610, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM50610", @@ -785,9 +774,7 @@ static struct phy_driver bcm50610_driver = { .ack_interrupt = bcm54xx_ack_interrupt, .config_intr = bcm54xx_config_intr, .driver = { .owner = THIS_MODULE }, -}; - -static struct phy_driver bcm50610m_driver = { +}, { .phy_id = PHY_ID_BCM50610M, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM50610M", @@ -800,9 +787,7 @@ static struct phy_driver bcm50610m_driver = { .ack_interrupt = bcm54xx_ack_interrupt, .config_intr = bcm54xx_config_intr, .driver = { .owner = THIS_MODULE }, -}; - -static struct phy_driver bcm57780_driver = { +}, { .phy_id = PHY_ID_BCM57780, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM57780", @@ -815,9 +800,7 @@ static struct phy_driver bcm57780_driver = { .ack_interrupt = bcm54xx_ack_interrupt, .config_intr = bcm54xx_config_intr, .driver = { .owner = THIS_MODULE }, -}; - -static struct phy_driver bcmac131_driver = { +}, { .phy_id = PHY_ID_BCMAC131, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCMAC131", @@ -830,9 +813,7 @@ static struct phy_driver bcmac131_driver = { .ack_interrupt = brcm_fet_ack_interrupt, .config_intr = brcm_fet_config_intr, .driver = { .owner = THIS_MODULE }, -}; - -static struct phy_driver bcm5241_driver = { +}, { .phy_id = PHY_ID_BCM5241, .phy_id_mask = 0xfffffff0, .name = "Broadcom BCM5241", @@ -845,84 +826,18 @@ static struct phy_driver bcm5241_driver = { .ack_interrupt = brcm_fet_ack_interrupt, .config_intr = brcm_fet_config_intr, .driver = { .owner = THIS_MODULE }, -}; +} }; static int __init broadcom_init(void) { - int ret; - - ret = phy_driver_register(&bcm5411_driver); - if (ret) - goto out_5411; - ret = phy_driver_register(&bcm5421_driver); - if (ret) - goto out_5421; - ret = phy_driver_register(&bcm5461_driver); - if (ret) - goto out_5461; - ret = phy_driver_register(&bcm5464_driver); - if (ret) - goto out_5464; - ret = phy_driver_register(&bcm5481_driver); - if (ret) - goto out_5481; - ret = phy_driver_register(&bcm5482_driver); - if (ret) - goto out_5482; - ret = phy_driver_register(&bcm50610_driver); - if (ret) - goto out_50610; - ret = phy_driver_register(&bcm50610m_driver); - if (ret) - goto out_50610m; - ret = phy_driver_register(&bcm57780_driver); - if (ret) - goto out_57780; - ret = phy_driver_register(&bcmac131_driver); - if (ret) - goto out_ac131; - ret = phy_driver_register(&bcm5241_driver); - if (ret) - goto out_5241; - return ret; - -out_5241: - phy_driver_unregister(&bcmac131_driver); -out_ac131: - phy_driver_unregister(&bcm57780_driver); -out_57780: - phy_driver_unregister(&bcm50610m_driver); -out_50610m: - phy_driver_unregister(&bcm50610_driver); -out_50610: - phy_driver_unregister(&bcm5482_driver); -out_5482: - phy_driver_unregister(&bcm5481_driver); -out_5481: - phy_driver_unregister(&bcm5464_driver); -out_5464: - phy_driver_unregister(&bcm5461_driver); -out_5461: - phy_driver_unregister(&bcm5421_driver); -out_5421: - phy_driver_unregister(&bcm5411_driver); -out_5411: - return ret; + return phy_drivers_register(broadcom_drivers, + ARRAY_SIZE(broadcom_drivers)); } static void __exit broadcom_exit(void) { - phy_driver_unregister(&bcm5241_driver); - phy_driver_unregister(&bcmac131_driver); - phy_driver_unregister(&bcm57780_driver); - phy_driver_unregister(&bcm50610m_driver); - phy_driver_unregister(&bcm50610_driver); - phy_driver_unregister(&bcm5482_driver); - phy_driver_unregister(&bcm5481_driver); - phy_driver_unregister(&bcm5464_driver); - phy_driver_unregister(&bcm5461_driver); - phy_driver_unregister(&bcm5421_driver); - phy_driver_unregister(&bcm5411_driver); + phy_drivers_unregister(broadcom_drivers, + ARRAY_SIZE(broadcom_drivers)); } module_init(broadcom_init); diff --git a/drivers/net/phy/cicada.c b/drivers/net/phy/cicada.c index d28173161c21..db472ffb6e89 100644 --- a/drivers/net/phy/cicada.c +++ b/drivers/net/phy/cicada.c @@ -102,7 +102,8 @@ static int cis820x_config_intr(struct phy_device *phydev) } /* Cicada 8201, a.k.a Vitesse VSC8201 */ -static struct phy_driver cis8201_driver = { +static struct phy_driver cis820x_driver[] = { +{ .phy_id = 0x000fc410, .name = "Cicada Cis8201", .phy_id_mask = 0x000ffff0, @@ -113,11 +114,8 @@ static struct phy_driver cis8201_driver = { .read_status = &genphy_read_status, .ack_interrupt = &cis820x_ack_interrupt, .config_intr = &cis820x_config_intr, - .driver = { .owner = THIS_MODULE,}, -}; - -/* Cicada 8204 */ -static struct phy_driver cis8204_driver = { + .driver = { .owner = THIS_MODULE,}, +}, { .phy_id = 0x000fc440, .name = "Cicada Cis8204", .phy_id_mask = 0x000fffc0, @@ -128,32 +126,19 @@ static struct phy_driver cis8204_driver = { .read_status = &genphy_read_status, .ack_interrupt = &cis820x_ack_interrupt, .config_intr = &cis820x_config_intr, - .driver = { .owner = THIS_MODULE,}, -}; + .driver = { .owner = THIS_MODULE,}, +} }; static int __init cicada_init(void) { - int ret; - - ret = phy_driver_register(&cis8204_driver); - if (ret) - goto err1; - - ret = phy_driver_register(&cis8201_driver); - if (ret) - goto err2; - return 0; - -err2: - phy_driver_unregister(&cis8204_driver); -err1: - return ret; + return phy_drivers_register(cis820x_driver, + ARRAY_SIZE(cis820x_driver)); } static void __exit cicada_exit(void) { - phy_driver_unregister(&cis8204_driver); - phy_driver_unregister(&cis8201_driver); + phy_drivers_unregister(cis820x_driver, + ARRAY_SIZE(cis820x_driver)); } module_init(cicada_init); diff --git a/drivers/net/phy/davicom.c b/drivers/net/phy/davicom.c index 5f59cc064778..81c7bc010dd8 100644 --- a/drivers/net/phy/davicom.c +++ b/drivers/net/phy/davicom.c @@ -144,7 +144,8 @@ static int dm9161_ack_interrupt(struct phy_device *phydev) return (err < 0) ? err : 0; } -static struct phy_driver dm9161e_driver = { +static struct phy_driver dm91xx_driver[] = { +{ .phy_id = 0x0181b880, .name = "Davicom DM9161E", .phy_id_mask = 0x0ffffff0, @@ -153,9 +154,7 @@ static struct phy_driver dm9161e_driver = { .config_aneg = dm9161_config_aneg, .read_status = genphy_read_status, .driver = { .owner = THIS_MODULE,}, -}; - -static struct phy_driver dm9161a_driver = { +}, { .phy_id = 0x0181b8a0, .name = "Davicom DM9161A", .phy_id_mask = 0x0ffffff0, @@ -164,9 +163,7 @@ static struct phy_driver dm9161a_driver = { .config_aneg = dm9161_config_aneg, .read_status = genphy_read_status, .driver = { .owner = THIS_MODULE,}, -}; - -static struct phy_driver dm9131_driver = { +}, { .phy_id = 0x00181b80, .name = "Davicom DM9131", .phy_id_mask = 0x0ffffff0, @@ -177,38 +174,18 @@ static struct phy_driver dm9131_driver = { .ack_interrupt = dm9161_ack_interrupt, .config_intr = dm9161_config_intr, .driver = { .owner = THIS_MODULE,}, -}; +} }; static int __init davicom_init(void) { - int ret; - - ret = phy_driver_register(&dm9161e_driver); - if (ret) - goto err1; - - ret = phy_driver_register(&dm9161a_driver); - if (ret) - goto err2; - - ret = phy_driver_register(&dm9131_driver); - if (ret) - goto err3; - return 0; - - err3: - phy_driver_unregister(&dm9161a_driver); - err2: - phy_driver_unregister(&dm9161e_driver); - err1: - return ret; + return phy_drivers_register(dm91xx_driver, + ARRAY_SIZE(dm91xx_driver)); } static void __exit davicom_exit(void) { - phy_driver_unregister(&dm9161e_driver); - phy_driver_unregister(&dm9161a_driver); - phy_driver_unregister(&dm9131_driver); + phy_drivers_unregister(dm91xx_driver, + ARRAY_SIZE(dm91xx_driver)); } module_init(davicom_init); diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c index 47f8e8939266..d5199cb4caec 100644 --- a/drivers/net/phy/icplus.c +++ b/drivers/net/phy/icplus.c @@ -202,7 +202,8 @@ static int ip101a_g_ack_interrupt(struct phy_device *phydev) return 0; } -static struct phy_driver ip175c_driver = { +static struct phy_driver icplus_driver[] = { +{ .phy_id = 0x02430d80, .name = "ICPlus IP175C", .phy_id_mask = 0x0ffffff0, @@ -213,9 +214,7 @@ static struct phy_driver ip175c_driver = { .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, -}; - -static struct phy_driver ip1001_driver = { +}, { .phy_id = 0x02430d90, .name = "ICPlus IP1001", .phy_id_mask = 0x0ffffff0, @@ -227,9 +226,7 @@ static struct phy_driver ip1001_driver = { .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, -}; - -static struct phy_driver ip101a_g_driver = { +}, { .phy_id = 0x02430c54, .name = "ICPlus IP101A/G", .phy_id_mask = 0x0ffffff0, @@ -243,28 +240,18 @@ static struct phy_driver ip101a_g_driver = { .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, -}; +} }; static int __init icplus_init(void) { - int ret = 0; - - ret = phy_driver_register(&ip1001_driver); - if (ret < 0) - return -ENODEV; - - ret = phy_driver_register(&ip101a_g_driver); - if (ret < 0) - return -ENODEV; - - return phy_driver_register(&ip175c_driver); + return phy_drivers_register(icplus_driver, + ARRAY_SIZE(icplus_driver)); } static void __exit icplus_exit(void) { - phy_driver_unregister(&ip1001_driver); - phy_driver_unregister(&ip101a_g_driver); - phy_driver_unregister(&ip175c_driver); + phy_drivers_unregister(icplus_driver, + ARRAY_SIZE(icplus_driver)); } module_init(icplus_init); diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c index 6f6e8b616a62..6d1e3fcc43e2 100644 --- a/drivers/net/phy/lxt.c +++ b/drivers/net/phy/lxt.c @@ -149,7 +149,8 @@ static int lxt973_config_aneg(struct phy_device *phydev) return phydev->priv ? 0 : genphy_config_aneg(phydev); } -static struct phy_driver lxt970_driver = { +static struct phy_driver lxt97x_driver[] = { +{ .phy_id = 0x78100000, .name = "LXT970", .phy_id_mask = 0xfffffff0, @@ -160,10 +161,8 @@ static struct phy_driver lxt970_driver = { .read_status = genphy_read_status, .ack_interrupt = lxt970_ack_interrupt, .config_intr = lxt970_config_intr, - .driver = { .owner = THIS_MODULE,}, -}; - -static struct phy_driver lxt971_driver = { + .driver = { .owner = THIS_MODULE,}, +}, { .phy_id = 0x001378e0, .name = "LXT971", .phy_id_mask = 0xfffffff0, @@ -173,10 +172,8 @@ static struct phy_driver lxt971_driver = { .read_status = genphy_read_status, .ack_interrupt = lxt971_ack_interrupt, .config_intr = lxt971_config_intr, - .driver = { .owner = THIS_MODULE,}, -}; - -static struct phy_driver lxt973_driver = { + .driver = { .owner = THIS_MODULE,}, +}, { .phy_id = 0x00137a10, .name = "LXT973", .phy_id_mask = 0xfffffff0, @@ -185,39 +182,19 @@ static struct phy_driver lxt973_driver = { .probe = lxt973_probe, .config_aneg = lxt973_config_aneg, .read_status = genphy_read_status, - .driver = { .owner = THIS_MODULE,}, -}; + .driver = { .owner = THIS_MODULE,}, +} }; static int __init lxt_init(void) { - int ret; - - ret = phy_driver_register(&lxt970_driver); - if (ret) - goto err1; - - ret = phy_driver_register(&lxt971_driver); - if (ret) - goto err2; - - ret = phy_driver_register(&lxt973_driver); - if (ret) - goto err3; - return 0; - - err3: - phy_driver_unregister(&lxt971_driver); - err2: - phy_driver_unregister(&lxt970_driver); - err1: - return ret; + return phy_drivers_register(lxt97x_driver, + ARRAY_SIZE(lxt97x_driver)); } static void __exit lxt_exit(void) { - phy_driver_unregister(&lxt970_driver); - phy_driver_unregister(&lxt971_driver); - phy_driver_unregister(&lxt973_driver); + phy_drivers_unregister(lxt97x_driver, + ARRAY_SIZE(lxt97x_driver)); } module_init(lxt_init); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 418928d644bf..5d2a3f215887 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -826,28 +826,14 @@ static struct phy_driver marvell_drivers[] = { static int __init marvell_init(void) { - int ret; - int i; - - for (i = 0; i < ARRAY_SIZE(marvell_drivers); i++) { - ret = phy_driver_register(&marvell_drivers[i]); - - if (ret) { - while (i-- > 0) - phy_driver_unregister(&marvell_drivers[i]); - return ret; - } - } - - return 0; + return phy_drivers_register(marvell_drivers, + ARRAY_SIZE(marvell_drivers)); } static void __exit marvell_exit(void) { - int i; - - for (i = 0; i < ARRAY_SIZE(marvell_drivers); i++) - phy_driver_unregister(&marvell_drivers[i]); + phy_drivers_unregister(marvell_drivers, + ARRAY_SIZE(marvell_drivers)); } module_init(marvell_init); diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 9d6c80c8a0cf..cf287e0eb408 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -114,7 +114,8 @@ static int ks8051_config_init(struct phy_device *phydev) return 0; } -static struct phy_driver ks8737_driver = { +static struct phy_driver ksphy_driver[] = { +{ .phy_id = PHY_ID_KS8737, .phy_id_mask = 0x00fffff0, .name = "Micrel KS8737", @@ -126,9 +127,7 @@ static struct phy_driver ks8737_driver = { .ack_interrupt = kszphy_ack_interrupt, .config_intr = ks8737_config_intr, .driver = { .owner = THIS_MODULE,}, -}; - -static struct phy_driver ks8041_driver = { +}, { .phy_id = PHY_ID_KS8041, .phy_id_mask = 0x00fffff0, .name = "Micrel KS8041", @@ -141,9 +140,7 @@ static struct phy_driver ks8041_driver = { .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, .driver = { .owner = THIS_MODULE,}, -}; - -static struct phy_driver ks8051_driver = { +}, { .phy_id = PHY_ID_KS8051, .phy_id_mask = 0x00fffff0, .name = "Micrel KS8051", @@ -156,9 +153,7 @@ static struct phy_driver ks8051_driver = { .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, .driver = { .owner = THIS_MODULE,}, -}; - -static struct phy_driver ks8001_driver = { +}, { .phy_id = PHY_ID_KS8001, .name = "Micrel KS8001 or KS8721", .phy_id_mask = 0x00ffffff, @@ -170,9 +165,7 @@ static struct phy_driver ks8001_driver = { .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, .driver = { .owner = THIS_MODULE,}, -}; - -static struct phy_driver ksz9021_driver = { +}, { .phy_id = PHY_ID_KSZ9021, .phy_id_mask = 0x000ffffe, .name = "Micrel KSZ9021 Gigabit PHY", @@ -185,51 +178,18 @@ static struct phy_driver ksz9021_driver = { .ack_interrupt = kszphy_ack_interrupt, .config_intr = ksz9021_config_intr, .driver = { .owner = THIS_MODULE, }, -}; +} }; static int __init ksphy_init(void) { - int ret; - - ret = phy_driver_register(&ks8001_driver); - if (ret) - goto err1; - - ret = phy_driver_register(&ksz9021_driver); - if (ret) - goto err2; - - ret = phy_driver_register(&ks8737_driver); - if (ret) - goto err3; - ret = phy_driver_register(&ks8041_driver); - if (ret) - goto err4; - ret = phy_driver_register(&ks8051_driver); - if (ret) - goto err5; - - return 0; - -err5: - phy_driver_unregister(&ks8041_driver); -err4: - phy_driver_unregister(&ks8737_driver); -err3: - phy_driver_unregister(&ksz9021_driver); -err2: - phy_driver_unregister(&ks8001_driver); -err1: - return ret; + return phy_drivers_register(ksphy_driver, + ARRAY_SIZE(ksphy_driver)); } static void __exit ksphy_exit(void) { - phy_driver_unregister(&ks8001_driver); - phy_driver_unregister(&ks8737_driver); - phy_driver_unregister(&ksz9021_driver); - phy_driver_unregister(&ks8041_driver); - phy_driver_unregister(&ks8051_driver); + phy_drivers_unregister(ksphy_driver, + ARRAY_SIZE(ksphy_driver)); } module_init(ksphy_init); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 47e02e7dc737..8af46e88a181 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1079,12 +1079,37 @@ int phy_driver_register(struct phy_driver *new_driver) } EXPORT_SYMBOL(phy_driver_register); +int phy_drivers_register(struct phy_driver *new_driver, int n) +{ + int i, ret = 0; + + for (i = 0; i < n; i++) { + ret = phy_driver_register(new_driver + i); + if (ret) { + while (i-- > 0) + phy_driver_unregister(new_driver + i); + break; + } + } + return ret; +} +EXPORT_SYMBOL(phy_drivers_register); + void phy_driver_unregister(struct phy_driver *drv) { driver_unregister(&drv->driver); } EXPORT_SYMBOL(phy_driver_unregister); +void phy_drivers_unregister(struct phy_driver *drv, int n) +{ + int i; + for (i = 0; i < n; i++) { + phy_driver_unregister(drv + i); + } +} +EXPORT_SYMBOL(phy_drivers_unregister); + static struct phy_driver genphy_driver = { .phy_id = 0xffffffff, .phy_id_mask = 0xffffffff, diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index f414ffb5b728..72f93470ea35 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -65,11 +65,7 @@ static struct phy_driver rtl821x_driver = { static int __init realtek_init(void) { - int ret; - - ret = phy_driver_register(&rtl821x_driver); - - return ret; + return phy_driver_register(&rtl821x_driver); } static void __exit realtek_exit(void) diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index fc3e7e96c88c..c6b06d311fee 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -61,7 +61,8 @@ static int lan911x_config_init(struct phy_device *phydev) return smsc_phy_ack_interrupt(phydev); } -static struct phy_driver lan83c185_driver = { +static struct phy_driver smsc_phy_driver[] = { +{ .phy_id = 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */ .phy_id_mask = 0xfffffff0, .name = "SMSC LAN83C185", @@ -83,9 +84,7 @@ static struct phy_driver lan83c185_driver = { .resume = genphy_resume, .driver = { .owner = THIS_MODULE, } -}; - -static struct phy_driver lan8187_driver = { +}, { .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ .phy_id_mask = 0xfffffff0, .name = "SMSC LAN8187", @@ -107,9 +106,7 @@ static struct phy_driver lan8187_driver = { .resume = genphy_resume, .driver = { .owner = THIS_MODULE, } -}; - -static struct phy_driver lan8700_driver = { +}, { .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ .phy_id_mask = 0xfffffff0, .name = "SMSC LAN8700", @@ -131,9 +128,7 @@ static struct phy_driver lan8700_driver = { .resume = genphy_resume, .driver = { .owner = THIS_MODULE, } -}; - -static struct phy_driver lan911x_int_driver = { +}, { .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ .phy_id_mask = 0xfffffff0, .name = "SMSC LAN911x Internal PHY", @@ -155,9 +150,7 @@ static struct phy_driver lan911x_int_driver = { .resume = genphy_resume, .driver = { .owner = THIS_MODULE, } -}; - -static struct phy_driver lan8710_driver = { +}, { .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ .phy_id_mask = 0xfffffff0, .name = "SMSC LAN8710/LAN8720", @@ -179,53 +172,18 @@ static struct phy_driver lan8710_driver = { .resume = genphy_resume, .driver = { .owner = THIS_MODULE, } -}; +} }; static int __init smsc_init(void) { - int ret; - - ret = phy_driver_register (&lan83c185_driver); - if (ret) - goto err1; - - ret = phy_driver_register (&lan8187_driver); - if (ret) - goto err2; - - ret = phy_driver_register (&lan8700_driver); - if (ret) - goto err3; - - ret = phy_driver_register (&lan911x_int_driver); - if (ret) - goto err4; - - ret = phy_driver_register (&lan8710_driver); - if (ret) - goto err5; - - return 0; - -err5: - phy_driver_unregister (&lan911x_int_driver); -err4: - phy_driver_unregister (&lan8700_driver); -err3: - phy_driver_unregister (&lan8187_driver); -err2: - phy_driver_unregister (&lan83c185_driver); -err1: - return ret; + return phy_drivers_register(smsc_phy_driver, + ARRAY_SIZE(smsc_phy_driver)); } static void __exit smsc_exit(void) { - phy_driver_unregister (&lan8710_driver); - phy_driver_unregister (&lan911x_int_driver); - phy_driver_unregister (&lan8700_driver); - phy_driver_unregister (&lan8187_driver); - phy_driver_unregister (&lan83c185_driver); + return phy_drivers_unregister(smsc_phy_driver, + ARRAY_SIZE(smsc_phy_driver)); } MODULE_DESCRIPTION("SMSC PHY driver"); diff --git a/drivers/net/phy/ste10Xp.c b/drivers/net/phy/ste10Xp.c index 187a2fa814f2..5e1eb138916f 100644 --- a/drivers/net/phy/ste10Xp.c +++ b/drivers/net/phy/ste10Xp.c @@ -81,7 +81,8 @@ static int ste10Xp_ack_interrupt(struct phy_device *phydev) return 0; } -static struct phy_driver ste101p_pdriver = { +static struct phy_driver ste10xp_pdriver[] = { +{ .phy_id = STE101P_PHY_ID, .phy_id_mask = 0xfffffff0, .name = "STe101p", @@ -95,9 +96,7 @@ static struct phy_driver ste101p_pdriver = { .suspend = genphy_suspend, .resume = genphy_resume, .driver = {.owner = THIS_MODULE,} -}; - -static struct phy_driver ste100p_pdriver = { +}, { .phy_id = STE100P_PHY_ID, .phy_id_mask = 0xffffffff, .name = "STe100p", @@ -111,22 +110,18 @@ static struct phy_driver ste100p_pdriver = { .suspend = genphy_suspend, .resume = genphy_resume, .driver = {.owner = THIS_MODULE,} -}; +} }; static int __init ste10Xp_init(void) { - int retval; - - retval = phy_driver_register(&ste100p_pdriver); - if (retval < 0) - return retval; - return phy_driver_register(&ste101p_pdriver); + return phy_drivers_register(ste10xp_pdriver, + ARRAY_SIZE(ste10xp_pdriver)); } static void __exit ste10Xp_exit(void) { - phy_driver_unregister(&ste100p_pdriver); - phy_driver_unregister(&ste101p_pdriver); + phy_drivers_unregister(ste10xp_pdriver, + ARRAY_SIZE(ste10xp_pdriver)); } module_init(ste10Xp_init); diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c index 0ec8e09cc2ac..2585c383e623 100644 --- a/drivers/net/phy/vitesse.c +++ b/drivers/net/phy/vitesse.c @@ -138,21 +138,6 @@ static int vsc82xx_config_intr(struct phy_device *phydev) return err; } -/* Vitesse 824x */ -static struct phy_driver vsc8244_driver = { - .phy_id = PHY_ID_VSC8244, - .name = "Vitesse VSC8244", - .phy_id_mask = 0x000fffc0, - .features = PHY_GBIT_FEATURES, - .flags = PHY_HAS_INTERRUPT, - .config_init = &vsc824x_config_init, - .config_aneg = &genphy_config_aneg, - .read_status = &genphy_read_status, - .ack_interrupt = &vsc824x_ack_interrupt, - .config_intr = &vsc82xx_config_intr, - .driver = { .owner = THIS_MODULE,}, -}; - static int vsc8221_config_init(struct phy_device *phydev) { int err; @@ -165,8 +150,22 @@ static int vsc8221_config_init(struct phy_device *phydev) Options are 802.3Z SerDes or SGMII */ } -/* Vitesse 8221 */ -static struct phy_driver vsc8221_driver = { +/* Vitesse 824x */ +static struct phy_driver vsc82xx_driver[] = { +{ + .phy_id = PHY_ID_VSC8244, + .name = "Vitesse VSC8244", + .phy_id_mask = 0x000fffc0, + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_init = &vsc824x_config_init, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .ack_interrupt = &vsc824x_ack_interrupt, + .config_intr = &vsc82xx_config_intr, + .driver = { .owner = THIS_MODULE,}, +}, { + /* Vitesse 8221 */ .phy_id = PHY_ID_VSC8221, .phy_id_mask = 0x000ffff0, .name = "Vitesse VSC8221", @@ -177,26 +176,19 @@ static struct phy_driver vsc8221_driver = { .read_status = &genphy_read_status, .ack_interrupt = &vsc824x_ack_interrupt, .config_intr = &vsc82xx_config_intr, - .driver = { .owner = THIS_MODULE,}, -}; + .driver = { .owner = THIS_MODULE,}, +} }; static int __init vsc82xx_init(void) { - int err; - - err = phy_driver_register(&vsc8244_driver); - if (err < 0) - return err; - err = phy_driver_register(&vsc8221_driver); - if (err < 0) - phy_driver_unregister(&vsc8244_driver); - return err; + return phy_drivers_register(vsc82xx_driver, + ARRAY_SIZE(vsc82xx_driver)); } static void __exit vsc82xx_exit(void) { - phy_driver_unregister(&vsc8244_driver); - phy_driver_unregister(&vsc8221_driver); + return phy_drivers_unregister(vsc82xx_driver, + ARRAY_SIZE(vsc82xx_driver)); } module_init(vsc82xx_init); diff --git a/include/linux/phy.h b/include/linux/phy.h index c35299e4da8e..93b3cf77f564 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -533,7 +533,9 @@ int genphy_read_status(struct phy_device *phydev); int genphy_suspend(struct phy_device *phydev); int genphy_resume(struct phy_device *phydev); void phy_driver_unregister(struct phy_driver *drv); +void phy_drivers_unregister(struct phy_driver *drv, int n); int phy_driver_register(struct phy_driver *new_driver); +int phy_drivers_register(struct phy_driver *new_driver, int n); void phy_state_machine(struct work_struct *work); void phy_start_machine(struct phy_device *phydev, void (*handler)(struct net_device *)); -- cgit v1.2.3-59-g8ed1b From 89a54e48b9cbb44aed1bf6cd712e087b96b6ae65 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 15 Jun 2012 14:33:17 +0200 Subject: nl80211: prepare for non-netdev wireless devs In order to support a P2P device abstraction and Bluetooth high-speed AMPs, we need to have a way to identify virtual interfaces that don't have a netdev associated. Do this by adding a NL80211_ATTR_WDEV attribute to identify a wdev which may or may not also be a netdev. To simplify things, use a 64-bit value with the high 32 bits being the wiphy index for this new wdev identifier in the nl80211 API. Signed-off-by: Johannes Berg --- include/linux/nl80211.h | 5 ++ include/net/cfg80211.h | 22 +++++--- net/wireless/core.c | 13 ++--- net/wireless/core.h | 6 +-- net/wireless/nl80211.c | 135 ++++++++++++++++++++++++++++++++++++++---------- net/wireless/sme.c | 4 +- net/wireless/util.c | 6 +-- 7 files changed, 144 insertions(+), 47 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index db961a59247f..e791487ead37 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -771,6 +771,9 @@ enum nl80211_commands { * @NL80211_ATTR_IFNAME: network interface name * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype * + * @NL80211_ATTR_WDEV: wireless device identifier, used for pseudo-devices + * that don't have a netdev (u64) + * * @NL80211_ATTR_MAC: MAC address (various uses) * * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of @@ -1493,6 +1496,8 @@ enum nl80211_attrs { NL80211_ATTR_BG_SCAN_PERIOD, + NL80211_ATTR_WDEV, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 51f67a9003a9..a14e6a406681 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2341,17 +2341,25 @@ struct cfg80211_internal_bss; struct cfg80211_cached_keys; /** - * struct wireless_dev - wireless per-netdev state + * struct wireless_dev - wireless device state * - * This structure must be allocated by the driver/stack - * that uses the ieee80211_ptr field in struct net_device - * (this is intentional so it can be allocated along with - * the netdev.) + * For netdevs, this structure must be allocated by the driver + * that uses the ieee80211_ptr field in struct net_device (this + * is intentional so it can be allocated along with the netdev.) + * It need not be registered then as netdev registration will + * be intercepted by cfg80211 to see the new wireless device. + * + * For non-netdev uses, it must also be allocated by the driver + * in response to the cfg80211 callbacks that require it, as + * there's no netdev registration in that case it may not be + * allocated outside of callback operations that return it. * * @wiphy: pointer to hardware description * @iftype: interface type * @list: (private) Used to collect the interfaces - * @netdev: (private) Used to reference back to the netdev + * @netdev: (private) Used to reference back to the netdev, may be %NULL + * @identifier: (private) Identifier used in nl80211 to identify this + * wireless device if it has no netdev * @current_bss: (private) Used by the internal configuration code * @channel: (private) Used by the internal configuration code to track * the user-set AP, monitor and WDS channel @@ -2383,6 +2391,8 @@ struct wireless_dev { struct list_head list; struct net_device *netdev; + u32 identifier; + struct list_head mgmt_registrations; spinlock_t mgmt_registrations_lock; diff --git a/net/wireless/core.c b/net/wireless/core.c index e42a97b5b971..b110a8a242db 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -176,7 +176,7 @@ int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, if (!(rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK)) return -EOPNOTSUPP; - list_for_each_entry(wdev, &rdev->netdev_list, list) { + list_for_each_entry(wdev, &rdev->wdev_list, list) { wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); if (err) @@ -188,7 +188,7 @@ int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, /* failed -- clean up to old netns */ net = wiphy_net(&rdev->wiphy); - list_for_each_entry_continue_reverse(wdev, &rdev->netdev_list, + list_for_each_entry_continue_reverse(wdev, &rdev->wdev_list, list) { wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; err = dev_change_net_namespace(wdev->netdev, net, @@ -226,7 +226,7 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked) rtnl_lock(); mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->netdev_list, list) + list_for_each_entry(wdev, &rdev->wdev_list, list) dev_close(wdev->netdev); mutex_unlock(&rdev->devlist_mtx); @@ -304,7 +304,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) mutex_init(&rdev->mtx); mutex_init(&rdev->devlist_mtx); mutex_init(&rdev->sched_scan_mtx); - INIT_LIST_HEAD(&rdev->netdev_list); + INIT_LIST_HEAD(&rdev->wdev_list); spin_lock_init(&rdev->bss_lock); INIT_LIST_HEAD(&rdev->bss_list); INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); @@ -622,7 +622,7 @@ void wiphy_unregister(struct wiphy *wiphy) __count == 0; })); mutex_lock(&rdev->devlist_mtx); - BUG_ON(!list_empty(&rdev->netdev_list)); + BUG_ON(!list_empty(&rdev->wdev_list)); mutex_unlock(&rdev->devlist_mtx); /* @@ -821,7 +821,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, spin_lock_init(&wdev->mgmt_registrations_lock); mutex_lock(&rdev->devlist_mtx); - list_add_rcu(&wdev->list, &rdev->netdev_list); + wdev->identifier = ++rdev->wdev_id; + list_add_rcu(&wdev->list, &rdev->wdev_list); rdev->devlist_generation++; /* can only change netns with wiphy */ dev->features |= NETIF_F_NETNS_LOCAL; diff --git a/net/wireless/core.h b/net/wireless/core.h index 377dc394f48c..6b0170a5f05f 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -47,11 +47,11 @@ struct cfg80211_registered_device { /* wiphy index, internal only */ int wiphy_idx; - /* associate netdev list */ + /* associated wireless interfaces */ struct mutex devlist_mtx; /* protected by devlist_mtx or RCU */ - struct list_head netdev_list; - int devlist_generation; + struct list_head wdev_list; + int devlist_generation, wdev_id; int opencount; /* also protected by devlist_mtx */ wait_queue_head_t dev_wait; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2a5cdb60bc6e..35a9b15289f1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -46,28 +46,60 @@ static struct genl_family nl80211_fam = { .post_doit = nl80211_post_doit, }; -/* internal helper: get rdev and dev */ -static int get_rdev_dev_by_ifindex(struct net *netns, struct nlattr **attrs, - struct cfg80211_registered_device **rdev, - struct net_device **dev) +/* returns ERR_PTR values */ +static struct wireless_dev * +__cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs) { - int ifindex; + struct cfg80211_registered_device *rdev; + struct wireless_dev *result = NULL; + bool have_ifidx = attrs[NL80211_ATTR_IFINDEX]; + bool have_wdev_id = attrs[NL80211_ATTR_WDEV]; + u64 wdev_id; + int wiphy_idx = -1; + int ifidx = -1; - if (!attrs[NL80211_ATTR_IFINDEX]) - return -EINVAL; + assert_cfg80211_lock(); - ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]); - *dev = dev_get_by_index(netns, ifindex); - if (!*dev) - return -ENODEV; + if (!have_ifidx && !have_wdev_id) + return ERR_PTR(-EINVAL); - *rdev = cfg80211_get_dev_from_ifindex(netns, ifindex); - if (IS_ERR(*rdev)) { - dev_put(*dev); - return PTR_ERR(*rdev); + if (have_ifidx) + ifidx = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]); + if (have_wdev_id) { + wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]); + wiphy_idx = wdev_id >> 32; } - return 0; + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + struct wireless_dev *wdev; + + if (wiphy_net(&rdev->wiphy) != netns) + continue; + + if (have_wdev_id && rdev->wiphy_idx != wiphy_idx) + continue; + + mutex_lock(&rdev->devlist_mtx); + list_for_each_entry(wdev, &rdev->wdev_list, list) { + if (have_ifidx && wdev->netdev && + wdev->netdev->ifindex == ifidx) { + result = wdev; + break; + } + if (have_wdev_id && wdev->identifier == (u32)wdev_id) { + result = wdev; + break; + } + } + mutex_unlock(&rdev->devlist_mtx); + + if (result) + break; + } + + if (result) + return result; + return ERR_PTR(-ENODEV); } static struct cfg80211_registered_device * @@ -79,13 +111,40 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs) assert_cfg80211_lock(); if (!attrs[NL80211_ATTR_WIPHY] && - !attrs[NL80211_ATTR_IFINDEX]) + !attrs[NL80211_ATTR_IFINDEX] && + !attrs[NL80211_ATTR_WDEV]) return ERR_PTR(-EINVAL); if (attrs[NL80211_ATTR_WIPHY]) rdev = cfg80211_rdev_by_wiphy_idx( nla_get_u32(attrs[NL80211_ATTR_WIPHY])); + if (attrs[NL80211_ATTR_WDEV]) { + u64 wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]); + struct wireless_dev *wdev; + bool found = false; + + tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32); + if (tmp) { + /* make sure wdev exists */ + mutex_lock(&tmp->devlist_mtx); + list_for_each_entry(wdev, &tmp->wdev_list, list) { + if (wdev->identifier != (u32)wdev_id) + continue; + found = true; + break; + } + mutex_unlock(&tmp->devlist_mtx); + + if (!found) + tmp = NULL; + + if (rdev && tmp != rdev) + return ERR_PTR(-EINVAL); + rdev = tmp; + } + } + if (attrs[NL80211_ATTR_IFINDEX]) { int ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]); netdev = dev_get_by_index(netns, ifindex); @@ -294,6 +353,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 }, [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 }, [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, + [NL80211_ATTR_WDEV] = { .type = NLA_U64 }, }; /* policy for the key attributes */ @@ -1674,6 +1734,8 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct net_device *dev) { void *hdr; + u64 wdev_id = (u64)dev->ieee80211_ptr->identifier | + ((u64)rdev->wiphy_idx << 32); hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE); if (!hdr) @@ -1684,6 +1746,7 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name) || nla_put_u32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype) || + nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id) || nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->devlist_generation ^ (cfg80211_rdev_list_generation << 2))) @@ -1724,7 +1787,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * if_idx = 0; mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->netdev_list, list) { + list_for_each_entry(wdev, &rdev->wdev_list, list) { if (if_idx < if_start) { if_idx++; continue; @@ -2350,7 +2413,7 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev, mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->netdev_list, list) { + list_for_each_entry(wdev, &rdev->wdev_list, list) { if (wdev->iftype != NL80211_IFTYPE_AP && wdev->iftype != NL80211_IFTYPE_P2P_GO) continue; @@ -6660,8 +6723,8 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; struct net_device *dev; - int err; bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL; if (rtnl) @@ -6676,21 +6739,39 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, } info->user_ptr[0] = rdev; } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) { - err = get_rdev_dev_by_ifindex(genl_info_net(info), info->attrs, - &rdev, &dev); - if (err) { + mutex_lock(&cfg80211_mutex); + wdev = __cfg80211_wdev_from_attrs(genl_info_net(info), + info->attrs); + if (IS_ERR(wdev)) { + mutex_unlock(&cfg80211_mutex); if (rtnl) rtnl_unlock(); - return err; + return PTR_ERR(wdev); } + + if (!wdev->netdev) { + mutex_unlock(&cfg80211_mutex); + if (rtnl) + rtnl_unlock(); + return -EINVAL; + } + + dev = wdev->netdev; + rdev = wiphy_to_dev(wdev->wiphy); + if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP && !netif_running(dev)) { - cfg80211_unlock_rdev(rdev); - dev_put(dev); + mutex_unlock(&cfg80211_mutex); if (rtnl) rtnl_unlock(); return -ENETDOWN; } + + dev_hold(dev); + cfg80211_lock_rdev(rdev); + + mutex_unlock(&cfg80211_mutex); + info->user_ptr[0] = rdev; info->user_ptr[1] = dev; } @@ -8483,7 +8564,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb, rcu_read_lock(); list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { - list_for_each_entry_rcu(wdev, &rdev->netdev_list, list) + list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) cfg80211_mlme_unregister_socket(wdev, notify->pid); if (rdev->ap_beacons_nlpid == notify->pid) rdev->ap_beacons_nlpid = 0; diff --git a/net/wireless/sme.c b/net/wireless/sme.c index f7e937ff8978..dec97981e689 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -51,7 +51,7 @@ static bool cfg80211_is_all_idle(void) */ list_for_each_entry(rdev, &cfg80211_rdev_list, list) { cfg80211_lock_rdev(rdev); - list_for_each_entry(wdev, &rdev->netdev_list, list) { + list_for_each_entry(wdev, &rdev->wdev_list, list) { wdev_lock(wdev); if (wdev->sme_state != CFG80211_SME_IDLE) is_all_idle = false; @@ -221,7 +221,7 @@ void cfg80211_conn_work(struct work_struct *work) cfg80211_lock_rdev(rdev); mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->netdev_list, list) { + list_for_each_entry(wdev, &rdev->wdev_list, list) { wdev_lock(wdev); if (!netif_running(wdev->netdev)) { wdev_unlock(wdev); diff --git a/net/wireless/util.c b/net/wireless/util.c index e31f1dba79ec..f7a0647bde9a 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -793,7 +793,7 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev) mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->netdev_list, list) + list_for_each_entry(wdev, &rdev->wdev_list, list) cfg80211_process_wdev_events(wdev); mutex_unlock(&rdev->devlist_mtx); @@ -994,7 +994,7 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->netdev_list, list) { + list_for_each_entry(wdev, &rdev->wdev_list, list) { if (!wdev->beacon_interval) continue; if (wdev->beacon_interval != beacon_int) { @@ -1050,7 +1050,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, break; } - list_for_each_entry(wdev_iter, &rdev->netdev_list, list) { + list_for_each_entry(wdev_iter, &rdev->wdev_list, list) { if (wdev_iter == wdev) continue; if (!netif_running(wdev_iter->netdev)) -- cgit v1.2.3-59-g8ed1b From 929578ab0339fe42bb3ceeaa2e6607189cddf70b Mon Sep 17 00:00:00 2001 From: Keng-Yu Lin Date: Fri, 6 Jul 2012 18:06:11 +0800 Subject: HID: Add suport for the brightness control keys on HP keyboards The keys are found on the keyboards bundled with HP All-In-One machines with USB VID/PID of 04ca:004d and 04f2:1061. Signed-off-by: Keng-Yu Lin Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 9 +++++++++ include/linux/hid.h | 1 + 2 files changed, 10 insertions(+) (limited to 'include/linux') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 132b0019365e..879443bf410f 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -834,6 +834,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } break; + case HID_UP_HPVENDOR2: + set_bit(EV_REP, input->evbit); + switch (usage->hid & HID_USAGE) { + case 0x003: map_key_clear(KEY_BRIGHTNESSDOWN); break; + case 0x004: map_key_clear(KEY_BRIGHTNESSUP); break; + default: goto ignore; + } + break; + case HID_UP_MSVENDOR: goto ignore; diff --git a/include/linux/hid.h b/include/linux/hid.h index 449fa385703d..42970de1b40c 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -200,6 +200,7 @@ struct hid_item { #define HID_UP_DIGITIZER 0x000d0000 #define HID_UP_PID 0x000f0000 #define HID_UP_HPVENDOR 0xff7f0000 +#define HID_UP_HPVENDOR2 0xff010000 #define HID_UP_MSVENDOR 0xff000000 #define HID_UP_CUSTOM 0x00ff0000 #define HID_UP_LOGIVENDOR 0xffbc0000 -- cgit v1.2.3-59-g8ed1b From 00eeedcf084a21bf436ff3147f11f0923c811155 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 30 Jun 2012 01:44:37 +0200 Subject: bcma: extend workaround for bcm4331 This patch is based on a recent version of the Broadcom SDK. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_chipcommon_pmu.c | 3 +++ include/linux/bcma/bcma_driver_chipcommon.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include/linux') diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c index 61ce4054b3c3..f18df1f392ec 100644 --- a/drivers/bcma/driver_chipcommon_pmu.c +++ b/drivers/bcma/driver_chipcommon_pmu.c @@ -123,8 +123,11 @@ void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable) val |= BCMA_CHIPCTL_4331_EXTPA_EN; if (bus->chipinfo.pkg == 9 || bus->chipinfo.pkg == 11) val |= BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5; + else if (bus->chipinfo.rev > 0) + val |= BCMA_CHIPCTL_4331_EXTPA_EN2; } else { val &= ~BCMA_CHIPCTL_4331_EXTPA_EN; + val &= ~BCMA_CHIPCTL_4331_EXTPA_EN2; val &= ~BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5; } bcma_cc_write32(cc, BCMA_CC_CHIPCTL, val); diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index 8bbfe31fbac8..1e523cc61860 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -321,6 +321,7 @@ #define BCMA_CHIPCTL_4331_OVR_PIPEAUXPWRDOWN BIT(9) /* override core control on pipe_AuxPowerDown */ #define BCMA_CHIPCTL_4331_PCIE_AUXCLKEN BIT(10) /* pcie_auxclkenable */ #define BCMA_CHIPCTL_4331_PCIE_PIPE_PLLDOWN BIT(11) /* pcie_pipe_pllpowerdown */ +#define BCMA_CHIPCTL_4331_EXTPA_EN2 BIT(12) /* 0 ext pa disable, 1 ext pa enabled */ #define BCMA_CHIPCTL_4331_BT_SHD0_ON_GPIO4 BIT(16) /* enable bt_shd0 at gpio4 */ #define BCMA_CHIPCTL_4331_BT_SHD1_ON_GPIO5 BIT(17) /* enable bt_shd1 at gpio5 */ -- cgit v1.2.3-59-g8ed1b From 4b4f5be2e49a604de11dee0ee9b3f151de061724 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 30 Jun 2012 01:44:38 +0200 Subject: bcma: add constants for chip ids The chip IDs are used all over bcma and no constants where defined. This patch adds the constants and makes bcma use them. Acked-by: Arend van Spriel Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_chipcommon_pmu.c | 81 ++++++++++++++++++------------------ drivers/bcma/driver_mips.c | 8 ++-- drivers/bcma/driver_pci_host.c | 8 ++-- drivers/bcma/sprom.c | 18 ++++---- include/linux/bcma/bcma.h | 30 +++++++++++++ 5 files changed, 90 insertions(+), 55 deletions(-) (limited to 'include/linux') diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c index f18df1f392ec..89528cf4d145 100644 --- a/drivers/bcma/driver_chipcommon_pmu.c +++ b/drivers/bcma/driver_chipcommon_pmu.c @@ -59,10 +59,10 @@ static void bcma_pmu_pll_init(struct bcma_drv_cc *cc) struct bcma_bus *bus = cc->core->bus; switch (bus->chipinfo.id) { - case 0x4313: - case 0x4331: - case 43224: - case 43225: + case BCMA_CHIP_ID_BCM4313: + case BCMA_CHIP_ID_BCM4331: + case BCMA_CHIP_ID_BCM43224: + case BCMA_CHIP_ID_BCM43225: break; default: pr_err("PLL init unknown for device 0x%04X\n", @@ -76,13 +76,13 @@ static void bcma_pmu_resources_init(struct bcma_drv_cc *cc) u32 min_msk = 0, max_msk = 0; switch (bus->chipinfo.id) { - case 0x4313: + case BCMA_CHIP_ID_BCM4313: min_msk = 0x200D; max_msk = 0xFFFF; break; - case 0x4331: - case 43224: - case 43225: + case BCMA_CHIP_ID_BCM4331: + case BCMA_CHIP_ID_BCM43224: + case BCMA_CHIP_ID_BCM43225: break; default: pr_err("PMU resource config unknown for device 0x%04X\n", @@ -101,10 +101,10 @@ void bcma_pmu_swreg_init(struct bcma_drv_cc *cc) struct bcma_bus *bus = cc->core->bus; switch (bus->chipinfo.id) { - case 0x4313: - case 0x4331: - case 43224: - case 43225: + case BCMA_CHIP_ID_BCM4313: + case BCMA_CHIP_ID_BCM4331: + case BCMA_CHIP_ID_BCM43224: + case BCMA_CHIP_ID_BCM43225: break; default: pr_err("PMU switch/regulators init unknown for device " @@ -138,15 +138,15 @@ void bcma_pmu_workarounds(struct bcma_drv_cc *cc) struct bcma_bus *bus = cc->core->bus; switch (bus->chipinfo.id) { - case 0x4313: + case BCMA_CHIP_ID_BCM4313: bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x7); break; - case 0x4331: - case 43431: + case BCMA_CHIP_ID_BCM4331: + case BCMA_CHIP_ID_BCM43431: /* Ext PA lines must be enabled for tx on BCM4331 */ bcma_chipco_bcm4331_ext_pa_lines_ctl(cc, true); break; - case 43224: + case BCMA_CHIP_ID_BCM43224: if (bus->chipinfo.rev == 0) { pr_err("Workarounds for 43224 rev 0 not fully " "implemented\n"); @@ -155,7 +155,7 @@ void bcma_pmu_workarounds(struct bcma_drv_cc *cc) bcma_chipco_chipctl_maskset(cc, 0, ~0, 0xF0); } break; - case 43225: + case BCMA_CHIP_ID_BCM43225: break; default: pr_err("Workarounds unknown for device 0x%04X\n", @@ -194,17 +194,17 @@ u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc) struct bcma_bus *bus = cc->core->bus; switch (bus->chipinfo.id) { - case 0x4716: - case 0x4748: - case 47162: - case 0x4313: - case 0x5357: - case 0x4749: - case 53572: + case BCMA_CHIP_ID_BCM4716: + case BCMA_CHIP_ID_BCM4748: + case BCMA_CHIP_ID_BCM47162: + case BCMA_CHIP_ID_BCM4313: + case BCMA_CHIP_ID_BCM5357: + case BCMA_CHIP_ID_BCM4749: + case BCMA_CHIP_ID_BCM53572: /* always 20Mhz */ return 20000 * 1000; - case 0x5356: - case 0x5300: + case BCMA_CHIP_ID_BCM5356: + case BCMA_CHIP_ID_BCM4706: /* always 25Mhz */ return 25000 * 1000; default: @@ -227,7 +227,8 @@ static u32 bcma_pmu_clock(struct bcma_drv_cc *cc, u32 pll0, u32 m) BUG_ON(!m || m > 4); - if (bus->chipinfo.id == 0x5357 || bus->chipinfo.id == 0x4749) { + if (bus->chipinfo.id == BCMA_CHIP_ID_BCM5357 || + bus->chipinfo.id == BCMA_CHIP_ID_BCM4749) { /* Detect failure in clock setting */ tmp = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT); if (tmp & 0x40000) @@ -259,22 +260,22 @@ u32 bcma_pmu_get_clockcontrol(struct bcma_drv_cc *cc) struct bcma_bus *bus = cc->core->bus; switch (bus->chipinfo.id) { - case 0x4716: - case 0x4748: - case 47162: + case BCMA_CHIP_ID_BCM4716: + case BCMA_CHIP_ID_BCM4748: + case BCMA_CHIP_ID_BCM47162: return bcma_pmu_clock(cc, BCMA_CC_PMU4716_MAINPLL_PLL0, BCMA_CC_PMU5_MAINPLL_SSB); - case 0x5356: + case BCMA_CHIP_ID_BCM5356: return bcma_pmu_clock(cc, BCMA_CC_PMU5356_MAINPLL_PLL0, BCMA_CC_PMU5_MAINPLL_SSB); - case 0x5357: - case 0x4749: + case BCMA_CHIP_ID_BCM5357: + case BCMA_CHIP_ID_BCM4749: return bcma_pmu_clock(cc, BCMA_CC_PMU5357_MAINPLL_PLL0, BCMA_CC_PMU5_MAINPLL_SSB); - case 0x5300: + case BCMA_CHIP_ID_BCM4706: return bcma_pmu_clock(cc, BCMA_CC_PMU4706_MAINPLL_PLL0, BCMA_CC_PMU5_MAINPLL_SSB); - case 53572: + case BCMA_CHIP_ID_BCM53572: return 75000000; default: pr_warn("No backplane clock specified for %04X device, " @@ -289,17 +290,17 @@ u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc) { struct bcma_bus *bus = cc->core->bus; - if (bus->chipinfo.id == 53572) + if (bus->chipinfo.id == BCMA_CHIP_ID_BCM53572) return 300000000; if (cc->pmu.rev >= 5) { u32 pll; switch (bus->chipinfo.id) { - case 0x5356: + case BCMA_CHIP_ID_BCM5356: pll = BCMA_CC_PMU5356_MAINPLL_PLL0; break; - case 0x5357: - case 0x4749: + case BCMA_CHIP_ID_BCM5357: + case BCMA_CHIP_ID_BCM4749: pll = BCMA_CC_PMU5357_MAINPLL_PLL0; break; default: @@ -307,7 +308,7 @@ u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc) break; } - /* TODO: if (bus->chipinfo.id == 0x5300) + /* TODO: if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) return si_4706_pmu_clock(sih, osh, cc, PMU4706_MAINPLL_PLL0, PMU5_MAINPLL_CPU); */ return bcma_pmu_clock(cc, pll, BCMA_CC_PMU5_MAINPLL_CPU); } diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c index c3e9dff4224e..73ed3017077f 100644 --- a/drivers/bcma/driver_mips.c +++ b/drivers/bcma/driver_mips.c @@ -22,15 +22,15 @@ /* The 47162a0 hangs when reading MIPS DMP registers registers */ static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev) { - return dev->bus->chipinfo.id == 47162 && dev->bus->chipinfo.rev == 0 && - dev->id.id == BCMA_CORE_MIPS_74K; + return dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM47162 && + dev->bus->chipinfo.rev == 0 && dev->id.id == BCMA_CORE_MIPS_74K; } /* The 5357b0 hangs when reading USB20H DMP registers */ static inline bool bcma_core_mips_bcm5357b0_quirk(struct bcma_device *dev) { - return (dev->bus->chipinfo.id == 0x5357 || - dev->bus->chipinfo.id == 0x4749) && + return (dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM5357 || + dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM4749) && dev->bus->chipinfo.pkg == 11 && dev->id.id == BCMA_CORE_USB20_HOST; } diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c index b9a86edfec39..d6e8a37e3e5f 100644 --- a/drivers/bcma/driver_pci_host.c +++ b/drivers/bcma/driver_pci_host.c @@ -215,7 +215,8 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev, } else { writel(val, mmio); - if (chipid == 0x4716 || chipid == 0x4748) + if (chipid == BCMA_CHIP_ID_BCM4716 || + chipid == BCMA_CHIP_ID_BCM4748) readl(mmio); } @@ -434,13 +435,14 @@ void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc) * as mips can't generate 64-bit address on the * backplane. */ - if (bus->chipinfo.id == 0x4716 || bus->chipinfo.id == 0x4748) { + if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4716 || + bus->chipinfo.id == BCMA_CHIP_ID_BCM4748) { pc_host->mem_resource.start = BCMA_SOC_PCI_MEM; pc_host->mem_resource.end = BCMA_SOC_PCI_MEM + BCMA_SOC_PCI_MEM_SZ - 1; pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0, BCMA_CORE_PCI_SBTOPCI_MEM | BCMA_SOC_PCI_MEM); - } else if (bus->chipinfo.id == 0x5300) { + } else if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) { tmp = BCMA_CORE_PCI_SBTOPCI_MEM; tmp |= BCMA_CORE_PCI_SBTOPCI_PREF; tmp |= BCMA_CORE_PCI_SBTOPCI_BURST; diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index f16f42d36071..e1eb598ec702 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -468,11 +468,11 @@ static bool bcma_sprom_ext_available(struct bcma_bus *bus) /* older chipcommon revisions use chip status register */ chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT); switch (bus->chipinfo.id) { - case 0x4313: + case BCMA_CHIP_ID_BCM4313: present_mask = BCMA_CC_CHIPST_4313_SPROM_PRESENT; break; - case 0x4331: + case BCMA_CHIP_ID_BCM4331: present_mask = BCMA_CC_CHIPST_4331_SPROM_PRESENT; break; @@ -494,16 +494,16 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus) chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT); switch (bus->chipinfo.id) { - case 0x4313: + case BCMA_CHIP_ID_BCM4313: present = chip_status & BCMA_CC_CHIPST_4313_OTP_PRESENT; break; - case 0x4331: + case BCMA_CHIP_ID_BCM4331: present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT; break; - case 43224: - case 43225: + case BCMA_CHIP_ID_BCM43224: + case BCMA_CHIP_ID_BCM43225: /* for these chips OTP is always available */ present = true; break; @@ -579,13 +579,15 @@ int bcma_sprom_get(struct bcma_bus *bus) if (!sprom) return -ENOMEM; - if (bus->chipinfo.id == 0x4331 || bus->chipinfo.id == 43431) + if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 || + bus->chipinfo.id == BCMA_CHIP_ID_BCM43431) bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false); pr_debug("SPROM offset 0x%x\n", offset); bcma_sprom_read(bus, offset, sprom); - if (bus->chipinfo.id == 0x4331 || bus->chipinfo.id == 43431) + if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 || + bus->chipinfo.id == BCMA_CHIP_ID_BCM43431) bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true); err = bcma_sprom_valid(sprom); diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 12334f9190cb..03b2f30d2ace 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -137,6 +137,36 @@ struct bcma_host_ops { #define BCMA_MAX_NR_CORES 16 +/* Chip IDs of PCIe devices */ +#define BCMA_CHIP_ID_BCM4313 0x4313 +#define BCMA_CHIP_ID_BCM43224 43224 +#define BCMA_PKG_ID_BCM43224_FAB_CSM 0x8 +#define BCMA_PKG_ID_BCM43224_FAB_SMIC 0xa +#define BCMA_CHIP_ID_BCM43225 43225 +#define BCMA_CHIP_ID_BCM43227 43227 +#define BCMA_CHIP_ID_BCM43228 43228 +#define BCMA_CHIP_ID_BCM43421 43421 +#define BCMA_CHIP_ID_BCM43428 43428 +#define BCMA_CHIP_ID_BCM43431 43431 +#define BCMA_CHIP_ID_BCM43460 43460 +#define BCMA_CHIP_ID_BCM4331 0x4331 +#define BCMA_CHIP_ID_BCM6362 0x6362 +#define BCMA_CHIP_ID_BCM4360 0x4360 +#define BCMA_CHIP_ID_BCM4352 0x4352 + +/* Chip IDs of SoCs */ +#define BCMA_CHIP_ID_BCM4706 0x5300 +#define BCMA_CHIP_ID_BCM4716 0x4716 +#define BCMA_PKG_ID_BCM4716 8 +#define BCMA_PKG_ID_BCM4717 9 +#define BCMA_PKG_ID_BCM4718 10 +#define BCMA_CHIP_ID_BCM47162 47162 +#define BCMA_CHIP_ID_BCM4748 0x4748 +#define BCMA_CHIP_ID_BCM4749 0x4749 +#define BCMA_CHIP_ID_BCM5356 0x5356 +#define BCMA_CHIP_ID_BCM5357 0x5357 +#define BCMA_CHIP_ID_BCM53572 53572 + struct bcma_device { struct bcma_bus *bus; struct bcma_device_id id; -- cgit v1.2.3-59-g8ed1b From b9562545ef0b13c0440ccd8d6dd4111fb77cb17a Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 30 Jun 2012 01:44:41 +0200 Subject: bcma: complete workaround for BCMA43224 and BCM4313 This code is based on the Broadcom SDK and brcmsmac. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_chipcommon_pmu.c | 22 +++++++++++++++++----- include/linux/bcma/bcma_driver_chipcommon.h | 8 ++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c index 2d9ac3e325ae..a8fcdf0222eb 100644 --- a/drivers/bcma/driver_chipcommon_pmu.c +++ b/drivers/bcma/driver_chipcommon_pmu.c @@ -139,7 +139,11 @@ void bcma_pmu_workarounds(struct bcma_drv_cc *cc) switch (bus->chipinfo.id) { case BCMA_CHIP_ID_BCM4313: - bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x7); + /* enable 12 mA drive strenth for 4313 and set chipControl + register bit 1 */ + bcma_chipco_chipctl_maskset(cc, 0, + BCMA_CCTRL_4313_12MA_LED_DRIVE, + BCMA_CCTRL_4313_12MA_LED_DRIVE); break; case BCMA_CHIP_ID_BCM4331: case BCMA_CHIP_ID_BCM43431: @@ -147,12 +151,20 @@ void bcma_pmu_workarounds(struct bcma_drv_cc *cc) bcma_chipco_bcm4331_ext_pa_lines_ctl(cc, true); break; case BCMA_CHIP_ID_BCM43224: + case BCMA_CHIP_ID_BCM43421: + /* enable 12 mA drive strenth for 43224 and set chipControl + register bit 15 */ if (bus->chipinfo.rev == 0) { - pr_err("Workarounds for 43224 rev 0 not fully " - "implemented\n"); - bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x00F000F0); + bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL, + BCMA_CCTRL_43224_GPIO_TOGGLE, + BCMA_CCTRL_43224_GPIO_TOGGLE); + bcma_chipco_chipctl_maskset(cc, 0, + BCMA_CCTRL_43224A0_12MA_LED_DRIVE, + BCMA_CCTRL_43224A0_12MA_LED_DRIVE); } else { - bcma_chipco_chipctl_maskset(cc, 0, ~0, 0xF0); + bcma_chipco_chipctl_maskset(cc, 0, + BCMA_CCTRL_43224B0_12MA_LED_DRIVE, + BCMA_CCTRL_43224B0_12MA_LED_DRIVE); } break; case BCMA_CHIP_ID_BCM43225: diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index 1e523cc61860..09f31ade1410 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -325,6 +325,14 @@ #define BCMA_CHIPCTL_4331_BT_SHD0_ON_GPIO4 BIT(16) /* enable bt_shd0 at gpio4 */ #define BCMA_CHIPCTL_4331_BT_SHD1_ON_GPIO5 BIT(17) /* enable bt_shd1 at gpio5 */ +/* 43224 chip-specific ChipControl register bits */ +#define BCMA_CCTRL_43224_GPIO_TOGGLE 0x8000 /* gpio[3:0] pins as btcoex or s/w gpio */ +#define BCMA_CCTRL_43224A0_12MA_LED_DRIVE 0x00F000F0 /* 12 mA drive strength */ +#define BCMA_CCTRL_43224B0_12MA_LED_DRIVE 0xF0 /* 12 mA drive strength for later 43224s */ + +/* 4313 Chip specific ChipControl register bits */ +#define BCMA_CCTRL_4313_12MA_LED_DRIVE 0x00000007 /* 12 mA drive strengh for later 4313 */ + /* Data for the PMU, if available. * Check availability with ((struct bcma_chipcommon)->capabilities & BCMA_CC_CAP_PMU) */ -- cgit v1.2.3-59-g8ed1b From c586e10992b2e5e2dfe7cca9be615818cfb98605 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 30 Jun 2012 01:44:44 +0200 Subject: bcma: add bcma_pmu_spuravoid_pllupdate() This function is needed by brcmsmac. This code is based on code from the Broadcom SDK. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_chipcommon_pmu.c | 183 +++++++++++++++++++++++++++- include/linux/bcma/bcma_driver_chipcommon.h | 14 +++ 2 files changed, 196 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c index 1a7a72fb77d6..35c9130746fb 100644 --- a/drivers/bcma/driver_chipcommon_pmu.c +++ b/drivers/bcma/driver_chipcommon_pmu.c @@ -3,7 +3,8 @@ * ChipCommon Power Management Unit driver * * Copyright 2009, Michael Buesch - * Copyright 2007, Broadcom Corporation + * Copyright 2007, 2011, Broadcom Corporation + * Copyright 2011, 2012, Hauke Mehrtens * * Licensed under the GNU/GPL. See COPYING for details. */ @@ -284,3 +285,183 @@ u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc) return bcma_pmu_get_clockcontrol(cc); } + +static void bcma_pmu_spuravoid_pll_write(struct bcma_drv_cc *cc, u32 offset, + u32 value) +{ + bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); + bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, value); +} + +void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid) +{ + u32 tmp = 0; + u8 phypll_offset = 0; + u8 bcm5357_bcm43236_p1div[] = {0x1, 0x5, 0x5}; + u8 bcm5357_bcm43236_ndiv[] = {0x30, 0xf6, 0xfc}; + struct bcma_bus *bus = cc->core->bus; + + switch (bus->chipinfo.id) { + case BCMA_CHIP_ID_BCM5357: + case BCMA_CHIP_ID_BCM4749: + case BCMA_CHIP_ID_BCM53572: + /* 5357[ab]0, 43236[ab]0, and 6362b0 */ + + /* BCM5357 needs to touch PLL1_PLLCTL[02], + so offset PLL0_PLLCTL[02] by 6 */ + phypll_offset = (bus->chipinfo.id == BCMA_CHIP_ID_BCM5357 || + bus->chipinfo.id == BCMA_CHIP_ID_BCM4749 || + bus->chipinfo.id == BCMA_CHIP_ID_BCM53572) ? 6 : 0; + + /* RMW only the P1 divider */ + bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, + BCMA_CC_PMU_PLL_CTL0 + phypll_offset); + tmp = bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA); + tmp &= (~(BCMA_CC_PMU1_PLL0_PC0_P1DIV_MASK)); + tmp |= (bcm5357_bcm43236_p1div[spuravoid] << BCMA_CC_PMU1_PLL0_PC0_P1DIV_SHIFT); + bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, tmp); + + /* RMW only the int feedback divider */ + bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, + BCMA_CC_PMU_PLL_CTL2 + phypll_offset); + tmp = bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA); + tmp &= ~(BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_MASK); + tmp |= (bcm5357_bcm43236_ndiv[spuravoid]) << BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_SHIFT; + bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, tmp); + + tmp = 1 << 10; + break; + + case BCMA_CHIP_ID_BCM4331: + case BCMA_CHIP_ID_BCM43431: + if (spuravoid == 2) { + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, + 0x11500014); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, + 0x0FC00a08); + } else if (spuravoid == 1) { + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, + 0x11500014); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, + 0x0F600a08); + } else { + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, + 0x11100014); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, + 0x03000a08); + } + tmp = 1 << 10; + break; + + case BCMA_CHIP_ID_BCM43224: + case BCMA_CHIP_ID_BCM43225: + case BCMA_CHIP_ID_BCM43421: + if (spuravoid == 1) { + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, + 0x11500010); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1, + 0x000C0C06); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, + 0x0F600a08); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, + 0x00000000); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4, + 0x2001E920); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5, + 0x88888815); + } else { + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, + 0x11100010); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1, + 0x000c0c06); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, + 0x03000a08); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, + 0x00000000); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4, + 0x200005c0); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5, + 0x88888815); + } + tmp = 1 << 10; + break; + + case BCMA_CHIP_ID_BCM4716: + case BCMA_CHIP_ID_BCM4748: + case BCMA_CHIP_ID_BCM47162: + if (spuravoid == 1) { + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, + 0x11500060); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1, + 0x080C0C06); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, + 0x0F600000); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, + 0x00000000); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4, + 0x2001E924); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5, + 0x88888815); + } else { + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, + 0x11100060); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1, + 0x080c0c06); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, + 0x03000000); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, + 0x00000000); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4, + 0x200005c0); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5, + 0x88888815); + } + + tmp = 3 << 9; + break; + + case BCMA_CHIP_ID_BCM43227: + case BCMA_CHIP_ID_BCM43228: + case BCMA_CHIP_ID_BCM43428: + /* LCNXN */ + /* PLL Settings for spur avoidance on/off mode, + no on2 support for 43228A0 */ + if (spuravoid == 1) { + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, + 0x01100014); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1, + 0x040C0C06); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, + 0x03140A08); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, + 0x00333333); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4, + 0x202C2820); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5, + 0x88888815); + } else { + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL0, + 0x11100014); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL1, + 0x040c0c06); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL2, + 0x03000a08); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL3, + 0x00000000); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL4, + 0x200005c0); + bcma_pmu_spuravoid_pll_write(cc, BCMA_CC_PMU_PLL_CTL5, + 0x88888815); + } + tmp = 1 << 10; + break; + default: + pr_err("unknown spuravoidance settings for chip 0x%04X, not changing PLL\n", + bus->chipinfo.id); + break; + } + + tmp |= bcma_cc_read32(cc, BCMA_CC_PMU_CTL); + bcma_cc_write32(cc, BCMA_CC_PMU_CTL, tmp); +} +EXPORT_SYMBOL_GPL(bcma_pmu_spuravoid_pllupdate); diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index 09f31ade1410..12975eac403f 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -308,6 +308,19 @@ #define BCMA_CC_PPL_PCHI_OFF 5 #define BCMA_CC_PPL_PCHI_MASK 0x0000003f +#define BCMA_CC_PMU_PLL_CTL0 0 +#define BCMA_CC_PMU_PLL_CTL1 1 +#define BCMA_CC_PMU_PLL_CTL2 2 +#define BCMA_CC_PMU_PLL_CTL3 3 +#define BCMA_CC_PMU_PLL_CTL4 4 +#define BCMA_CC_PMU_PLL_CTL5 5 + +#define BCMA_CC_PMU1_PLL0_PC0_P1DIV_MASK 0x00f00000 +#define BCMA_CC_PMU1_PLL0_PC0_P1DIV_SHIFT 20 + +#define BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_MASK 0x1ff00000 +#define BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_SHIFT 20 + /* BCM4331 ChipControl numbers. */ #define BCMA_CHIPCTL_4331_BT_COEXIST BIT(0) /* 0 disable */ #define BCMA_CHIPCTL_4331_SECI BIT(1) /* 0 SECI is disabled (JATG functional) */ @@ -420,5 +433,6 @@ extern void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, u32 set); extern void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, u32 set); +extern void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid); #endif /* LINUX_BCMA_DRIVER_CC_H_ */ -- cgit v1.2.3-59-g8ed1b From 01d719a2287ec34f631800d10f1fad3c134c3e89 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 4 Jul 2012 00:14:04 +0200 Subject: NFC: Add ISO 14443 type B protocol Some devices (e.g. Sony's PaSoRi) can not do type B polling, so we have to make a distinction between ISO14443 type A and B poll modes. Cc: Eric Lapuyade Cc: Ilan Elias Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcwilink.c | 7 ++++--- drivers/nfc/pn533.c | 12 +++++++----- drivers/nfc/pn544_hci.c | 1 + include/linux/nfc.h | 14 ++++++++------ net/nfc/hci/core.c | 2 +- net/nfc/nci/core.c | 5 +++-- net/nfc/nci/ntf.c | 5 ++++- 7 files changed, 28 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c index 1f74a77d040d..e7fd4938f9bc 100644 --- a/drivers/nfc/nfcwilink.c +++ b/drivers/nfc/nfcwilink.c @@ -535,9 +535,10 @@ static int nfcwilink_probe(struct platform_device *pdev) drv->pdev = pdev; protocols = NFC_PROTO_JEWEL_MASK - | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK - | NFC_PROTO_ISO14443_MASK - | NFC_PROTO_NFC_DEP_MASK; + | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK + | NFC_PROTO_ISO14443_MASK + | NFC_PROTO_ISO14443_B_MASK + | NFC_PROTO_NFC_DEP_MASK; drv->ndev = nci_allocate_device(&nfcwilink_ops, protocols, diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 84d8175db818..d606f52fec84 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -49,13 +49,15 @@ #define PN533_DEVICE_STD 0x1 #define PN533_DEVICE_PASORI 0x2 -#define PN533_ALL_PROTOCOLS (NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK \ - | NFC_PROTO_FELICA_MASK | NFC_PROTO_ISO14443_MASK \ - | NFC_PROTO_NFC_DEP_MASK) +#define PN533_ALL_PROTOCOLS (NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK |\ + NFC_PROTO_FELICA_MASK | NFC_PROTO_ISO14443_MASK |\ + NFC_PROTO_NFC_DEP_MASK |\ + NFC_PROTO_ISO14443_B_MASK) #define PN533_NO_TYPE_B_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ NFC_PROTO_MIFARE_MASK | \ NFC_PROTO_FELICA_MASK | \ + NFC_PROTO_ISO14443_MASK | \ NFC_PROTO_NFC_DEP_MASK) static const struct usb_device_id pn533_table[] = { @@ -987,7 +989,7 @@ static int pn533_target_found_type_b(struct nfc_target *nfc_tgt, u8 *tgt_data, if (!pn533_target_type_b_is_valid(tgt_type_b, tgt_data_len)) return -EPROTO; - nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_MASK; + nfc_tgt->supported_protocols = NFC_PROTO_ISO14443_B_MASK; return 0; } @@ -1094,7 +1096,7 @@ static void pn533_poll_create_mod_list(struct pn533 *dev, if (im_protocols & NFC_PROTO_JEWEL_MASK) pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_JEWEL); - if (im_protocols & NFC_PROTO_ISO14443_MASK) + if (im_protocols & NFC_PROTO_ISO14443_B_MASK) pn533_poll_add_mod(dev, PN533_POLL_MOD_847KBPS_B); if (tm_protocols) diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index c67b55e224e0..aa71807189ba 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -869,6 +869,7 @@ static int __devinit pn544_hci_probe(struct i2c_client *client, NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK | NFC_PROTO_ISO14443_MASK | + NFC_PROTO_ISO14443_B_MASK | NFC_PROTO_NFC_DEP_MASK; info->shdlc = nfc_shdlc_allocate(&pn544_shdlc_ops, diff --git a/include/linux/nfc.h b/include/linux/nfc.h index f4e6dd915b1c..6189f27e305b 100644 --- a/include/linux/nfc.h +++ b/include/linux/nfc.h @@ -136,8 +136,9 @@ enum nfc_attrs { #define NFC_PROTO_FELICA 3 #define NFC_PROTO_ISO14443 4 #define NFC_PROTO_NFC_DEP 5 +#define NFC_PROTO_ISO14443_B 6 -#define NFC_PROTO_MAX 6 +#define NFC_PROTO_MAX 7 /* NFC communication modes */ #define NFC_COMM_ACTIVE 0 @@ -149,11 +150,12 @@ enum nfc_attrs { #define NFC_RF_NONE 2 /* NFC protocols masks used in bitsets */ -#define NFC_PROTO_JEWEL_MASK (1 << NFC_PROTO_JEWEL) -#define NFC_PROTO_MIFARE_MASK (1 << NFC_PROTO_MIFARE) -#define NFC_PROTO_FELICA_MASK (1 << NFC_PROTO_FELICA) -#define NFC_PROTO_ISO14443_MASK (1 << NFC_PROTO_ISO14443) -#define NFC_PROTO_NFC_DEP_MASK (1 << NFC_PROTO_NFC_DEP) +#define NFC_PROTO_JEWEL_MASK (1 << NFC_PROTO_JEWEL) +#define NFC_PROTO_MIFARE_MASK (1 << NFC_PROTO_MIFARE) +#define NFC_PROTO_FELICA_MASK (1 << NFC_PROTO_FELICA) +#define NFC_PROTO_ISO14443_MASK (1 << NFC_PROTO_ISO14443) +#define NFC_PROTO_NFC_DEP_MASK (1 << NFC_PROTO_NFC_DEP) +#define NFC_PROTO_ISO14443_B_MASK (1 << NFC_PROTO_ISO14443_B) struct sockaddr_nfc { sa_family_t sa_family; diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 4ccc518f56eb..36717cebfbb6 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -230,7 +230,7 @@ static int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate) } break; case NFC_HCI_RF_READER_B_GATE: - targets->supported_protocols = NFC_PROTO_ISO14443_MASK; + targets->supported_protocols = NFC_PROTO_ISO14443_B_MASK; break; default: if (hdev->ops->target_from_gate) diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 766a02b1dfa1..5bb4da680427 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -194,7 +194,7 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) } if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && - (protocols & NFC_PROTO_ISO14443_MASK)) { + (protocols & NFC_PROTO_ISO14443_B_MASK)) { cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = NCI_NFC_B_PASSIVE_POLL_MODE; cmd.disc_configs[cmd.num_disc_configs].frequency = 1; @@ -486,7 +486,8 @@ static int nci_activate_target(struct nfc_dev *nfc_dev, param.rf_protocol = NCI_RF_PROTOCOL_T2T; else if (protocol == NFC_PROTO_FELICA) param.rf_protocol = NCI_RF_PROTOCOL_T3T; - else if (protocol == NFC_PROTO_ISO14443) + else if (protocol == NFC_PROTO_ISO14443 || + protocol == NFC_PROTO_ISO14443_B) param.rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; else param.rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 2ab196a9f228..af7a93b04393 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -170,7 +170,10 @@ static int nci_add_new_protocol(struct nci_dev *ndev, if (rf_protocol == NCI_RF_PROTOCOL_T2T) protocol = NFC_PROTO_MIFARE_MASK; else if (rf_protocol == NCI_RF_PROTOCOL_ISO_DEP) - protocol = NFC_PROTO_ISO14443_MASK; + if (rf_tech_and_mode == NCI_NFC_A_PASSIVE_POLL_MODE) + protocol = NFC_PROTO_ISO14443_MASK; + else + protocol = NFC_PROTO_ISO14443_B_MASK; else if (rf_protocol == NCI_RF_PROTOCOL_T3T) protocol = NFC_PROTO_FELICA_MASK; else -- cgit v1.2.3-59-g8ed1b From 2b28ae1912e5ce5bb0527e352ae6ff04e76183d1 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 9 Jul 2012 13:38:57 -0600 Subject: PCI: reimplement P2P bridge 1K I/O windows (Intel P64H2) 9d265124d051 and 15a260d53f7c added quirks for P2P bridges that support I/O windows that start/end at 1K boundaries, not just the 4K boundaries defined by the PCI spec. For details, see the IOBL_ADR register and the EN1K bit in the CNF register in the Intel 82870P2 (P64H2). These quirks complicate the code that reads P2P bridge windows (pci_read_bridge_io() and pci_cfg_fake_ranges()) because the bridge I/O resource is updated in the HEADER quirk, in pci_read_bridge_io(), in pci_setup_bridge(), and again in the FINAL quirk. This is confusing and makes it impossible to reassign the bridge windows after FINAL quirks are run. This patch adds support for 1K windows in the generic paths, so the HEADER quirk only has to enable this support. The FINAL quirk, which used to undo damage done by pci_setup_bridge(), is no longer needed. This removes "if (!res->start) res->start = ..." from pci_read_bridge_io(); that was part of 9d265124d051 to avoid overwriting the resource filled in by the quirk. Since pci_read_bridge_io() itself now knows about granularity, the quirk no longer updates the resource and this test is no longer needed. Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 25 ++++++++++++++----------- drivers/pci/quirks.c | 39 +-------------------------------------- drivers/pci/setup-bus.c | 11 +++++++++-- include/linux/pci.h | 1 + include/linux/pci_regs.h | 3 ++- 5 files changed, 27 insertions(+), 52 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 9c5d2a992999..ef24cf765b2f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -269,15 +269,23 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) { struct pci_dev *dev = child->self; u8 io_base_lo, io_limit_lo; - unsigned long base, limit; + unsigned long io_mask, io_granularity, base, limit; struct pci_bus_region region; - struct resource *res, res2; + struct resource *res; + + io_mask = PCI_IO_RANGE_MASK; + io_granularity = 0x1000; + if (dev->io_window_1k) { + /* Support 1K I/O space granularity */ + io_mask = PCI_IO_1K_RANGE_MASK; + io_granularity = 0x400; + } res = child->resource[0]; pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo); - base = (io_base_lo & PCI_IO_RANGE_MASK) << 8; - limit = (io_limit_lo & PCI_IO_RANGE_MASK) << 8; + base = (io_base_lo & io_mask) << 8; + limit = (io_limit_lo & io_mask) << 8; if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { u16 io_base_hi, io_limit_hi; @@ -289,14 +297,9 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) if (base <= limit) { res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; - res2.flags = res->flags; region.start = base; - region.end = limit + 0xfff; - pcibios_bus_to_resource(dev, &res2, ®ion); - if (!res->start) - res->start = res2.start; - if (!res->end) - res->end = res2.end; + region.end = limit + io_granularity - 1; + pcibios_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 2a7521677541..356846bd7ffb 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1938,53 +1938,16 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C810, fixup_rev1 static void __devinit quirk_p64h2_1k_io(struct pci_dev *dev) { u16 en1k; - u8 io_base_lo, io_limit_lo; - unsigned long base, limit; - struct resource *res = dev->resource + PCI_BRIDGE_RESOURCES; pci_read_config_word(dev, 0x40, &en1k); if (en1k & 0x200) { dev_info(&dev->dev, "Enable I/O Space to 1KB granularity\n"); - - pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); - pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo); - base = (io_base_lo & (PCI_IO_RANGE_MASK | 0x0c)) << 8; - limit = (io_limit_lo & (PCI_IO_RANGE_MASK | 0x0c)) << 8; - - if (base <= limit) { - res->start = base; - res->end = limit + 0x3ff; - } + dev->io_window_1k = 1; } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1460, quirk_p64h2_1k_io); -/* Fix the IOBL_ADR for 1k I/O space granularity on the Intel P64H2 - * The IOBL_ADR gets re-written to 4k boundaries in pci_setup_bridge() - * in drivers/pci/setup-bus.c - */ -static void __devinit quirk_p64h2_1k_io_fix_iobl(struct pci_dev *dev) -{ - u16 en1k, iobl_adr, iobl_adr_1k; - struct resource *res = dev->resource + PCI_BRIDGE_RESOURCES; - - pci_read_config_word(dev, 0x40, &en1k); - - if (en1k & 0x200) { - pci_read_config_word(dev, PCI_IO_BASE, &iobl_adr); - - iobl_adr_1k = iobl_adr | (res->start >> 8) | (res->end & 0xfc00); - - if (iobl_adr != iobl_adr_1k) { - dev_info(&dev->dev, "Fixing P64H2 IOBL_ADR from 0x%x to 0x%x for 1KB granularity\n", - iobl_adr,iobl_adr_1k); - pci_write_config_word(dev, PCI_IO_BASE, iobl_adr_1k); - } - } -} -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1460, quirk_p64h2_1k_io_fix_iobl); - /* Under some circumstances, AER is not linked with extended capabilities. * Force it to be linked by setting the corresponding control bit in the * config space. diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 8fa2d4be88de..dad5425f1f09 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -469,16 +469,23 @@ static void pci_setup_bridge_io(struct pci_bus *bus) struct pci_dev *bridge = bus->self; struct resource *res; struct pci_bus_region region; + unsigned long io_mask; + u8 io_base_lo, io_limit_lo; u32 l, io_upper16; + io_mask = PCI_IO_RANGE_MASK; + if (bridge->io_window_1k) + io_mask = PCI_IO_1K_RANGE_MASK; + /* Set up the top and bottom of the PCI I/O segment for this bus. */ res = bus->resource[0]; pcibios_resource_to_bus(bridge, ®ion, res); if (res->flags & IORESOURCE_IO) { pci_read_config_dword(bridge, PCI_IO_BASE, &l); l &= 0xffff0000; - l |= (region.start >> 8) & 0x00f0; - l |= region.end & 0xf000; + io_base_lo = (region.start >> 8) & io_mask; + io_limit_lo = (region.end >> 8) & io_mask; + l |= ((u32) io_limit_lo << 8) | io_base_lo; /* Set up upper 16 bits of I/O base/limit. */ io_upper16 = (region.end & 0xffff0000) | (region.start >> 16); dev_info(&bridge->dev, " bridge window %pR\n", res); diff --git a/include/linux/pci.h b/include/linux/pci.h index d8c379dba6ad..89b46fd245c6 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -324,6 +324,7 @@ struct pci_dev { unsigned int is_hotplug_bridge:1; unsigned int __aer_firmware_first_valid:1; unsigned int __aer_firmware_first:1; + unsigned int io_window_1k:1; /* Intel P2P bridge 1K I/O windows */ pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 4b608f543412..88c9ea56e252 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -125,7 +125,8 @@ #define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ #define PCI_IO_RANGE_TYPE_16 0x00 #define PCI_IO_RANGE_TYPE_32 0x01 -#define PCI_IO_RANGE_MASK (~0x0fUL) +#define PCI_IO_RANGE_MASK (~0x0fUL) /* Standard 4K I/O windows */ +#define PCI_IO_1K_RANGE_MASK (~0x03UL) /* Intel 1K I/O windows */ #define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ #define PCI_MEMORY_BASE 0x20 /* Memory range behind */ #define PCI_MEMORY_LIMIT 0x22 -- cgit v1.2.3-59-g8ed1b From afae021abeadc58aec5074f26a1d62912773edf7 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 10 Jul 2012 11:49:30 +0900 Subject: sh: pfc: Shuffle PFC support core. This follows the intc/clk changes and shuffles the PFC support code under its own directory. This will facilitate better code sharing, and allow us to trim down the exported interface by quite a margin. Signed-off-by: Paul Mundt --- drivers/sh/Kconfig | 16 +- drivers/sh/Makefile | 4 +- drivers/sh/pfc-gpio.c | 309 -------------------------- drivers/sh/pfc.c | 578 ------------------------------------------------ drivers/sh/pfc/Kconfig | 14 ++ drivers/sh/pfc/Makefile | 2 + drivers/sh/pfc/core.c | 578 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/sh/pfc/gpio.c | 309 ++++++++++++++++++++++++++ include/linux/sh_pfc.h | 4 +- 9 files changed, 908 insertions(+), 906 deletions(-) delete mode 100644 drivers/sh/pfc-gpio.c delete mode 100644 drivers/sh/pfc.c create mode 100644 drivers/sh/pfc/Kconfig create mode 100644 drivers/sh/pfc/Makefile create mode 100644 drivers/sh/pfc/core.c create mode 100644 drivers/sh/pfc/gpio.c (limited to 'include/linux') diff --git a/drivers/sh/Kconfig b/drivers/sh/Kconfig index d7dbfee1bc70..d860ef743568 100644 --- a/drivers/sh/Kconfig +++ b/drivers/sh/Kconfig @@ -1,20 +1,6 @@ menu "SuperH / SH-Mobile Driver Options" source "drivers/sh/intc/Kconfig" - -comment "Pin function controller options" - -config SH_PFC - # XXX move off the gpio dependency - depends on GENERIC_GPIO - select GPIO_SH_PFC if ARCH_REQUIRE_GPIOLIB - def_bool y - -config GPIO_SH_PFC - tristate "SuperH PFC GPIO support" - depends on SH_PFC && GPIOLIB - help - This enables support for GPIOs within the SoC's pin function - controller. +source "drivers/sh/pfc/Kconfig" endmenu diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index f5d93e8de090..e57895b1a425 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -5,7 +5,7 @@ obj-y := intc/ obj-$(CONFIG_HAVE_CLK) += clk/ obj-$(CONFIG_MAPLE) += maple/ +obj-$(CONFIG_SH_PFC) += pfc/ obj-$(CONFIG_SUPERHYWAY) += superhyway/ -obj-$(CONFIG_SH_PFC) += pfc.o -obj-$(CONFIG_GPIO_SH_PFC) += pfc-gpio.o + obj-y += pm_runtime.o diff --git a/drivers/sh/pfc-gpio.c b/drivers/sh/pfc-gpio.c deleted file mode 100644 index d74e5a96024b..000000000000 --- a/drivers/sh/pfc-gpio.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * SuperH Pin Function Controller GPIO driver. - * - * Copyright (C) 2008 Magnus Damm - * Copyright (C) 2009 - 2012 Paul Mundt - * - * 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. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include - -struct sh_pfc_chip { - struct sh_pfc *pfc; - struct gpio_chip gpio_chip; -}; - -static struct sh_pfc_chip *gpio_to_pfc_chip(struct gpio_chip *gc) -{ - return container_of(gc, struct sh_pfc_chip, gpio_chip); -} - -static struct sh_pfc *gpio_to_pfc(struct gpio_chip *gc) -{ - return gpio_to_pfc_chip(gc)->pfc; -} - -static int sh_gpio_request(struct gpio_chip *gc, unsigned offset) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - struct pinmux_data_reg *dummy; - unsigned long flags; - int i, ret, pinmux_type; - - ret = -EINVAL; - - if (!pfc) - goto err_out; - - spin_lock_irqsave(&pfc->lock, flags); - - if ((pfc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) - goto err_unlock; - - /* setup pin function here if no data is associated with pin */ - - if (sh_pfc_get_data_reg(pfc, offset, &dummy, &i) != 0) - pinmux_type = PINMUX_TYPE_FUNCTION; - else - pinmux_type = PINMUX_TYPE_GPIO; - - if (pinmux_type == PINMUX_TYPE_FUNCTION) { - if (sh_pfc_config_gpio(pfc, offset, - pinmux_type, - GPIO_CFG_DRYRUN) != 0) - goto err_unlock; - - if (sh_pfc_config_gpio(pfc, offset, - pinmux_type, - GPIO_CFG_REQ) != 0) - BUG(); - } - - pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[offset].flags |= pinmux_type; - - ret = 0; - err_unlock: - spin_unlock_irqrestore(&pfc->lock, flags); - err_out: - return ret; -} - -static void sh_gpio_free(struct gpio_chip *gc, unsigned offset) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - unsigned long flags; - int pinmux_type; - - if (!pfc) - return; - - spin_lock_irqsave(&pfc->lock, flags); - - pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; - sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); - pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[offset].flags |= PINMUX_TYPE_NONE; - - spin_unlock_irqrestore(&pfc->lock, flags); -} - -static int sh_gpio_direction_input(struct gpio_chip *gc, unsigned offset) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - unsigned long flags; - int ret; - - spin_lock_irqsave(&pfc->lock, flags); - ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_INPUT); - spin_unlock_irqrestore(&pfc->lock, flags); - - return ret; -} - -static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) -{ - struct pinmux_data_reg *dr = NULL; - int bit = 0; - - if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) - BUG(); - else - sh_pfc_write_bit(dr, bit, value); -} - -static int sh_gpio_direction_output(struct gpio_chip *gc, unsigned offset, - int value) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - unsigned long flags; - int ret; - - sh_gpio_set_value(pfc, offset, value); - - spin_lock_irqsave(&pfc->lock, flags); - ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_OUTPUT); - spin_unlock_irqrestore(&pfc->lock, flags); - - return ret; -} - -static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) -{ - struct pinmux_data_reg *dr = NULL; - int bit = 0; - - if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) - return -EINVAL; - - return sh_pfc_read_bit(dr, bit); -} - -static int sh_gpio_get(struct gpio_chip *gc, unsigned offset) -{ - return sh_gpio_get_value(gpio_to_pfc(gc), offset); -} - -static void sh_gpio_set(struct gpio_chip *gc, unsigned offset, int value) -{ - sh_gpio_set_value(gpio_to_pfc(gc), offset, value); -} - -static int sh_gpio_to_irq(struct gpio_chip *gc, unsigned offset) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - pinmux_enum_t enum_id; - pinmux_enum_t *enum_ids; - int i, k, pos; - - pos = 0; - enum_id = 0; - while (1) { - pos = sh_pfc_gpio_to_enum(pfc, offset, pos, &enum_id); - if (pos <= 0 || !enum_id) - break; - - for (i = 0; i < pfc->gpio_irq_size; i++) { - enum_ids = pfc->gpio_irq[i].enum_ids; - for (k = 0; enum_ids[k]; k++) { - if (enum_ids[k] == enum_id) - return pfc->gpio_irq[i].irq; - } - } - } - - return -ENOSYS; -} - -static void sh_pfc_gpio_setup(struct sh_pfc_chip *chip) -{ - struct sh_pfc *pfc = chip->pfc; - struct gpio_chip *gc = &chip->gpio_chip; - - gc->request = sh_gpio_request; - gc->free = sh_gpio_free; - gc->direction_input = sh_gpio_direction_input; - gc->get = sh_gpio_get; - gc->direction_output = sh_gpio_direction_output; - gc->set = sh_gpio_set; - gc->to_irq = sh_gpio_to_irq; - - WARN_ON(pfc->first_gpio != 0); /* needs testing */ - - gc->label = pfc->name; - gc->owner = THIS_MODULE; - gc->base = pfc->first_gpio; - gc->ngpio = (pfc->last_gpio - pfc->first_gpio) + 1; -} - -int sh_pfc_register_gpiochip(struct sh_pfc *pfc) -{ - struct sh_pfc_chip *chip; - int ret; - - chip = kzalloc(sizeof(struct sh_pfc_chip), GFP_KERNEL); - if (unlikely(!chip)) - return -ENOMEM; - - chip->pfc = pfc; - - sh_pfc_gpio_setup(chip); - - ret = gpiochip_add(&chip->gpio_chip); - if (unlikely(ret < 0)) - kfree(chip); - - pr_info("%s handling gpio %d -> %d\n", - pfc->name, pfc->first_gpio, pfc->last_gpio); - - return ret; -} -EXPORT_SYMBOL_GPL(sh_pfc_register_gpiochip); - -static int sh_pfc_gpio_match(struct gpio_chip *gc, void *data) -{ - return !!strstr(gc->label, data); -} - -static int __devinit sh_pfc_gpio_probe(struct platform_device *pdev) -{ - struct sh_pfc_chip *chip; - struct gpio_chip *gc; - - gc = gpiochip_find("_pfc", sh_pfc_gpio_match); - if (unlikely(!gc)) { - pr_err("Cant find gpio chip\n"); - return -ENODEV; - } - - chip = gpio_to_pfc_chip(gc); - platform_set_drvdata(pdev, chip); - - pr_info("attaching to GPIO chip %s\n", chip->pfc->name); - - return 0; -} - -static int __devexit sh_pfc_gpio_remove(struct platform_device *pdev) -{ - struct sh_pfc_chip *chip = platform_get_drvdata(pdev); - int ret; - - ret = gpiochip_remove(&chip->gpio_chip); - if (unlikely(ret < 0)) - return ret; - - kfree(chip); - return 0; -} - -static struct platform_driver sh_pfc_gpio_driver = { - .probe = sh_pfc_gpio_probe, - .remove = __devexit_p(sh_pfc_gpio_remove), - .driver = { - .name = KBUILD_MODNAME, - .owner = THIS_MODULE, - }, -}; - -static struct platform_device sh_pfc_gpio_device = { - .name = KBUILD_MODNAME, - .id = -1, -}; - -static int __init sh_pfc_gpio_init(void) -{ - int rc; - - rc = platform_driver_register(&sh_pfc_gpio_driver); - if (likely(!rc)) { - rc = platform_device_register(&sh_pfc_gpio_device); - if (unlikely(rc)) - platform_driver_unregister(&sh_pfc_gpio_driver); - } - - return rc; -} - -static void __exit sh_pfc_gpio_exit(void) -{ - platform_device_unregister(&sh_pfc_gpio_device); - platform_driver_unregister(&sh_pfc_gpio_driver); -} - -module_init(sh_pfc_gpio_init); -module_exit(sh_pfc_gpio_exit); - -MODULE_AUTHOR("Magnus Damm, Paul Mundt"); -MODULE_DESCRIPTION("GPIO driver for SuperH pin function controller"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:pfc-gpio"); diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c deleted file mode 100644 index ce4579ebd602..000000000000 --- a/drivers/sh/pfc.c +++ /dev/null @@ -1,578 +0,0 @@ -/* - * SuperH Pin Function Controller support. - * - * Copyright (C) 2008 Magnus Damm - * Copyright (C) 2009 - 2012 Paul Mundt - * - * 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. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct sh_pfc *sh_pfc __read_mostly; - -static inline bool sh_pfc_initialized(void) -{ - return !!sh_pfc; -} - -static void pfc_iounmap(struct sh_pfc *pfc) -{ - int k; - - for (k = 0; k < pfc->num_resources; k++) - if (pfc->window[k].virt) - iounmap(pfc->window[k].virt); - - kfree(pfc->window); - pfc->window = NULL; -} - -static int pfc_ioremap(struct sh_pfc *pfc) -{ - struct resource *res; - int k; - - if (!pfc->num_resources) - return 0; - - pfc->window = kzalloc(pfc->num_resources * sizeof(*pfc->window), - GFP_NOWAIT); - if (!pfc->window) - goto err1; - - for (k = 0; k < pfc->num_resources; k++) { - res = pfc->resource + k; - WARN_ON(resource_type(res) != IORESOURCE_MEM); - pfc->window[k].phys = res->start; - pfc->window[k].size = resource_size(res); - pfc->window[k].virt = ioremap_nocache(res->start, - resource_size(res)); - if (!pfc->window[k].virt) - goto err2; - } - - return 0; - -err2: - pfc_iounmap(pfc); -err1: - return -1; -} - -static void __iomem *pfc_phys_to_virt(struct sh_pfc *pfc, - unsigned long address) -{ - struct pfc_window *window; - int k; - - /* scan through physical windows and convert address */ - for (k = 0; k < pfc->num_resources; k++) { - window = pfc->window + k; - - if (address < window->phys) - continue; - - if (address >= (window->phys + window->size)) - continue; - - return window->virt + (address - window->phys); - } - - /* no windows defined, register must be 1:1 mapped virt:phys */ - return (void __iomem *)address; -} - -static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r) -{ - if (enum_id < r->begin) - return 0; - - if (enum_id > r->end) - return 0; - - return 1; -} - -static unsigned long gpio_read_raw_reg(void __iomem *mapped_reg, - unsigned long reg_width) -{ - switch (reg_width) { - case 8: - return ioread8(mapped_reg); - case 16: - return ioread16(mapped_reg); - case 32: - return ioread32(mapped_reg); - } - - BUG(); - return 0; -} - -static void gpio_write_raw_reg(void __iomem *mapped_reg, - unsigned long reg_width, - unsigned long data) -{ - switch (reg_width) { - case 8: - iowrite8(data, mapped_reg); - return; - case 16: - iowrite16(data, mapped_reg); - return; - case 32: - iowrite32(data, mapped_reg); - return; - } - - BUG(); -} - -int sh_pfc_read_bit(struct pinmux_data_reg *dr, unsigned long in_pos) -{ - unsigned long pos; - - pos = dr->reg_width - (in_pos + 1); - - pr_debug("read_bit: addr = %lx, pos = %ld, " - "r_width = %ld\n", dr->reg, pos, dr->reg_width); - - return (gpio_read_raw_reg(dr->mapped_reg, dr->reg_width) >> pos) & 1; -} -EXPORT_SYMBOL_GPL(sh_pfc_read_bit); - -void sh_pfc_write_bit(struct pinmux_data_reg *dr, unsigned long in_pos, - unsigned long value) -{ - unsigned long pos; - - pos = dr->reg_width - (in_pos + 1); - - pr_debug("write_bit addr = %lx, value = %d, pos = %ld, " - "r_width = %ld\n", - dr->reg, !!value, pos, dr->reg_width); - - if (value) - set_bit(pos, &dr->reg_shadow); - else - clear_bit(pos, &dr->reg_shadow); - - gpio_write_raw_reg(dr->mapped_reg, dr->reg_width, dr->reg_shadow); -} -EXPORT_SYMBOL_GPL(sh_pfc_write_bit); - -static void config_reg_helper(struct sh_pfc *pfc, - struct pinmux_cfg_reg *crp, - unsigned long in_pos, - void __iomem **mapped_regp, - unsigned long *maskp, - unsigned long *posp) -{ - int k; - - *mapped_regp = pfc_phys_to_virt(pfc, crp->reg); - - if (crp->field_width) { - *maskp = (1 << crp->field_width) - 1; - *posp = crp->reg_width - ((in_pos + 1) * crp->field_width); - } else { - *maskp = (1 << crp->var_field_width[in_pos]) - 1; - *posp = crp->reg_width; - for (k = 0; k <= in_pos; k++) - *posp -= crp->var_field_width[k]; - } -} - -static int read_config_reg(struct sh_pfc *pfc, - struct pinmux_cfg_reg *crp, - unsigned long field) -{ - void __iomem *mapped_reg; - unsigned long mask, pos; - - config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); - - pr_debug("read_reg: addr = %lx, field = %ld, " - "r_width = %ld, f_width = %ld\n", - crp->reg, field, crp->reg_width, crp->field_width); - - return (gpio_read_raw_reg(mapped_reg, crp->reg_width) >> pos) & mask; -} - -static void write_config_reg(struct sh_pfc *pfc, - struct pinmux_cfg_reg *crp, - unsigned long field, unsigned long value) -{ - void __iomem *mapped_reg; - unsigned long mask, pos, data; - - config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); - - pr_debug("write_reg addr = %lx, value = %ld, field = %ld, " - "r_width = %ld, f_width = %ld\n", - crp->reg, value, field, crp->reg_width, crp->field_width); - - mask = ~(mask << pos); - value = value << pos; - - data = gpio_read_raw_reg(mapped_reg, crp->reg_width); - data &= mask; - data |= value; - - if (pfc->unlock_reg) - gpio_write_raw_reg(pfc_phys_to_virt(pfc, pfc->unlock_reg), - 32, ~data); - - gpio_write_raw_reg(mapped_reg, crp->reg_width, data); -} - -static int setup_data_reg(struct sh_pfc *pfc, unsigned gpio) -{ - struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; - struct pinmux_data_reg *data_reg; - int k, n; - - if (!enum_in_range(gpiop->enum_id, &pfc->data)) - return -1; - - k = 0; - while (1) { - data_reg = pfc->data_regs + k; - - if (!data_reg->reg_width) - break; - - data_reg->mapped_reg = pfc_phys_to_virt(pfc, data_reg->reg); - - for (n = 0; n < data_reg->reg_width; n++) { - if (data_reg->enum_ids[n] == gpiop->enum_id) { - gpiop->flags &= ~PINMUX_FLAG_DREG; - gpiop->flags |= (k << PINMUX_FLAG_DREG_SHIFT); - gpiop->flags &= ~PINMUX_FLAG_DBIT; - gpiop->flags |= (n << PINMUX_FLAG_DBIT_SHIFT); - return 0; - } - } - k++; - } - - BUG(); - - return -1; -} - -static void setup_data_regs(struct sh_pfc *pfc) -{ - struct pinmux_data_reg *drp; - int k; - - for (k = pfc->first_gpio; k <= pfc->last_gpio; k++) - setup_data_reg(pfc, k); - - k = 0; - while (1) { - drp = pfc->data_regs + k; - - if (!drp->reg_width) - break; - - drp->reg_shadow = gpio_read_raw_reg(drp->mapped_reg, - drp->reg_width); - k++; - } -} - -int sh_pfc_get_data_reg(struct sh_pfc *pfc, unsigned gpio, - struct pinmux_data_reg **drp, int *bitp) -{ - struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; - int k, n; - - if (!enum_in_range(gpiop->enum_id, &pfc->data)) - return -1; - - k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT; - n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT; - *drp = pfc->data_regs + k; - *bitp = n; - return 0; -} -EXPORT_SYMBOL_GPL(sh_pfc_get_data_reg); - -static int get_config_reg(struct sh_pfc *pfc, pinmux_enum_t enum_id, - struct pinmux_cfg_reg **crp, - int *fieldp, int *valuep, - unsigned long **cntp) -{ - struct pinmux_cfg_reg *config_reg; - unsigned long r_width, f_width, curr_width, ncomb; - int k, m, n, pos, bit_pos; - - k = 0; - while (1) { - config_reg = pfc->cfg_regs + k; - - r_width = config_reg->reg_width; - f_width = config_reg->field_width; - - if (!r_width) - break; - - pos = 0; - m = 0; - for (bit_pos = 0; bit_pos < r_width; bit_pos += curr_width) { - if (f_width) - curr_width = f_width; - else - curr_width = config_reg->var_field_width[m]; - - ncomb = 1 << curr_width; - for (n = 0; n < ncomb; n++) { - if (config_reg->enum_ids[pos + n] == enum_id) { - *crp = config_reg; - *fieldp = m; - *valuep = n; - *cntp = &config_reg->cnt[m]; - return 0; - } - } - pos += ncomb; - m++; - } - k++; - } - - return -1; -} - -int sh_pfc_gpio_to_enum(struct sh_pfc *pfc, unsigned gpio, int pos, - pinmux_enum_t *enum_idp) -{ - pinmux_enum_t enum_id = pfc->gpios[gpio].enum_id; - pinmux_enum_t *data = pfc->gpio_data; - int k; - - if (!enum_in_range(enum_id, &pfc->data)) { - if (!enum_in_range(enum_id, &pfc->mark)) { - pr_err("non data/mark enum_id for gpio %d\n", gpio); - return -1; - } - } - - if (pos) { - *enum_idp = data[pos + 1]; - return pos + 1; - } - - for (k = 0; k < pfc->gpio_data_size; k++) { - if (data[k] == enum_id) { - *enum_idp = data[k + 1]; - return k + 1; - } - } - - pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio); - return -1; -} -EXPORT_SYMBOL_GPL(sh_pfc_gpio_to_enum); - -int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type, - int cfg_mode) -{ - struct pinmux_cfg_reg *cr = NULL; - pinmux_enum_t enum_id; - struct pinmux_range *range; - int in_range, pos, field, value; - unsigned long *cntp; - - switch (pinmux_type) { - - case PINMUX_TYPE_FUNCTION: - range = NULL; - break; - - case PINMUX_TYPE_OUTPUT: - range = &pfc->output; - break; - - case PINMUX_TYPE_INPUT: - range = &pfc->input; - break; - - case PINMUX_TYPE_INPUT_PULLUP: - range = &pfc->input_pu; - break; - - case PINMUX_TYPE_INPUT_PULLDOWN: - range = &pfc->input_pd; - break; - - default: - goto out_err; - } - - pos = 0; - enum_id = 0; - field = 0; - value = 0; - while (1) { - pos = sh_pfc_gpio_to_enum(pfc, gpio, pos, &enum_id); - if (pos <= 0) - goto out_err; - - if (!enum_id) - break; - - /* first check if this is a function enum */ - in_range = enum_in_range(enum_id, &pfc->function); - if (!in_range) { - /* not a function enum */ - if (range) { - /* - * other range exists, so this pin is - * a regular GPIO pin that now is being - * bound to a specific direction. - * - * for this case we only allow function enums - * and the enums that match the other range. - */ - in_range = enum_in_range(enum_id, range); - - /* - * special case pass through for fixed - * input-only or output-only pins without - * function enum register association. - */ - if (in_range && enum_id == range->force) - continue; - } else { - /* - * no other range exists, so this pin - * must then be of the function type. - * - * allow function type pins to select - * any combination of function/in/out - * in their MARK lists. - */ - in_range = 1; - } - } - - if (!in_range) - continue; - - if (get_config_reg(pfc, enum_id, &cr, - &field, &value, &cntp) != 0) - goto out_err; - - switch (cfg_mode) { - case GPIO_CFG_DRYRUN: - if (!*cntp || - (read_config_reg(pfc, cr, field) != value)) - continue; - break; - - case GPIO_CFG_REQ: - write_config_reg(pfc, cr, field, value); - *cntp = *cntp + 1; - break; - - case GPIO_CFG_FREE: - *cntp = *cntp - 1; - break; - } - } - - return 0; - out_err: - return -1; -} -EXPORT_SYMBOL_GPL(sh_pfc_config_gpio); - -int sh_pfc_set_direction(struct sh_pfc *pfc, unsigned gpio, - int new_pinmux_type) -{ - int pinmux_type; - int ret = -EINVAL; - - if (!pfc) - goto err_out; - - pinmux_type = pfc->gpios[gpio].flags & PINMUX_FLAG_TYPE; - - switch (pinmux_type) { - case PINMUX_TYPE_GPIO: - break; - case PINMUX_TYPE_OUTPUT: - case PINMUX_TYPE_INPUT: - case PINMUX_TYPE_INPUT_PULLUP: - case PINMUX_TYPE_INPUT_PULLDOWN: - sh_pfc_config_gpio(pfc, gpio, pinmux_type, GPIO_CFG_FREE); - break; - default: - goto err_out; - } - - if (sh_pfc_config_gpio(pfc, gpio, - new_pinmux_type, - GPIO_CFG_DRYRUN) != 0) - goto err_out; - - if (sh_pfc_config_gpio(pfc, gpio, - new_pinmux_type, - GPIO_CFG_REQ) != 0) - BUG(); - - pfc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[gpio].flags |= new_pinmux_type; - - ret = 0; - err_out: - return ret; -} -EXPORT_SYMBOL_GPL(sh_pfc_set_direction); - -int register_sh_pfc(struct sh_pfc *pfc) -{ - int (*initroutine)(struct sh_pfc *) = NULL; - int ret; - - /* - * Ensure that the type encoding fits - */ - BUILD_BUG_ON(PINMUX_FLAG_TYPE > ((1 << PINMUX_FLAG_DBIT_SHIFT) - 1)); - - if (sh_pfc) - return -EBUSY; - - ret = pfc_ioremap(pfc); - if (unlikely(ret < 0)) - return ret; - - spin_lock_init(&pfc->lock); - - setup_data_regs(pfc); - - sh_pfc = pfc; - pr_info("%s support registered\n", pfc->name); - - initroutine = symbol_request(sh_pfc_register_gpiochip); - if (initroutine) { - (*initroutine)(pfc); - symbol_put_addr(initroutine); - } - - return 0; -} diff --git a/drivers/sh/pfc/Kconfig b/drivers/sh/pfc/Kconfig new file mode 100644 index 000000000000..95b04f4edb88 --- /dev/null +++ b/drivers/sh/pfc/Kconfig @@ -0,0 +1,14 @@ +comment "Pin function controller options" + +config SH_PFC + # XXX move off the gpio dependency + depends on GENERIC_GPIO + select GPIO_SH_PFC if ARCH_REQUIRE_GPIOLIB + def_bool y + +config GPIO_SH_PFC + tristate "SuperH PFC GPIO support" + depends on SH_PFC && GPIOLIB + help + This enables support for GPIOs within the SoC's pin function + controller. diff --git a/drivers/sh/pfc/Makefile b/drivers/sh/pfc/Makefile new file mode 100644 index 000000000000..d81707744b27 --- /dev/null +++ b/drivers/sh/pfc/Makefile @@ -0,0 +1,2 @@ +obj-y += core.o +obj-$(CONFIG_GPIO_SH_PFC) += gpio.o diff --git a/drivers/sh/pfc/core.c b/drivers/sh/pfc/core.c new file mode 100644 index 000000000000..ce4579ebd602 --- /dev/null +++ b/drivers/sh/pfc/core.c @@ -0,0 +1,578 @@ +/* + * SuperH Pin Function Controller support. + * + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2009 - 2012 Paul Mundt + * + * 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. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct sh_pfc *sh_pfc __read_mostly; + +static inline bool sh_pfc_initialized(void) +{ + return !!sh_pfc; +} + +static void pfc_iounmap(struct sh_pfc *pfc) +{ + int k; + + for (k = 0; k < pfc->num_resources; k++) + if (pfc->window[k].virt) + iounmap(pfc->window[k].virt); + + kfree(pfc->window); + pfc->window = NULL; +} + +static int pfc_ioremap(struct sh_pfc *pfc) +{ + struct resource *res; + int k; + + if (!pfc->num_resources) + return 0; + + pfc->window = kzalloc(pfc->num_resources * sizeof(*pfc->window), + GFP_NOWAIT); + if (!pfc->window) + goto err1; + + for (k = 0; k < pfc->num_resources; k++) { + res = pfc->resource + k; + WARN_ON(resource_type(res) != IORESOURCE_MEM); + pfc->window[k].phys = res->start; + pfc->window[k].size = resource_size(res); + pfc->window[k].virt = ioremap_nocache(res->start, + resource_size(res)); + if (!pfc->window[k].virt) + goto err2; + } + + return 0; + +err2: + pfc_iounmap(pfc); +err1: + return -1; +} + +static void __iomem *pfc_phys_to_virt(struct sh_pfc *pfc, + unsigned long address) +{ + struct pfc_window *window; + int k; + + /* scan through physical windows and convert address */ + for (k = 0; k < pfc->num_resources; k++) { + window = pfc->window + k; + + if (address < window->phys) + continue; + + if (address >= (window->phys + window->size)) + continue; + + return window->virt + (address - window->phys); + } + + /* no windows defined, register must be 1:1 mapped virt:phys */ + return (void __iomem *)address; +} + +static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r) +{ + if (enum_id < r->begin) + return 0; + + if (enum_id > r->end) + return 0; + + return 1; +} + +static unsigned long gpio_read_raw_reg(void __iomem *mapped_reg, + unsigned long reg_width) +{ + switch (reg_width) { + case 8: + return ioread8(mapped_reg); + case 16: + return ioread16(mapped_reg); + case 32: + return ioread32(mapped_reg); + } + + BUG(); + return 0; +} + +static void gpio_write_raw_reg(void __iomem *mapped_reg, + unsigned long reg_width, + unsigned long data) +{ + switch (reg_width) { + case 8: + iowrite8(data, mapped_reg); + return; + case 16: + iowrite16(data, mapped_reg); + return; + case 32: + iowrite32(data, mapped_reg); + return; + } + + BUG(); +} + +int sh_pfc_read_bit(struct pinmux_data_reg *dr, unsigned long in_pos) +{ + unsigned long pos; + + pos = dr->reg_width - (in_pos + 1); + + pr_debug("read_bit: addr = %lx, pos = %ld, " + "r_width = %ld\n", dr->reg, pos, dr->reg_width); + + return (gpio_read_raw_reg(dr->mapped_reg, dr->reg_width) >> pos) & 1; +} +EXPORT_SYMBOL_GPL(sh_pfc_read_bit); + +void sh_pfc_write_bit(struct pinmux_data_reg *dr, unsigned long in_pos, + unsigned long value) +{ + unsigned long pos; + + pos = dr->reg_width - (in_pos + 1); + + pr_debug("write_bit addr = %lx, value = %d, pos = %ld, " + "r_width = %ld\n", + dr->reg, !!value, pos, dr->reg_width); + + if (value) + set_bit(pos, &dr->reg_shadow); + else + clear_bit(pos, &dr->reg_shadow); + + gpio_write_raw_reg(dr->mapped_reg, dr->reg_width, dr->reg_shadow); +} +EXPORT_SYMBOL_GPL(sh_pfc_write_bit); + +static void config_reg_helper(struct sh_pfc *pfc, + struct pinmux_cfg_reg *crp, + unsigned long in_pos, + void __iomem **mapped_regp, + unsigned long *maskp, + unsigned long *posp) +{ + int k; + + *mapped_regp = pfc_phys_to_virt(pfc, crp->reg); + + if (crp->field_width) { + *maskp = (1 << crp->field_width) - 1; + *posp = crp->reg_width - ((in_pos + 1) * crp->field_width); + } else { + *maskp = (1 << crp->var_field_width[in_pos]) - 1; + *posp = crp->reg_width; + for (k = 0; k <= in_pos; k++) + *posp -= crp->var_field_width[k]; + } +} + +static int read_config_reg(struct sh_pfc *pfc, + struct pinmux_cfg_reg *crp, + unsigned long field) +{ + void __iomem *mapped_reg; + unsigned long mask, pos; + + config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); + + pr_debug("read_reg: addr = %lx, field = %ld, " + "r_width = %ld, f_width = %ld\n", + crp->reg, field, crp->reg_width, crp->field_width); + + return (gpio_read_raw_reg(mapped_reg, crp->reg_width) >> pos) & mask; +} + +static void write_config_reg(struct sh_pfc *pfc, + struct pinmux_cfg_reg *crp, + unsigned long field, unsigned long value) +{ + void __iomem *mapped_reg; + unsigned long mask, pos, data; + + config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); + + pr_debug("write_reg addr = %lx, value = %ld, field = %ld, " + "r_width = %ld, f_width = %ld\n", + crp->reg, value, field, crp->reg_width, crp->field_width); + + mask = ~(mask << pos); + value = value << pos; + + data = gpio_read_raw_reg(mapped_reg, crp->reg_width); + data &= mask; + data |= value; + + if (pfc->unlock_reg) + gpio_write_raw_reg(pfc_phys_to_virt(pfc, pfc->unlock_reg), + 32, ~data); + + gpio_write_raw_reg(mapped_reg, crp->reg_width, data); +} + +static int setup_data_reg(struct sh_pfc *pfc, unsigned gpio) +{ + struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; + struct pinmux_data_reg *data_reg; + int k, n; + + if (!enum_in_range(gpiop->enum_id, &pfc->data)) + return -1; + + k = 0; + while (1) { + data_reg = pfc->data_regs + k; + + if (!data_reg->reg_width) + break; + + data_reg->mapped_reg = pfc_phys_to_virt(pfc, data_reg->reg); + + for (n = 0; n < data_reg->reg_width; n++) { + if (data_reg->enum_ids[n] == gpiop->enum_id) { + gpiop->flags &= ~PINMUX_FLAG_DREG; + gpiop->flags |= (k << PINMUX_FLAG_DREG_SHIFT); + gpiop->flags &= ~PINMUX_FLAG_DBIT; + gpiop->flags |= (n << PINMUX_FLAG_DBIT_SHIFT); + return 0; + } + } + k++; + } + + BUG(); + + return -1; +} + +static void setup_data_regs(struct sh_pfc *pfc) +{ + struct pinmux_data_reg *drp; + int k; + + for (k = pfc->first_gpio; k <= pfc->last_gpio; k++) + setup_data_reg(pfc, k); + + k = 0; + while (1) { + drp = pfc->data_regs + k; + + if (!drp->reg_width) + break; + + drp->reg_shadow = gpio_read_raw_reg(drp->mapped_reg, + drp->reg_width); + k++; + } +} + +int sh_pfc_get_data_reg(struct sh_pfc *pfc, unsigned gpio, + struct pinmux_data_reg **drp, int *bitp) +{ + struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; + int k, n; + + if (!enum_in_range(gpiop->enum_id, &pfc->data)) + return -1; + + k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT; + n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT; + *drp = pfc->data_regs + k; + *bitp = n; + return 0; +} +EXPORT_SYMBOL_GPL(sh_pfc_get_data_reg); + +static int get_config_reg(struct sh_pfc *pfc, pinmux_enum_t enum_id, + struct pinmux_cfg_reg **crp, + int *fieldp, int *valuep, + unsigned long **cntp) +{ + struct pinmux_cfg_reg *config_reg; + unsigned long r_width, f_width, curr_width, ncomb; + int k, m, n, pos, bit_pos; + + k = 0; + while (1) { + config_reg = pfc->cfg_regs + k; + + r_width = config_reg->reg_width; + f_width = config_reg->field_width; + + if (!r_width) + break; + + pos = 0; + m = 0; + for (bit_pos = 0; bit_pos < r_width; bit_pos += curr_width) { + if (f_width) + curr_width = f_width; + else + curr_width = config_reg->var_field_width[m]; + + ncomb = 1 << curr_width; + for (n = 0; n < ncomb; n++) { + if (config_reg->enum_ids[pos + n] == enum_id) { + *crp = config_reg; + *fieldp = m; + *valuep = n; + *cntp = &config_reg->cnt[m]; + return 0; + } + } + pos += ncomb; + m++; + } + k++; + } + + return -1; +} + +int sh_pfc_gpio_to_enum(struct sh_pfc *pfc, unsigned gpio, int pos, + pinmux_enum_t *enum_idp) +{ + pinmux_enum_t enum_id = pfc->gpios[gpio].enum_id; + pinmux_enum_t *data = pfc->gpio_data; + int k; + + if (!enum_in_range(enum_id, &pfc->data)) { + if (!enum_in_range(enum_id, &pfc->mark)) { + pr_err("non data/mark enum_id for gpio %d\n", gpio); + return -1; + } + } + + if (pos) { + *enum_idp = data[pos + 1]; + return pos + 1; + } + + for (k = 0; k < pfc->gpio_data_size; k++) { + if (data[k] == enum_id) { + *enum_idp = data[k + 1]; + return k + 1; + } + } + + pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio); + return -1; +} +EXPORT_SYMBOL_GPL(sh_pfc_gpio_to_enum); + +int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type, + int cfg_mode) +{ + struct pinmux_cfg_reg *cr = NULL; + pinmux_enum_t enum_id; + struct pinmux_range *range; + int in_range, pos, field, value; + unsigned long *cntp; + + switch (pinmux_type) { + + case PINMUX_TYPE_FUNCTION: + range = NULL; + break; + + case PINMUX_TYPE_OUTPUT: + range = &pfc->output; + break; + + case PINMUX_TYPE_INPUT: + range = &pfc->input; + break; + + case PINMUX_TYPE_INPUT_PULLUP: + range = &pfc->input_pu; + break; + + case PINMUX_TYPE_INPUT_PULLDOWN: + range = &pfc->input_pd; + break; + + default: + goto out_err; + } + + pos = 0; + enum_id = 0; + field = 0; + value = 0; + while (1) { + pos = sh_pfc_gpio_to_enum(pfc, gpio, pos, &enum_id); + if (pos <= 0) + goto out_err; + + if (!enum_id) + break; + + /* first check if this is a function enum */ + in_range = enum_in_range(enum_id, &pfc->function); + if (!in_range) { + /* not a function enum */ + if (range) { + /* + * other range exists, so this pin is + * a regular GPIO pin that now is being + * bound to a specific direction. + * + * for this case we only allow function enums + * and the enums that match the other range. + */ + in_range = enum_in_range(enum_id, range); + + /* + * special case pass through for fixed + * input-only or output-only pins without + * function enum register association. + */ + if (in_range && enum_id == range->force) + continue; + } else { + /* + * no other range exists, so this pin + * must then be of the function type. + * + * allow function type pins to select + * any combination of function/in/out + * in their MARK lists. + */ + in_range = 1; + } + } + + if (!in_range) + continue; + + if (get_config_reg(pfc, enum_id, &cr, + &field, &value, &cntp) != 0) + goto out_err; + + switch (cfg_mode) { + case GPIO_CFG_DRYRUN: + if (!*cntp || + (read_config_reg(pfc, cr, field) != value)) + continue; + break; + + case GPIO_CFG_REQ: + write_config_reg(pfc, cr, field, value); + *cntp = *cntp + 1; + break; + + case GPIO_CFG_FREE: + *cntp = *cntp - 1; + break; + } + } + + return 0; + out_err: + return -1; +} +EXPORT_SYMBOL_GPL(sh_pfc_config_gpio); + +int sh_pfc_set_direction(struct sh_pfc *pfc, unsigned gpio, + int new_pinmux_type) +{ + int pinmux_type; + int ret = -EINVAL; + + if (!pfc) + goto err_out; + + pinmux_type = pfc->gpios[gpio].flags & PINMUX_FLAG_TYPE; + + switch (pinmux_type) { + case PINMUX_TYPE_GPIO: + break; + case PINMUX_TYPE_OUTPUT: + case PINMUX_TYPE_INPUT: + case PINMUX_TYPE_INPUT_PULLUP: + case PINMUX_TYPE_INPUT_PULLDOWN: + sh_pfc_config_gpio(pfc, gpio, pinmux_type, GPIO_CFG_FREE); + break; + default: + goto err_out; + } + + if (sh_pfc_config_gpio(pfc, gpio, + new_pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err_out; + + if (sh_pfc_config_gpio(pfc, gpio, + new_pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + + pfc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[gpio].flags |= new_pinmux_type; + + ret = 0; + err_out: + return ret; +} +EXPORT_SYMBOL_GPL(sh_pfc_set_direction); + +int register_sh_pfc(struct sh_pfc *pfc) +{ + int (*initroutine)(struct sh_pfc *) = NULL; + int ret; + + /* + * Ensure that the type encoding fits + */ + BUILD_BUG_ON(PINMUX_FLAG_TYPE > ((1 << PINMUX_FLAG_DBIT_SHIFT) - 1)); + + if (sh_pfc) + return -EBUSY; + + ret = pfc_ioremap(pfc); + if (unlikely(ret < 0)) + return ret; + + spin_lock_init(&pfc->lock); + + setup_data_regs(pfc); + + sh_pfc = pfc; + pr_info("%s support registered\n", pfc->name); + + initroutine = symbol_request(sh_pfc_register_gpiochip); + if (initroutine) { + (*initroutine)(pfc); + symbol_put_addr(initroutine); + } + + return 0; +} diff --git a/drivers/sh/pfc/gpio.c b/drivers/sh/pfc/gpio.c new file mode 100644 index 000000000000..d74e5a96024b --- /dev/null +++ b/drivers/sh/pfc/gpio.c @@ -0,0 +1,309 @@ +/* + * SuperH Pin Function Controller GPIO driver. + * + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2009 - 2012 Paul Mundt + * + * 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. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +struct sh_pfc_chip { + struct sh_pfc *pfc; + struct gpio_chip gpio_chip; +}; + +static struct sh_pfc_chip *gpio_to_pfc_chip(struct gpio_chip *gc) +{ + return container_of(gc, struct sh_pfc_chip, gpio_chip); +} + +static struct sh_pfc *gpio_to_pfc(struct gpio_chip *gc) +{ + return gpio_to_pfc_chip(gc)->pfc; +} + +static int sh_gpio_request(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + struct pinmux_data_reg *dummy; + unsigned long flags; + int i, ret, pinmux_type; + + ret = -EINVAL; + + if (!pfc) + goto err_out; + + spin_lock_irqsave(&pfc->lock, flags); + + if ((pfc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) + goto err_unlock; + + /* setup pin function here if no data is associated with pin */ + + if (sh_pfc_get_data_reg(pfc, offset, &dummy, &i) != 0) + pinmux_type = PINMUX_TYPE_FUNCTION; + else + pinmux_type = PINMUX_TYPE_GPIO; + + if (pinmux_type == PINMUX_TYPE_FUNCTION) { + if (sh_pfc_config_gpio(pfc, offset, + pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err_unlock; + + if (sh_pfc_config_gpio(pfc, offset, + pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + } + + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= pinmux_type; + + ret = 0; + err_unlock: + spin_unlock_irqrestore(&pfc->lock, flags); + err_out: + return ret; +} + +static void sh_gpio_free(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned long flags; + int pinmux_type; + + if (!pfc) + return; + + spin_lock_irqsave(&pfc->lock, flags); + + pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; + sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= PINMUX_TYPE_NONE; + + spin_unlock_irqrestore(&pfc->lock, flags); +} + +static int sh_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned long flags; + int ret; + + spin_lock_irqsave(&pfc->lock, flags); + ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_INPUT); + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) + BUG(); + else + sh_pfc_write_bit(dr, bit, value); +} + +static int sh_gpio_direction_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned long flags; + int ret; + + sh_gpio_set_value(pfc, offset, value); + + spin_lock_irqsave(&pfc->lock, flags); + ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_OUTPUT); + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) + return -EINVAL; + + return sh_pfc_read_bit(dr, bit); +} + +static int sh_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + return sh_gpio_get_value(gpio_to_pfc(gc), offset); +} + +static void sh_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +{ + sh_gpio_set_value(gpio_to_pfc(gc), offset, value); +} + +static int sh_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + pinmux_enum_t enum_id; + pinmux_enum_t *enum_ids; + int i, k, pos; + + pos = 0; + enum_id = 0; + while (1) { + pos = sh_pfc_gpio_to_enum(pfc, offset, pos, &enum_id); + if (pos <= 0 || !enum_id) + break; + + for (i = 0; i < pfc->gpio_irq_size; i++) { + enum_ids = pfc->gpio_irq[i].enum_ids; + for (k = 0; enum_ids[k]; k++) { + if (enum_ids[k] == enum_id) + return pfc->gpio_irq[i].irq; + } + } + } + + return -ENOSYS; +} + +static void sh_pfc_gpio_setup(struct sh_pfc_chip *chip) +{ + struct sh_pfc *pfc = chip->pfc; + struct gpio_chip *gc = &chip->gpio_chip; + + gc->request = sh_gpio_request; + gc->free = sh_gpio_free; + gc->direction_input = sh_gpio_direction_input; + gc->get = sh_gpio_get; + gc->direction_output = sh_gpio_direction_output; + gc->set = sh_gpio_set; + gc->to_irq = sh_gpio_to_irq; + + WARN_ON(pfc->first_gpio != 0); /* needs testing */ + + gc->label = pfc->name; + gc->owner = THIS_MODULE; + gc->base = pfc->first_gpio; + gc->ngpio = (pfc->last_gpio - pfc->first_gpio) + 1; +} + +int sh_pfc_register_gpiochip(struct sh_pfc *pfc) +{ + struct sh_pfc_chip *chip; + int ret; + + chip = kzalloc(sizeof(struct sh_pfc_chip), GFP_KERNEL); + if (unlikely(!chip)) + return -ENOMEM; + + chip->pfc = pfc; + + sh_pfc_gpio_setup(chip); + + ret = gpiochip_add(&chip->gpio_chip); + if (unlikely(ret < 0)) + kfree(chip); + + pr_info("%s handling gpio %d -> %d\n", + pfc->name, pfc->first_gpio, pfc->last_gpio); + + return ret; +} +EXPORT_SYMBOL_GPL(sh_pfc_register_gpiochip); + +static int sh_pfc_gpio_match(struct gpio_chip *gc, void *data) +{ + return !!strstr(gc->label, data); +} + +static int __devinit sh_pfc_gpio_probe(struct platform_device *pdev) +{ + struct sh_pfc_chip *chip; + struct gpio_chip *gc; + + gc = gpiochip_find("_pfc", sh_pfc_gpio_match); + if (unlikely(!gc)) { + pr_err("Cant find gpio chip\n"); + return -ENODEV; + } + + chip = gpio_to_pfc_chip(gc); + platform_set_drvdata(pdev, chip); + + pr_info("attaching to GPIO chip %s\n", chip->pfc->name); + + return 0; +} + +static int __devexit sh_pfc_gpio_remove(struct platform_device *pdev) +{ + struct sh_pfc_chip *chip = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&chip->gpio_chip); + if (unlikely(ret < 0)) + return ret; + + kfree(chip); + return 0; +} + +static struct platform_driver sh_pfc_gpio_driver = { + .probe = sh_pfc_gpio_probe, + .remove = __devexit_p(sh_pfc_gpio_remove), + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, +}; + +static struct platform_device sh_pfc_gpio_device = { + .name = KBUILD_MODNAME, + .id = -1, +}; + +static int __init sh_pfc_gpio_init(void) +{ + int rc; + + rc = platform_driver_register(&sh_pfc_gpio_driver); + if (likely(!rc)) { + rc = platform_device_register(&sh_pfc_gpio_device); + if (unlikely(rc)) + platform_driver_unregister(&sh_pfc_gpio_driver); + } + + return rc; +} + +static void __exit sh_pfc_gpio_exit(void) +{ + platform_device_unregister(&sh_pfc_gpio_device); + platform_driver_unregister(&sh_pfc_gpio_driver); +} + +module_init(sh_pfc_gpio_init); +module_exit(sh_pfc_gpio_exit); + +MODULE_AUTHOR("Magnus Damm, Paul Mundt"); +MODULE_DESCRIPTION("GPIO driver for SuperH pin function controller"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:pfc-gpio"); diff --git a/include/linux/sh_pfc.h b/include/linux/sh_pfc.h index ed1d8234f6ae..f522550fc32b 100644 --- a/include/linux/sh_pfc.h +++ b/include/linux/sh_pfc.h @@ -127,10 +127,10 @@ struct sh_pfc { /* XXX compat for now */ #define pinmux_info sh_pfc -/* drivers/sh/pfc-gpio.c */ +/* drivers/sh/pfc/gpio.c */ int sh_pfc_register_gpiochip(struct sh_pfc *pfc); -/* drivers/sh/pfc.c */ +/* drivers/sh/pfc/core.c */ int register_sh_pfc(struct sh_pfc *pfc); int sh_pfc_read_bit(struct pinmux_data_reg *dr, unsigned long in_pos); -- cgit v1.2.3-59-g8ed1b From 72c7afa10f272710028f244da65d35e571144085 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 10 Jul 2012 11:59:29 +0900 Subject: sh: pfc: Dumb GPIO stringification. This implements fairly simplistic stringification of existing pinmux GPIOs for easy enum id -> string mapping, which will subsequently be used by the pinctrl support code. Signed-off-by: Paul Mundt --- include/linux/sh_pfc.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/sh_pfc.h b/include/linux/sh_pfc.h index f522550fc32b..8c4cbcb9064d 100644 --- a/include/linux/sh_pfc.h +++ b/include/linux/sh_pfc.h @@ -11,6 +11,7 @@ #ifndef __SH_PFC_H #define __SH_PFC_H +#include #include typedef unsigned short pinmux_enum_t; @@ -37,10 +38,11 @@ enum { struct pinmux_gpio { pinmux_enum_t enum_id; pinmux_flag_t flags; + const char *name; }; #define PINMUX_GPIO(gpio, data_or_mark) \ - [gpio] = { .enum_id = data_or_mark, .flags = PINMUX_TYPE_NONE } + [gpio] = { .name = __stringify(gpio), .enum_id = data_or_mark, .flags = PINMUX_TYPE_NONE } #define PINMUX_DATA(data_or_mark, ids...) data_or_mark, ids, 0 -- cgit v1.2.3-59-g8ed1b From ca5481c68e9fbcea62bb3c78ae6cccf99ca8fb73 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 10 Jul 2012 12:08:14 +0900 Subject: sh: pfc: Rudimentary pinctrl-backed GPIO support. This begins the migration of the PFC core to the pinctrl subsystem. Initial support is very basic, with the bulk of the implementation simply being nopped out in such a way to allow registration with the pinctrl core to succeed. The gpio chip driver is stripped down considerably now relying purely on pinctrl API calls to manage the bulk of its operations. This provides a basis for further PFC refactoring, including decoupling pin functions from the GPIO API, establishing pin groups, and so forth. These will all be dealt with incrementally so as to introduce as few growing and migratory pains to tree-wide PFC pinmux users today. When the interfaces have been well established and in-tree users have been migrated off of the legacy interfaces it will be possible to strip down the core considerably, leading to eventual drivers/pinctrl rehoming. Signed-off-by: Paul Mundt --- drivers/sh/pfc/Kconfig | 13 ++ drivers/sh/pfc/Makefile | 1 + drivers/sh/pfc/core.c | 81 +++++------ drivers/sh/pfc/gpio.c | 102 ++----------- drivers/sh/pfc/pinctrl.c | 371 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sh_pfc.h | 5 +- 6 files changed, 440 insertions(+), 133 deletions(-) create mode 100644 drivers/sh/pfc/pinctrl.c (limited to 'include/linux') diff --git a/drivers/sh/pfc/Kconfig b/drivers/sh/pfc/Kconfig index 95b04f4edb88..b743aaa543f8 100644 --- a/drivers/sh/pfc/Kconfig +++ b/drivers/sh/pfc/Kconfig @@ -4,8 +4,21 @@ config SH_PFC # XXX move off the gpio dependency depends on GENERIC_GPIO select GPIO_SH_PFC if ARCH_REQUIRE_GPIOLIB + select PINCTRL_SH_PFC def_bool y +# +# Placeholder for now, rehome to drivers/pinctrl once the PFC APIs +# have settled. +# +config PINCTRL_SH_PFC + tristate "SuperH PFC pin controller driver" + depends on SH_PFC + select PINCTRL + select PINMUX + select PINCONF + select GENERIC_PINCONF + config GPIO_SH_PFC tristate "SuperH PFC GPIO support" depends on SH_PFC && GPIOLIB diff --git a/drivers/sh/pfc/Makefile b/drivers/sh/pfc/Makefile index d81707744b27..7916027cce37 100644 --- a/drivers/sh/pfc/Makefile +++ b/drivers/sh/pfc/Makefile @@ -1,2 +1,3 @@ obj-y += core.o +obj-$(CONFIG_PINCTRL_SH_PFC) += pinctrl.o obj-$(CONFIG_GPIO_SH_PFC) += gpio.o diff --git a/drivers/sh/pfc/core.c b/drivers/sh/pfc/core.c index ce4579ebd602..02e9f62e2b28 100644 --- a/drivers/sh/pfc/core.c +++ b/drivers/sh/pfc/core.c @@ -19,6 +19,7 @@ #include #include #include +#include static struct sh_pfc *sh_pfc __read_mostly; @@ -501,49 +502,6 @@ int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type, } EXPORT_SYMBOL_GPL(sh_pfc_config_gpio); -int sh_pfc_set_direction(struct sh_pfc *pfc, unsigned gpio, - int new_pinmux_type) -{ - int pinmux_type; - int ret = -EINVAL; - - if (!pfc) - goto err_out; - - pinmux_type = pfc->gpios[gpio].flags & PINMUX_FLAG_TYPE; - - switch (pinmux_type) { - case PINMUX_TYPE_GPIO: - break; - case PINMUX_TYPE_OUTPUT: - case PINMUX_TYPE_INPUT: - case PINMUX_TYPE_INPUT_PULLUP: - case PINMUX_TYPE_INPUT_PULLDOWN: - sh_pfc_config_gpio(pfc, gpio, pinmux_type, GPIO_CFG_FREE); - break; - default: - goto err_out; - } - - if (sh_pfc_config_gpio(pfc, gpio, - new_pinmux_type, - GPIO_CFG_DRYRUN) != 0) - goto err_out; - - if (sh_pfc_config_gpio(pfc, gpio, - new_pinmux_type, - GPIO_CFG_REQ) != 0) - BUG(); - - pfc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[gpio].flags |= new_pinmux_type; - - ret = 0; - err_out: - return ret; -} -EXPORT_SYMBOL_GPL(sh_pfc_set_direction); - int register_sh_pfc(struct sh_pfc *pfc) { int (*initroutine)(struct sh_pfc *) = NULL; @@ -563,16 +521,49 @@ int register_sh_pfc(struct sh_pfc *pfc) spin_lock_init(&pfc->lock); + pinctrl_provide_dummies(); setup_data_regs(pfc); sh_pfc = pfc; - pr_info("%s support registered\n", pfc->name); + /* + * Initialize pinctrl bindings first + */ + initroutine = symbol_request(sh_pfc_register_pinctrl); + if (initroutine) { + ret = (*initroutine)(pfc); + symbol_put_addr(initroutine); + + if (unlikely(ret != 0)) + goto err; + } + + /* + * Then the GPIO chip + */ initroutine = symbol_request(sh_pfc_register_gpiochip); if (initroutine) { - (*initroutine)(pfc); + ret = (*initroutine)(pfc); symbol_put_addr(initroutine); + + /* + * If the GPIO chip fails to come up we still leave the + * PFC state as it is, given that there are already + * extant users of it that have succeeded by this point. + */ + if (unlikely(ret != 0)) { + pr_notice("failed to init GPIO chip, ignoring...\n"); + ret = 0; + } } + pr_info("%s support registered\n", pfc->name); + return 0; + +err: + pfc_iounmap(pfc); + sh_pfc = NULL; + + return ret; } diff --git a/drivers/sh/pfc/gpio.c b/drivers/sh/pfc/gpio.c index d74e5a96024b..f37f0c6d89b3 100644 --- a/drivers/sh/pfc/gpio.c +++ b/drivers/sh/pfc/gpio.c @@ -16,6 +16,7 @@ #include #include #include +#include struct sh_pfc_chip { struct sh_pfc *pfc; @@ -34,80 +35,12 @@ static struct sh_pfc *gpio_to_pfc(struct gpio_chip *gc) static int sh_gpio_request(struct gpio_chip *gc, unsigned offset) { - struct sh_pfc *pfc = gpio_to_pfc(gc); - struct pinmux_data_reg *dummy; - unsigned long flags; - int i, ret, pinmux_type; - - ret = -EINVAL; - - if (!pfc) - goto err_out; - - spin_lock_irqsave(&pfc->lock, flags); - - if ((pfc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) - goto err_unlock; - - /* setup pin function here if no data is associated with pin */ - - if (sh_pfc_get_data_reg(pfc, offset, &dummy, &i) != 0) - pinmux_type = PINMUX_TYPE_FUNCTION; - else - pinmux_type = PINMUX_TYPE_GPIO; - - if (pinmux_type == PINMUX_TYPE_FUNCTION) { - if (sh_pfc_config_gpio(pfc, offset, - pinmux_type, - GPIO_CFG_DRYRUN) != 0) - goto err_unlock; - - if (sh_pfc_config_gpio(pfc, offset, - pinmux_type, - GPIO_CFG_REQ) != 0) - BUG(); - } - - pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[offset].flags |= pinmux_type; - - ret = 0; - err_unlock: - spin_unlock_irqrestore(&pfc->lock, flags); - err_out: - return ret; + return pinctrl_request_gpio(offset); } static void sh_gpio_free(struct gpio_chip *gc, unsigned offset) { - struct sh_pfc *pfc = gpio_to_pfc(gc); - unsigned long flags; - int pinmux_type; - - if (!pfc) - return; - - spin_lock_irqsave(&pfc->lock, flags); - - pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; - sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); - pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[offset].flags |= PINMUX_TYPE_NONE; - - spin_unlock_irqrestore(&pfc->lock, flags); -} - -static int sh_gpio_direction_input(struct gpio_chip *gc, unsigned offset) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - unsigned long flags; - int ret; - - spin_lock_irqsave(&pfc->lock, flags); - ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_INPUT); - spin_unlock_irqrestore(&pfc->lock, flags); - - return ret; + pinctrl_free_gpio(offset); } static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) @@ -121,22 +54,6 @@ static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) sh_pfc_write_bit(dr, bit, value); } -static int sh_gpio_direction_output(struct gpio_chip *gc, unsigned offset, - int value) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - unsigned long flags; - int ret; - - sh_gpio_set_value(pfc, offset, value); - - spin_lock_irqsave(&pfc->lock, flags); - ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_OUTPUT); - spin_unlock_irqrestore(&pfc->lock, flags); - - return ret; -} - static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) { struct pinmux_data_reg *dr = NULL; @@ -148,6 +65,19 @@ static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) return sh_pfc_read_bit(dr, bit); } +static int sh_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + return pinctrl_gpio_direction_input(offset); +} + +static int sh_gpio_direction_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + sh_gpio_set_value(gpio_to_pfc(gc), offset, value); + + return pinctrl_gpio_direction_output(offset); +} + static int sh_gpio_get(struct gpio_chip *gc, unsigned offset) { return sh_gpio_get_value(gpio_to_pfc(gc), offset); diff --git a/drivers/sh/pfc/pinctrl.c b/drivers/sh/pfc/pinctrl.c new file mode 100644 index 000000000000..6008328594ff --- /dev/null +++ b/drivers/sh/pfc/pinctrl.c @@ -0,0 +1,371 @@ +/* + * SuperH Pin Function Controller pinmux support. + * + * Copyright (C) 2012 Paul Mundt + * + * 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. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct sh_pfc_pinctrl { + struct pinctrl_dev *pctl; + struct sh_pfc *pfc; + + struct pinctrl_pin_desc *pads; + unsigned int nr_pads; +}; + +static struct sh_pfc_pinctrl *sh_pfc_pmx; + +/* + * No group support yet + */ +static int sh_pfc_get_noop_count(struct pinctrl_dev *pctldev) +{ + return 0; +} + +static const char *sh_pfc_get_noop_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return NULL; +} + +static int sh_pfc_get_group_pins(struct pinctrl_dev *pctldev, unsigned group, + const unsigned **pins, unsigned *num_pins) +{ + return -ENOTSUPP; +} + +static struct pinctrl_ops sh_pfc_pinctrl_ops = { + .get_groups_count = sh_pfc_get_noop_count, + .get_group_name = sh_pfc_get_noop_name, + .get_group_pins = sh_pfc_get_group_pins, +}; + + +/* + * No function support yet + */ +static int sh_pfc_get_function_groups(struct pinctrl_dev *pctldev, unsigned func, + const char * const **groups, + unsigned * const num_groups) +{ + return 0; +} + +static int sh_pfc_noop_enable(struct pinctrl_dev *pctldev, unsigned func, + unsigned group) +{ + return 0; +} + +static void sh_pfc_noop_disable(struct pinctrl_dev *pctldev, unsigned func, + unsigned group) +{ +} + +static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + struct sh_pfc *pfc = pmx->pfc; + struct pinmux_data_reg *dummy; + unsigned long flags; + int i, ret, pinmux_type; + + ret = -EINVAL; + + spin_lock_irqsave(&pfc->lock, flags); + + if ((pfc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) + goto err; + + /* setup pin function here if no data is associated with pin */ + if (sh_pfc_get_data_reg(pfc, offset, &dummy, &i) != 0) { + pinmux_type = PINMUX_TYPE_FUNCTION; + + if (sh_pfc_config_gpio(pfc, offset, + pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err; + + if (sh_pfc_config_gpio(pfc, offset, + pinmux_type, + GPIO_CFG_REQ) != 0) + goto err; + } else + pinmux_type = PINMUX_TYPE_GPIO; + + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= pinmux_type; + + ret = 0; + +err: + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static void sh_pfc_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + struct sh_pfc *pfc = pmx->pfc; + unsigned long flags; + int pinmux_type; + + spin_lock_irqsave(&pfc->lock, flags); + + pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; + + sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); + + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= PINMUX_TYPE_NONE; + + spin_unlock_irqrestore(&pfc->lock, flags); +} + +static int sh_pfc_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset, bool input) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + struct sh_pfc *pfc = pmx->pfc; + unsigned long flags; + int pinmux_type, new_pinmux_type; + int ret = -EINVAL; + + new_pinmux_type = input ? PINMUX_TYPE_INPUT : PINMUX_TYPE_OUTPUT; + + spin_lock_irqsave(&pfc->lock, flags); + + pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; + + switch (pinmux_type) { + case PINMUX_TYPE_GPIO: + break; + case PINMUX_TYPE_OUTPUT: + case PINMUX_TYPE_INPUT: + case PINMUX_TYPE_INPUT_PULLUP: + case PINMUX_TYPE_INPUT_PULLDOWN: + sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); + break; + default: + goto err; + } + + if (sh_pfc_config_gpio(pfc, offset, + new_pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err; + + if (sh_pfc_config_gpio(pfc, offset, + new_pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= new_pinmux_type; + + ret = 0; + +err: + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static struct pinmux_ops sh_pfc_pinmux_ops = { + .get_functions_count = sh_pfc_get_noop_count, + .get_function_name = sh_pfc_get_noop_name, + .get_function_groups = sh_pfc_get_function_groups, + .enable = sh_pfc_noop_enable, + .disable = sh_pfc_noop_disable, + .gpio_request_enable = sh_pfc_gpio_request_enable, + .gpio_disable_free = sh_pfc_gpio_disable_free, + .gpio_set_direction = sh_pfc_gpio_set_direction, +}; + +static int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config) +{ + return -ENOTSUPP; +} + +static int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long config) +{ + return -EINVAL; +} + +static struct pinconf_ops sh_pfc_pinconf_ops = { + .is_generic = true, + .pin_config_get = sh_pfc_pinconf_get, + .pin_config_set = sh_pfc_pinconf_set, +}; + +static struct pinctrl_gpio_range sh_pfc_gpio_range = { + .name = KBUILD_MODNAME, + .id = 0, +}; + +static struct pinctrl_desc sh_pfc_pinctrl_desc = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .pctlops = &sh_pfc_pinctrl_ops, + .pmxops = &sh_pfc_pinmux_ops, + .confops = &sh_pfc_pinconf_ops, +}; + +int sh_pfc_register_pinctrl(struct sh_pfc *pfc) +{ + sh_pfc_pmx = kmalloc(sizeof(struct sh_pfc_pinctrl), GFP_KERNEL); + if (unlikely(!sh_pfc_pmx)) + return -ENOMEM; + + sh_pfc_pmx->pfc = pfc; + + return 0; +} + +/* pinmux ranges -> pinctrl pin descs */ +static int __devinit sh_pfc_map_gpios(struct sh_pfc *pfc, + struct sh_pfc_pinctrl *pmx) +{ + int i; + + pmx->nr_pads = pfc->last_gpio - pfc->first_gpio + 1; + + pmx->pads = kmalloc(sizeof(struct pinctrl_pin_desc) * pmx->nr_pads, + GFP_KERNEL); + if (unlikely(!pmx->pads)) { + pmx->nr_pads = 0; + return -ENOMEM; + } + + /* + * We don't necessarily have a 1:1 mapping between pin and linux + * GPIO number, as the latter maps to the associated enum_id. + * Care needs to be taken to translate back to pin space when + * dealing with any pin configurations. + */ + for (i = 0; i < pmx->nr_pads; i++) { + struct pinctrl_pin_desc *pin = pmx->pads + i; + struct pinmux_gpio *gpio = pfc->gpios + i; + + pin->number = pfc->first_gpio + i; + pin->name = gpio->name; + } + + sh_pfc_pinctrl_desc.pins = pmx->pads; + sh_pfc_pinctrl_desc.npins = pmx->nr_pads; + + return 0; +} + +static int __devinit sh_pfc_pinctrl_probe(struct platform_device *pdev) +{ + struct sh_pfc *pfc; + int ret; + + if (unlikely(!sh_pfc_pmx)) + return -ENODEV; + + pfc = sh_pfc_pmx->pfc; + + ret = sh_pfc_map_gpios(pfc, sh_pfc_pmx); + if (unlikely(ret != 0)) + return ret; + + sh_pfc_pmx->pctl = pinctrl_register(&sh_pfc_pinctrl_desc, &pdev->dev, + sh_pfc_pmx); + if (IS_ERR(sh_pfc_pmx->pctl)) { + ret = PTR_ERR(sh_pfc_pmx->pctl); + goto out; + } + + sh_pfc_gpio_range.npins = pfc->last_gpio - pfc->first_gpio + 1; + sh_pfc_gpio_range.base = pfc->first_gpio; + sh_pfc_gpio_range.pin_base = pfc->first_gpio; + + pinctrl_add_gpio_range(sh_pfc_pmx->pctl, &sh_pfc_gpio_range); + + platform_set_drvdata(pdev, sh_pfc_pmx); + + return 0; + +out: + kfree(sh_pfc_pmx->pads); + kfree(sh_pfc_pmx); + return ret; +} + +static int __devexit sh_pfc_pinctrl_remove(struct platform_device *pdev) +{ + struct sh_pfc_pinctrl *pmx = platform_get_drvdata(pdev); + + pinctrl_remove_gpio_range(pmx->pctl, &sh_pfc_gpio_range); + pinctrl_unregister(pmx->pctl); + + platform_set_drvdata(pdev, NULL); + + kfree(sh_pfc_pmx->pads); + kfree(sh_pfc_pmx); + + return 0; +} + +static struct platform_driver sh_pfc_pinctrl_driver = { + .probe = sh_pfc_pinctrl_probe, + .remove = __devexit_p(sh_pfc_pinctrl_remove), + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, +}; + +static struct platform_device sh_pfc_pinctrl_device = { + .name = KBUILD_MODNAME, + .id = -1, +}; + +static int __init sh_pfc_pinctrl_init(void) +{ + int rc; + + rc = platform_driver_register(&sh_pfc_pinctrl_driver); + if (likely(!rc)) { + rc = platform_device_register(&sh_pfc_pinctrl_device); + if (unlikely(rc)) + platform_driver_unregister(&sh_pfc_pinctrl_driver); + } + + return rc; +} + +static void __exit sh_pfc_pinctrl_exit(void) +{ + platform_driver_unregister(&sh_pfc_pinctrl_driver); +} + +subsys_initcall(sh_pfc_pinctrl_init); +module_exit(sh_pfc_pinctrl_exit); diff --git a/include/linux/sh_pfc.h b/include/linux/sh_pfc.h index 8c4cbcb9064d..c19a0925829a 100644 --- a/include/linux/sh_pfc.h +++ b/include/linux/sh_pfc.h @@ -132,6 +132,9 @@ struct sh_pfc { /* drivers/sh/pfc/gpio.c */ int sh_pfc_register_gpiochip(struct sh_pfc *pfc); +/* drivers/sh/pfc/pinctrl.c */ +int sh_pfc_register_pinctrl(struct sh_pfc *pfc); + /* drivers/sh/pfc/core.c */ int register_sh_pfc(struct sh_pfc *pfc); @@ -144,8 +147,6 @@ int sh_pfc_gpio_to_enum(struct sh_pfc *pfc, unsigned gpio, int pos, pinmux_enum_t *enum_idp); int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type, int cfg_mode); -int sh_pfc_set_direction(struct sh_pfc *pfc, unsigned gpio, - int new_pinmux_type); /* xxx */ static inline int register_pinmux(struct pinmux_info *pip) -- cgit v1.2.3-59-g8ed1b From 6fe9cbd1119b3cd2595dabaf25a400eed95b170c Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 13 Jun 2012 18:58:09 +0200 Subject: clocksource: time-armada-370-xp: Marvell Armada 370/XP SoC timer driver Timer 0 is used as free-running clocksource, while timer 1 is used as clock_event_device. Signed-off-by: Gregory CLEMENT Signed-off-by: Thomas Petazzoni Signed-off-by: Lior Amsalem Reviewed-by: Thomas Gleixner Tested-by: Yehuda Yitschak Tested-by: Lior Amsalem Acked-by: Andrew Lunn CC: Thomas Gleixner CC: John Stultz --- drivers/clocksource/Kconfig | 3 + drivers/clocksource/Makefile | 3 +- drivers/clocksource/time-armada-370-xp.c | 226 +++++++++++++++++++++++++++++++ include/linux/time-armada-370-xp.h | 18 +++ 4 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 drivers/clocksource/time-armada-370-xp.c create mode 100644 include/linux/time-armada-370-xp.h (limited to 'include/linux') diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 99c6b203e6cd..b32363173584 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -16,6 +16,9 @@ config CLKSRC_MMIO config DW_APB_TIMER bool +config ARMADA_370_XP_TIMER + bool + config CLKSRC_DBX500_PRCMU bool "Clocksource PRCMU Timer" depends on UX500_SOC_DB8500 diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index dd3e661a124d..022015cbee48 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -10,4 +10,5 @@ obj-$(CONFIG_EM_TIMER_STI) += em_sti.o obj-$(CONFIG_CLKBLD_I8253) += i8253.o obj-$(CONFIG_CLKSRC_MMIO) += mmio.o obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o -obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o \ No newline at end of file +obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o +obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c new file mode 100644 index 000000000000..4674f94957cd --- /dev/null +++ b/drivers/clocksource/time-armada-370-xp.c @@ -0,0 +1,226 @@ +/* + * Marvell Armada 370/XP SoC timer handling. + * + * Copyright (C) 2012 Marvell + * + * Lior Amsalem + * Gregory CLEMENT + * Thomas Petazzoni + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * Timer 0 is used as free-running clocksource, while timer 1 is + * used as clock_event_device. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Timer block registers. + */ +#define TIMER_CTRL_OFF 0x0000 +#define TIMER0_EN 0x0001 +#define TIMER0_RELOAD_EN 0x0002 +#define TIMER0_25MHZ 0x0800 +#define TIMER0_DIV(div) ((div) << 19) +#define TIMER1_EN 0x0004 +#define TIMER1_RELOAD_EN 0x0008 +#define TIMER1_25MHZ 0x1000 +#define TIMER1_DIV(div) ((div) << 22) +#define TIMER_EVENTS_STATUS 0x0004 +#define TIMER0_CLR_MASK (~0x1) +#define TIMER1_CLR_MASK (~0x100) +#define TIMER0_RELOAD_OFF 0x0010 +#define TIMER0_VAL_OFF 0x0014 +#define TIMER1_RELOAD_OFF 0x0018 +#define TIMER1_VAL_OFF 0x001c + +/* Global timers are connected to the coherency fabric clock, and the + below divider reduces their incrementing frequency. */ +#define TIMER_DIVIDER_SHIFT 5 +#define TIMER_DIVIDER (1 << TIMER_DIVIDER_SHIFT) + +/* + * SoC-specific data. + */ +static void __iomem *timer_base; +static int timer_irq; + +/* + * Number of timer ticks per jiffy. + */ +static u32 ticks_per_jiffy; + +static u32 notrace armada_370_xp_read_sched_clock(void) +{ + return ~readl(timer_base + TIMER0_VAL_OFF); +} + +/* + * Clockevent handling. + */ +static int +armada_370_xp_clkevt_next_event(unsigned long delta, + struct clock_event_device *dev) +{ + u32 u; + + /* + * Clear clockevent timer interrupt. + */ + writel(TIMER1_CLR_MASK, timer_base + TIMER_EVENTS_STATUS); + + /* + * Setup new clockevent timer value. + */ + writel(delta, timer_base + TIMER1_VAL_OFF); + + /* + * Enable the timer. + */ + u = readl(timer_base + TIMER_CTRL_OFF); + u = ((u & ~TIMER1_RELOAD_EN) | TIMER1_EN | + TIMER1_DIV(TIMER_DIVIDER_SHIFT)); + writel(u, timer_base + TIMER_CTRL_OFF); + + return 0; +} + +static void +armada_370_xp_clkevt_mode(enum clock_event_mode mode, + struct clock_event_device *dev) +{ + u32 u; + + if (mode == CLOCK_EVT_MODE_PERIODIC) { + /* + * Setup timer to fire at 1/HZ intervals. + */ + writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD_OFF); + writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL_OFF); + + /* + * Enable timer. + */ + u = readl(timer_base + TIMER_CTRL_OFF); + + writel((u | TIMER1_EN | TIMER1_RELOAD_EN | + TIMER1_DIV(TIMER_DIVIDER_SHIFT)), + timer_base + TIMER_CTRL_OFF); + } else { + /* + * Disable timer. + */ + u = readl(timer_base + TIMER_CTRL_OFF); + writel(u & ~TIMER1_EN, timer_base + TIMER_CTRL_OFF); + + /* + * ACK pending timer interrupt. + */ + writel(TIMER1_CLR_MASK, timer_base + TIMER_EVENTS_STATUS); + + } +} + +static struct clock_event_device armada_370_xp_clkevt = { + .name = "armada_370_xp_tick", + .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, + .shift = 32, + .rating = 300, + .set_next_event = armada_370_xp_clkevt_next_event, + .set_mode = armada_370_xp_clkevt_mode, +}; + +static irqreturn_t armada_370_xp_timer_interrupt(int irq, void *dev_id) +{ + /* + * ACK timer interrupt and call event handler. + */ + + writel(TIMER1_CLR_MASK, timer_base + TIMER_EVENTS_STATUS); + armada_370_xp_clkevt.event_handler(&armada_370_xp_clkevt); + + return IRQ_HANDLED; +} + +static struct irqaction armada_370_xp_timer_irq = { + .name = "armada_370_xp_tick", + .flags = IRQF_DISABLED | IRQF_TIMER, + .handler = armada_370_xp_timer_interrupt +}; + +void __init armada_370_xp_timer_init(void) +{ + u32 u; + struct device_node *np; + unsigned int timer_clk; + int ret; + np = of_find_compatible_node(NULL, NULL, "marvell,armada-370-xp-timer"); + timer_base = of_iomap(np, 0); + WARN_ON(!timer_base); + + if (of_find_property(np, "marvell,timer-25Mhz", NULL)) { + /* The fixed 25MHz timer is available so let's use it */ + u = readl(timer_base + TIMER_CTRL_OFF); + writel(u | TIMER0_25MHZ | TIMER1_25MHZ, + timer_base + TIMER_CTRL_OFF); + timer_clk = 25000000; + } else { + u32 clk = 0; + ret = of_property_read_u32(np, "clock-frequency", &clk); + WARN_ON(!clk || ret < 0); + u = readl(timer_base + TIMER_CTRL_OFF); + writel(u & ~(TIMER0_25MHZ | TIMER1_25MHZ), + timer_base + TIMER_CTRL_OFF); + timer_clk = clk / TIMER_DIVIDER; + } + + /* We use timer 0 as clocksource, and timer 1 for + clockevents */ + timer_irq = irq_of_parse_and_map(np, 1); + + ticks_per_jiffy = (timer_clk + HZ / 2) / HZ; + + /* + * Set scale and timer for sched_clock. + */ + setup_sched_clock(armada_370_xp_read_sched_clock, 32, timer_clk); + + /* + * Setup free-running clocksource timer (interrupts + * disabled). + */ + writel(0xffffffff, timer_base + TIMER0_VAL_OFF); + writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF); + + u = readl(timer_base + TIMER_CTRL_OFF); + + writel((u | TIMER0_EN | TIMER0_RELOAD_EN | + TIMER0_DIV(TIMER_DIVIDER_SHIFT)), timer_base + TIMER_CTRL_OFF); + + clocksource_mmio_init(timer_base + TIMER0_VAL_OFF, + "armada_370_xp_clocksource", + timer_clk, 300, 32, clocksource_mmio_readl_down); + + /* + * Setup clockevent timer (interrupt-driven). + */ + setup_irq(timer_irq, &armada_370_xp_timer_irq); + armada_370_xp_clkevt.cpumask = cpumask_of(0); + clockevents_config_and_register(&armada_370_xp_clkevt, + timer_clk, 1, 0xfffffffe); +} + diff --git a/include/linux/time-armada-370-xp.h b/include/linux/time-armada-370-xp.h new file mode 100644 index 000000000000..dfdfdc03115b --- /dev/null +++ b/include/linux/time-armada-370-xp.h @@ -0,0 +1,18 @@ +/* + * Marvell Armada 370/XP SoC timer handling. + * + * Copyright (C) 2012 Marvell + * + * Lior Amsalem + * Gregory CLEMENT + * Thomas Petazzoni + * + */ +#ifndef __TIME_ARMADA_370_XPPRCMU_H +#define __TIME_ARMADA_370_XPPRCMU_H + +#include + +void __init armada_370_xp_timer_init(void); + +#endif -- cgit v1.2.3-59-g8ed1b From e95d8aafa5d911bf523bc47fe89f3336eb8a1b51 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 10 Jul 2012 13:35:50 +0000 Subject: of: mtd: nuke useless const qualifier This patch does the following: -const int of_get_nand_ecc_mode(struct device_node *np) +int of_get_nand_ecc_mode(struct device_node *np) because: 1. it is probably just a typo? 2. it causes warnings like this when people assing the returned value to an 'int' variable: include/linux/of_mtd.h:14:18: warning: type qualifiers ignored on functi= on return type [-Wignored-qualifiers] Remove also the unnecessary "extern" qualifier to be consistent with other declarations in this file. Signed-off-by: Artem Bityutskiy Signed-off-by: Rob Herring --- drivers/of/of_mtd.c | 2 +- include/linux/of_mtd.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c index e7cad627a5d1..a27ec94877e4 100644 --- a/drivers/of/of_mtd.c +++ b/drivers/of/of_mtd.c @@ -32,7 +32,7 @@ static const char *nand_ecc_modes[] = { * The function gets ecc mode string from property 'nand-ecc-mode', * and return its index in nand_ecc_modes table, or errno in error case. */ -const int of_get_nand_ecc_mode(struct device_node *np) +int of_get_nand_ecc_mode(struct device_node *np) { const char *pm; int err, i; diff --git a/include/linux/of_mtd.h b/include/linux/of_mtd.h index bae1b6094c63..ed7f267e6389 100644 --- a/include/linux/of_mtd.h +++ b/include/linux/of_mtd.h @@ -11,7 +11,7 @@ #ifdef CONFIG_OF_MTD #include -extern const int of_get_nand_ecc_mode(struct device_node *np); +int of_get_nand_ecc_mode(struct device_node *np); int of_get_nand_bus_width(struct device_node *np); bool of_get_nand_on_flash_bbt(struct device_node *np); #endif -- cgit v1.2.3-59-g8ed1b From 00f5ce99dc6ee46c3113393cc8fa12173f9bbcd7 Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Tue, 19 Jun 2012 11:21:40 +0300 Subject: mlx4: Use port management change event instead of smp_snoop The port management change event can replace smp_snoop. If the capability bit for this event is set in dev-caps, the event is used (by the driver setting the PORT_MNG_CHG_EVENT bit in the async event mask in the MAP_EQ fw command). In this case, when the driver passes incoming SMP PORT_INFO SET mads to the FW, the FW generates port management change events to signal any changes to the driver. If the FW generates these events, smp_snoop shouldn't be invoked in ib_process_mad(), or duplicate events will occur (once from the FW-generated event, and once from smp_snoop). In the case where the FW does not generate port management change events smp_snoop needs to be invoked to create these events. The flow in smp_snoop has been modified to make use of the same procedures as in the fw-generated-event event case to generate the port management events (LID change, Client-rereg, Pkey change, and/or GID change). Port management change event handling required changing the mlx4_ib_event and mlx4_dispatch_event prototypes; the "param" argument (last argument) had to be changed to unsigned long in order to accomodate passing the EQE pointer. We also needed to move the definition of struct mlx4_eqe from net/mlx4.h to file device.h -- to make it available to the IB driver, to handle port management change events. Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/mad.c | 118 ++++++++++++++++++++------- drivers/infiniband/hw/mlx4/main.c | 29 +++++-- drivers/infiniband/hw/mlx4/mlx4_ib.h | 9 ++ drivers/net/ethernet/mellanox/mlx4/en_main.c | 5 +- drivers/net/ethernet/mellanox/mlx4/eq.c | 22 ++++- drivers/net/ethernet/mellanox/mlx4/fw.c | 1 + drivers/net/ethernet/mellanox/mlx4/intf.c | 5 +- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 63 +------------- include/linux/mlx4/device.h | 99 +++++++++++++++++++++- include/linux/mlx4/driver.h | 3 +- 10 files changed, 249 insertions(+), 105 deletions(-) (limited to 'include/linux') diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c index 84786a9fb64f..58c45fb5bd31 100644 --- a/drivers/infiniband/hw/mlx4/mad.c +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -147,47 +147,49 @@ static void update_sm_ah(struct mlx4_ib_dev *dev, u8 port_num, u16 lid, u8 sl) } /* - * Snoop SM MADs for port info and P_Key table sets, so we can - * synthesize LID change and P_Key change events. + * Snoop SM MADs for port info, GUID info, and P_Key table sets, so we can + * synthesize LID change, Client-Rereg, GID change, and P_Key change events. */ static void smp_snoop(struct ib_device *ibdev, u8 port_num, struct ib_mad *mad, - u16 prev_lid) + u16 prev_lid) { - struct ib_event event; + struct ib_port_info *pinfo; + u16 lid; + struct mlx4_ib_dev *dev = to_mdev(ibdev); if ((mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED || mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) && - mad->mad_hdr.method == IB_MGMT_METHOD_SET) { - if (mad->mad_hdr.attr_id == IB_SMP_ATTR_PORT_INFO) { - struct ib_port_info *pinfo = - (struct ib_port_info *) ((struct ib_smp *) mad)->data; - u16 lid = be16_to_cpu(pinfo->lid); + mad->mad_hdr.method == IB_MGMT_METHOD_SET) + switch (mad->mad_hdr.attr_id) { + case IB_SMP_ATTR_PORT_INFO: + pinfo = (struct ib_port_info *) ((struct ib_smp *) mad)->data; + lid = be16_to_cpu(pinfo->lid); - update_sm_ah(to_mdev(ibdev), port_num, + update_sm_ah(dev, port_num, be16_to_cpu(pinfo->sm_lid), pinfo->neighbormtu_mastersmsl & 0xf); - event.device = ibdev; - event.element.port_num = port_num; + if (pinfo->clientrereg_resv_subnetto & 0x80) + mlx4_ib_dispatch_event(dev, port_num, + IB_EVENT_CLIENT_REREGISTER); - if (pinfo->clientrereg_resv_subnetto & 0x80) { - event.event = IB_EVENT_CLIENT_REREGISTER; - ib_dispatch_event(&event); - } + if (prev_lid != lid) + mlx4_ib_dispatch_event(dev, port_num, + IB_EVENT_LID_CHANGE); + break; - if (prev_lid != lid) { - event.event = IB_EVENT_LID_CHANGE; - ib_dispatch_event(&event); - } - } + case IB_SMP_ATTR_PKEY_TABLE: + mlx4_ib_dispatch_event(dev, port_num, + IB_EVENT_PKEY_CHANGE); + break; - if (mad->mad_hdr.attr_id == IB_SMP_ATTR_PKEY_TABLE) { - event.device = ibdev; - event.event = IB_EVENT_PKEY_CHANGE; - event.element.port_num = port_num; - ib_dispatch_event(&event); + case IB_SMP_ATTR_GUID_INFO: + mlx4_ib_dispatch_event(dev, port_num, + IB_EVENT_GID_CHANGE); + break; + default: + break; } - } } static void node_desc_override(struct ib_device *dev, @@ -305,7 +307,8 @@ static int ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num, return IB_MAD_RESULT_FAILURE; if (!out_mad->mad_hdr.status) { - smp_snoop(ibdev, port_num, in_mad, prev_lid); + if (!(to_mdev(ibdev)->dev->caps.flags & MLX4_DEV_CAP_FLAG_PORT_MNG_CHG_EV)) + smp_snoop(ibdev, port_num, in_mad, prev_lid); node_desc_override(ibdev, out_mad); } @@ -446,3 +449,62 @@ void mlx4_ib_mad_cleanup(struct mlx4_ib_dev *dev) ib_destroy_ah(dev->sm_ah[p]); } } + +void handle_port_mgmt_change_event(struct work_struct *work) +{ + struct ib_event_work *ew = container_of(work, struct ib_event_work, work); + struct mlx4_ib_dev *dev = ew->ib_dev; + struct mlx4_eqe *eqe = &(ew->ib_eqe); + u8 port = eqe->event.port_mgmt_change.port; + u32 changed_attr; + + switch (eqe->subtype) { + case MLX4_DEV_PMC_SUBTYPE_PORT_INFO: + changed_attr = be32_to_cpu(eqe->event.port_mgmt_change.params.port_info.changed_attr); + + /* Update the SM ah - This should be done before handling + the other changed attributes so that MADs can be sent to the SM */ + if (changed_attr & MSTR_SM_CHANGE_MASK) { + u16 lid = be16_to_cpu(eqe->event.port_mgmt_change.params.port_info.mstr_sm_lid); + u8 sl = eqe->event.port_mgmt_change.params.port_info.mstr_sm_sl & 0xf; + update_sm_ah(dev, port, lid, sl); + } + + /* Check if it is a lid change event */ + if (changed_attr & MLX4_EQ_PORT_INFO_LID_CHANGE_MASK) + mlx4_ib_dispatch_event(dev, port, IB_EVENT_LID_CHANGE); + + /* Generate GUID changed event */ + if (changed_attr & MLX4_EQ_PORT_INFO_GID_PFX_CHANGE_MASK) + mlx4_ib_dispatch_event(dev, port, IB_EVENT_GID_CHANGE); + + if (changed_attr & MLX4_EQ_PORT_INFO_CLIENT_REREG_MASK) + mlx4_ib_dispatch_event(dev, port, + IB_EVENT_CLIENT_REREGISTER); + break; + + case MLX4_DEV_PMC_SUBTYPE_PKEY_TABLE: + mlx4_ib_dispatch_event(dev, port, IB_EVENT_PKEY_CHANGE); + break; + case MLX4_DEV_PMC_SUBTYPE_GUID_INFO: + mlx4_ib_dispatch_event(dev, port, IB_EVENT_GID_CHANGE); + break; + default: + pr_warn("Unsupported subtype 0x%x for " + "Port Management Change event\n", eqe->subtype); + } + + kfree(ew); +} + +void mlx4_ib_dispatch_event(struct mlx4_ib_dev *dev, u8 port_num, + enum ib_event_type type) +{ + struct ib_event event; + + event.device = &dev->ib_dev; + event.element.port_num = port_num; + event.event = type; + + ib_dispatch_event(&event); +} diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 5266b49c46ee..4f230c26622d 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -898,7 +898,6 @@ static void update_gids_task(struct work_struct *work) union ib_gid *gids; int err; struct mlx4_dev *dev = gw->dev->dev; - struct ib_event event; mailbox = mlx4_alloc_cmd_mailbox(dev); if (IS_ERR(mailbox)) { @@ -916,10 +915,7 @@ static void update_gids_task(struct work_struct *work) pr_warn("set port command failed\n"); else { memcpy(gw->dev->iboe.gid_table[gw->port - 1], gw->gids, sizeof gw->gids); - event.device = &gw->dev->ib_dev; - event.element.port_num = gw->port; - event.event = IB_EVENT_GID_CHANGE; - ib_dispatch_event(&event); + mlx4_ib_dispatch_event(gw->dev, gw->port, IB_EVENT_GID_CHANGE); } mlx4_free_cmd_mailbox(dev, mailbox); @@ -1383,10 +1379,18 @@ static void mlx4_ib_remove(struct mlx4_dev *dev, void *ibdev_ptr) } static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr, - enum mlx4_dev_event event, int port) + enum mlx4_dev_event event, unsigned long param) { struct ib_event ibev; struct mlx4_ib_dev *ibdev = to_mdev((struct ib_device *) ibdev_ptr); + struct mlx4_eqe *eqe = NULL; + struct ib_event_work *ew; + int port = 0; + + if (event == MLX4_DEV_EVENT_PORT_MGMT_CHANGE) + eqe = (struct mlx4_eqe *)param; + else + port = (u8)param; if (port > ibdev->num_ports) return; @@ -1405,6 +1409,19 @@ static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr, ibev.event = IB_EVENT_DEVICE_FATAL; break; + case MLX4_DEV_EVENT_PORT_MGMT_CHANGE: + ew = kmalloc(sizeof *ew, GFP_ATOMIC); + if (!ew) { + pr_err("failed to allocate memory for events work\n"); + break; + } + + INIT_WORK(&ew->work, handle_port_mgmt_change_event); + memcpy(&ew->ib_eqe, eqe, sizeof *eqe); + ew->ib_dev = ibdev; + handle_port_mgmt_change_event(&ew->work); + return; + default: return; } diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index 5f298afaa81f..23bfbf9ee0e0 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -224,6 +224,12 @@ struct mlx4_ib_dev { int eq_added; }; +struct ib_event_work { + struct work_struct work; + struct mlx4_ib_dev *ib_dev; + struct mlx4_eqe ib_eqe; +}; + static inline struct mlx4_ib_dev *to_mdev(struct ib_device *ibdev) { return container_of(ibdev, struct mlx4_ib_dev, ib_dev); @@ -381,4 +387,7 @@ static inline int mlx4_ib_ah_grh_present(struct mlx4_ib_ah *ah) int mlx4_ib_add_mc(struct mlx4_ib_dev *mdev, struct mlx4_ib_qp *mqp, union ib_gid *gid); +void mlx4_ib_dispatch_event(struct mlx4_ib_dev *dev, u8 port_num, + enum ib_event_type type); + #endif /* MLX4_IB_H */ diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c index 69ba57270481..a52922ed85c1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c @@ -131,7 +131,7 @@ static void *mlx4_en_get_netdev(struct mlx4_dev *dev, void *ctx, u8 port) } static void mlx4_en_event(struct mlx4_dev *dev, void *endev_ptr, - enum mlx4_dev_event event, int port) + enum mlx4_dev_event event, unsigned long port) { struct mlx4_en_dev *mdev = (struct mlx4_en_dev *) endev_ptr; struct mlx4_en_priv *priv; @@ -156,7 +156,8 @@ static void mlx4_en_event(struct mlx4_dev *dev, void *endev_ptr, if (port < 1 || port > dev->caps.num_ports || !mdev->pndev[port]) return; - mlx4_warn(mdev, "Unhandled event %d for port %d\n", event, port); + mlx4_warn(mdev, "Unhandled event %d for port %d\n", event, + (int) port); } } diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index bce98d9c0039..9b15d0219950 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -82,6 +82,15 @@ enum { (1ull << MLX4_EVENT_TYPE_FLR_EVENT) | \ (1ull << MLX4_EVENT_TYPE_FATAL_WARNING)) +static u64 get_async_ev_mask(struct mlx4_dev *dev) +{ + u64 async_ev_mask = MLX4_ASYNC_EVENT_MASK; + if (dev->caps.flags & MLX4_DEV_CAP_FLAG_PORT_MNG_CHG_EV) + async_ev_mask |= (1ull << MLX4_EVENT_TYPE_PORT_MNG_CHG_EVENT); + + return async_ev_mask; +} + static void eq_set_ci(struct mlx4_eq *eq, int req_not) { __raw_writel((__force u32) cpu_to_be32((eq->cons_index & 0xffffff) | @@ -473,6 +482,11 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) break; + case MLX4_EVENT_TYPE_PORT_MNG_CHG_EVENT: + mlx4_dispatch_event(dev, MLX4_DEV_EVENT_PORT_MGMT_CHANGE, + (unsigned long) eqe); + break; + case MLX4_EVENT_TYPE_EEC_CATAS_ERROR: case MLX4_EVENT_TYPE_ECC_DETECT: default: @@ -956,7 +970,7 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) priv->eq_table.have_irq = 1; } - err = mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 0, + err = mlx4_MAP_EQ(dev, get_async_ev_mask(dev), 0, priv->eq_table.eq[dev->caps.num_comp_vectors].eqn); if (err) mlx4_warn(dev, "MAP_EQ for async EQ %d failed (%d)\n", @@ -996,7 +1010,7 @@ void mlx4_cleanup_eq_table(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); int i; - mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 1, + mlx4_MAP_EQ(dev, get_async_ev_mask(dev), 1, priv->eq_table.eq[dev->caps.num_comp_vectors].eqn); mlx4_free_irqs(dev); @@ -1040,7 +1054,7 @@ int mlx4_test_interrupts(struct mlx4_dev *dev) mlx4_cmd_use_polling(dev); /* Map the new eq to handle all asyncronous events */ - err = mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 0, + err = mlx4_MAP_EQ(dev, get_async_ev_mask(dev), 0, priv->eq_table.eq[i].eqn); if (err) { mlx4_warn(dev, "Failed mapping eq for interrupt test\n"); @@ -1054,7 +1068,7 @@ int mlx4_test_interrupts(struct mlx4_dev *dev) } /* Return to default */ - mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 0, + mlx4_MAP_EQ(dev, get_async_ev_mask(dev), 0, priv->eq_table.eq[dev->caps.num_comp_vectors].eqn); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 4281ce09add8..ee9d6b0b4d20 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -109,6 +109,7 @@ static void dump_dev_cap_flags(struct mlx4_dev *dev, u64 flags) [41] = "Unicast VEP steering support", [42] = "Multicast VEP steering support", [48] = "Counters support", + [59] = "Port management change event support", }; int i; diff --git a/drivers/net/ethernet/mellanox/mlx4/intf.c b/drivers/net/ethernet/mellanox/mlx4/intf.c index b4e9f6f5cc04..116895ac8b35 100644 --- a/drivers/net/ethernet/mellanox/mlx4/intf.c +++ b/drivers/net/ethernet/mellanox/mlx4/intf.c @@ -115,7 +115,8 @@ void mlx4_unregister_interface(struct mlx4_interface *intf) } EXPORT_SYMBOL_GPL(mlx4_unregister_interface); -void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_dev_event type, int port) +void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_dev_event type, + unsigned long param) { struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_device_context *dev_ctx; @@ -125,7 +126,7 @@ void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_dev_event type, int por list_for_each_entry(dev_ctx, &priv->ctx_list, list) if (dev_ctx->intf->event) - dev_ctx->intf->event(dev, dev_ctx->context, type, port); + dev_ctx->intf->event(dev, dev_ctx->context, type, param); spin_unlock_irqrestore(&priv->ctx_lock, flags); } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index e5d20220762c..4d11d12b9db4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -338,66 +338,6 @@ struct mlx4_srq_context { __be64 db_rec_addr; }; -struct mlx4_eqe { - u8 reserved1; - u8 type; - u8 reserved2; - u8 subtype; - union { - u32 raw[6]; - struct { - __be32 cqn; - } __packed comp; - struct { - u16 reserved1; - __be16 token; - u32 reserved2; - u8 reserved3[3]; - u8 status; - __be64 out_param; - } __packed cmd; - struct { - __be32 qpn; - } __packed qp; - struct { - __be32 srqn; - } __packed srq; - struct { - __be32 cqn; - u32 reserved1; - u8 reserved2[3]; - u8 syndrome; - } __packed cq_err; - struct { - u32 reserved1[2]; - __be32 port; - } __packed port_change; - struct { - #define COMM_CHANNEL_BIT_ARRAY_SIZE 4 - u32 reserved; - u32 bit_vec[COMM_CHANNEL_BIT_ARRAY_SIZE]; - } __packed comm_channel_arm; - struct { - u8 port; - u8 reserved[3]; - __be64 mac; - } __packed mac_update; - struct { - u8 port; - } __packed sw_event; - struct { - __be32 slave_id; - } __packed flr_event; - struct { - __be16 current_temperature; - __be16 warning_threshold; - } __packed warming; - } event; - u8 slave_id; - u8 reserved3[2]; - u8 owner; -} __packed; - struct mlx4_eq { struct mlx4_dev *dev; void __iomem *doorbell; @@ -887,7 +827,8 @@ void mlx4_catas_init(void); int mlx4_restart_one(struct pci_dev *pdev); int mlx4_register_device(struct mlx4_dev *dev); void mlx4_unregister_device(struct mlx4_dev *dev); -void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_dev_event type, int port); +void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_dev_event type, + unsigned long param); struct mlx4_dev_cap; struct mlx4_init_hca_param; diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 8eadf0f14cc5..560b2201519f 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -96,7 +96,8 @@ enum { MLX4_DEV_CAP_FLAG_VEP_UC_STEER = 1LL << 41, MLX4_DEV_CAP_FLAG_VEP_MC_STEER = 1LL << 42, MLX4_DEV_CAP_FLAG_COUNTERS = 1LL << 48, - MLX4_DEV_CAP_FLAG_SENSE_SUPPORT = 1LL << 55 + MLX4_DEV_CAP_FLAG_SENSE_SUPPORT = 1LL << 55, + MLX4_DEV_CAP_FLAG_PORT_MNG_CHG_EV = 1LL << 59, }; enum { @@ -138,6 +139,7 @@ enum mlx4_event { MLX4_EVENT_TYPE_COMM_CHANNEL = 0x18, MLX4_EVENT_TYPE_FATAL_WARNING = 0x1b, MLX4_EVENT_TYPE_FLR_EVENT = 0x1c, + MLX4_EVENT_TYPE_PORT_MNG_CHG_EVENT = 0x1d, MLX4_EVENT_TYPE_NONE = 0xff, }; @@ -235,6 +237,24 @@ enum { MLX4_MAX_FAST_REG_PAGES = 511, }; +enum { + MLX4_DEV_PMC_SUBTYPE_GUID_INFO = 0x14, + MLX4_DEV_PMC_SUBTYPE_PORT_INFO = 0x15, + MLX4_DEV_PMC_SUBTYPE_PKEY_TABLE = 0x16, +}; + +/* Port mgmt change event handling */ +enum { + MLX4_EQ_PORT_INFO_MSTR_SM_LID_CHANGE_MASK = 1 << 0, + MLX4_EQ_PORT_INFO_GID_PFX_CHANGE_MASK = 1 << 1, + MLX4_EQ_PORT_INFO_LID_CHANGE_MASK = 1 << 2, + MLX4_EQ_PORT_INFO_CLIENT_REREG_MASK = 1 << 3, + MLX4_EQ_PORT_INFO_MSTR_SM_SL_CHANGE_MASK = 1 << 4, +}; + +#define MSTR_SM_CHANGE_MASK (MLX4_EQ_PORT_INFO_MSTR_SM_SL_CHANGE_MASK | \ + MLX4_EQ_PORT_INFO_MSTR_SM_LID_CHANGE_MASK) + static inline u64 mlx4_fw_ver(u64 major, u64 minor, u64 subminor) { return (major << 32) | (minor << 16) | subminor; @@ -511,6 +531,81 @@ struct mlx4_dev { int num_vfs; }; +struct mlx4_eqe { + u8 reserved1; + u8 type; + u8 reserved2; + u8 subtype; + union { + u32 raw[6]; + struct { + __be32 cqn; + } __packed comp; + struct { + u16 reserved1; + __be16 token; + u32 reserved2; + u8 reserved3[3]; + u8 status; + __be64 out_param; + } __packed cmd; + struct { + __be32 qpn; + } __packed qp; + struct { + __be32 srqn; + } __packed srq; + struct { + __be32 cqn; + u32 reserved1; + u8 reserved2[3]; + u8 syndrome; + } __packed cq_err; + struct { + u32 reserved1[2]; + __be32 port; + } __packed port_change; + struct { + #define COMM_CHANNEL_BIT_ARRAY_SIZE 4 + u32 reserved; + u32 bit_vec[COMM_CHANNEL_BIT_ARRAY_SIZE]; + } __packed comm_channel_arm; + struct { + u8 port; + u8 reserved[3]; + __be64 mac; + } __packed mac_update; + struct { + __be32 slave_id; + } __packed flr_event; + struct { + __be16 current_temperature; + __be16 warning_threshold; + } __packed warming; + struct { + u8 reserved[3]; + u8 port; + union { + struct { + __be16 mstr_sm_lid; + __be16 port_lid; + __be32 changed_attr; + u8 reserved[3]; + u8 mstr_sm_sl; + __be64 gid_prefix; + } __packed port_info; + struct { + __be32 block_ptr; + __be32 tbl_entries_mask; + } __packed tbl_change_info; + } params; + } __packed port_mgmt_change; + } event; + u8 slave_id; + u8 reserved3[2]; + u8 owner; +} __packed; + struct mlx4_init_port_param { int set_guid0; int set_node_guid; @@ -536,6 +631,8 @@ struct mlx4_init_port_param { #define MLX4_INVALID_SLAVE_ID 0xFF +void handle_port_mgmt_change_event(struct work_struct *work); + static inline int mlx4_is_master(struct mlx4_dev *dev) { return dev->flags & MLX4_FLAG_MASTER; diff --git a/include/linux/mlx4/driver.h b/include/linux/mlx4/driver.h index 5f1298b1b5ef..0f509229fb3d 100644 --- a/include/linux/mlx4/driver.h +++ b/include/linux/mlx4/driver.h @@ -42,13 +42,14 @@ enum mlx4_dev_event { MLX4_DEV_EVENT_PORT_UP, MLX4_DEV_EVENT_PORT_DOWN, MLX4_DEV_EVENT_PORT_REINIT, + MLX4_DEV_EVENT_PORT_MGMT_CHANGE, }; struct mlx4_interface { void * (*add) (struct mlx4_dev *dev); void (*remove)(struct mlx4_dev *dev, void *context); void (*event) (struct mlx4_dev *dev, void *context, - enum mlx4_dev_event event, int port); + enum mlx4_dev_event event, unsigned long param); void * (*get_dev)(struct mlx4_dev *dev, void *context, u8 port); struct list_head list; enum mlx4_protocol protocol; -- cgit v1.2.3-59-g8ed1b From dbf0e4c7257f8d684ec1a3c919853464293de66e Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 9 Jul 2012 11:09:21 -0400 Subject: PCI: EHCI: fix crash during suspend on ASUS computers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quite a few ASUS computers experience a nasty problem, related to the EHCI controllers, when going into system suspend. It was observed that the problem didn't occur if the controllers were not put into the D3 power state before starting the suspend, and commit 151b61284776be2d6f02d48c23c3625678960b97 (USB: EHCI: fix crash during suspend on ASUS computers) was created to do this. It turned out this approach messed up other computers that didn't have the problem -- it prevented USB wakeup from working. Consequently commit c2fb8a3fa25513de8fedb38509b1f15a5bbee47b (USB: add NO_D3_DURING_SLEEP flag and revert 151b61284776be2) was merged; it reverted the earlier commit and added a whitelist of known good board names. Now we know the actual cause of the problem. Thanks to AceLan Kao for tracking it down. According to him, an engineer at ASUS explained that some of their BIOSes contain a bug that was added in an attempt to work around a problem in early versions of Windows. When the computer goes into S3 suspend, the BIOS tries to verify that the EHCI controllers were first quiesced by the OS. Nothing's wrong with this, but the BIOS does it by checking that the PCI COMMAND registers contain 0 without checking the controllers' power state. If the register isn't 0, the BIOS assumes the controller needs to be quiesced and tries to do so. This involves making various MMIO accesses to the controller, which don't work very well if the controller is already in D3. The end result is a system hang or memory corruption. Since the value in the PCI COMMAND register doesn't matter once the controller has been suspended, and since the value will be restored anyway when the controller is resumed, we can work around the BIOS bug simply by setting the register to 0 during system suspend. This patch (as1590) does so and also reverts the second commit mentioned above, which is now unnecessary. In theory we could do this for every PCI device. However to avoid introducing new problems, the patch restricts itself to EHCI host controllers. Finally the affected systems can suspend with USB wakeup working properly. Reference: https://bugzilla.kernel.org/show_bug.cgi?id=37632 Reference: https://bugzilla.kernel.org/show_bug.cgi?id=42728 Based-on-patch-by: AceLan Kao Signed-off-by: Alan Stern Tested-by: Dâniel Fraga Tested-by: Javier Marcet Tested-by: Andrey Rahmatullin Tested-by: Oleksij Rempel Tested-by: Pavel Pisa Cc: stable Acked-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-driver.c | 12 ++++++++++++ drivers/pci/pci.c | 5 ----- drivers/pci/quirks.c | 26 -------------------------- include/linux/pci.h | 2 -- 4 files changed, 12 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index bf0cee629b60..099f46cd8e87 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -748,6 +748,18 @@ static int pci_pm_suspend_noirq(struct device *dev) pci_pm_set_unknown_state(pci_dev); + /* + * Some BIOSes from ASUS have a bug: If a USB EHCI host controller's + * PCI COMMAND register isn't 0, the BIOS assumes that the controller + * hasn't been quiesced and tries to turn it off. If the controller + * is already in D3, this can hang or cause memory corruption. + * + * Since the value of the COMMAND register doesn't matter once the + * device has been suspended, we can safely set it to 0 here. + */ + if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI) + pci_write_config_word(pci_dev, PCI_COMMAND, 0); + return 0; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 77cb54a65cde..447e83472c01 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1744,11 +1744,6 @@ int pci_prepare_to_sleep(struct pci_dev *dev) if (target_state == PCI_POWER_ERROR) return -EIO; - /* Some devices mustn't be in D3 during system sleep */ - if (target_state == PCI_D3hot && - (dev->dev_flags & PCI_DEV_FLAGS_NO_D3_DURING_SLEEP)) - return 0; - pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev)); error = pci_set_power_state(dev, target_state); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 194b243a2817..2a7521677541 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2929,32 +2929,6 @@ static void __devinit disable_igfx_irq(struct pci_dev *dev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0102, disable_igfx_irq); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq); -/* - * The Intel 6 Series/C200 Series chipset's EHCI controllers on many - * ASUS motherboards will cause memory corruption or a system crash - * if they are in D3 while the system is put into S3 sleep. - */ -static void __devinit asus_ehci_no_d3(struct pci_dev *dev) -{ - const char *sys_info; - static const char good_Asus_board[] = "P8Z68-V"; - - if (dev->dev_flags & PCI_DEV_FLAGS_NO_D3_DURING_SLEEP) - return; - if (dev->subsystem_vendor != PCI_VENDOR_ID_ASUSTEK) - return; - sys_info = dmi_get_system_info(DMI_BOARD_NAME); - if (sys_info && memcmp(sys_info, good_Asus_board, - sizeof(good_Asus_board) - 1) == 0) - return; - - dev_info(&dev->dev, "broken D3 during system sleep on ASUS\n"); - dev->dev_flags |= PCI_DEV_FLAGS_NO_D3_DURING_SLEEP; - device_set_wakeup_capable(&dev->dev, false); -} -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1c26, asus_ehci_no_d3); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1c2d, asus_ehci_no_d3); - static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) { diff --git a/include/linux/pci.h b/include/linux/pci.h index fefb4e19bf6a..d8c379dba6ad 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -176,8 +176,6 @@ enum pci_dev_flags { PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2, /* Provide indication device is assigned by a Virtual Machine Manager */ PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) 4, - /* Device causes system crash if in D3 during S3 sleep */ - PCI_DEV_FLAGS_NO_D3_DURING_SLEEP = (__force pci_dev_flags_t) 8, }; enum pci_irq_reroute_variant { -- cgit v1.2.3-59-g8ed1b From 2aca1172c2f5b27fbc37297574f716c1c15f4153 Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Tue, 19 Jun 2012 11:21:41 +0300 Subject: net/mlx4_core: Initialize IB port capabilities for all slaves With IB SR-IOV, each slave has its own separate copy of the port capabilities flags. For example, the master can run a subnet manager (which causes the IsSM bit to be set in the master's port capabilities) without affecting the port capabilities seen by the slaves (the IsSM bit will be seen as cleared in the slaves). Also add a static inline mlx4_master_func_num() to enhance readability of the code. Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/net/ethernet/mellanox/mlx4/main.c | 11 +++++++++++ include/linux/mlx4/device.h | 5 +++++ 2 files changed, 16 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index a0313de122de..83afb1541a74 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -1477,6 +1477,17 @@ static int mlx4_setup_hca(struct mlx4_dev *dev) "with caps = 0\n", port, err); dev->caps.ib_port_def_cap[port] = ib_port_default_caps; + /* initialize per-slave default ib port capabilities */ + if (mlx4_is_master(dev)) { + int i; + for (i = 0; i < dev->num_slaves; i++) { + if (i == mlx4_master_func_num(dev)) + continue; + priv->mfunc.master.slave_state[i].ib_cap_mask[port] = + ib_port_default_caps; + } + } + if (mlx4_is_mfunc(dev)) dev->caps.port_ib_mtu[port] = IB_MTU_2048; else diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 560b2201519f..7fbdc89de495 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -633,6 +633,11 @@ struct mlx4_init_port_param { void handle_port_mgmt_change_event(struct work_struct *work); +static inline int mlx4_master_func_num(struct mlx4_dev *dev) +{ + return dev->caps.function; +} + static inline int mlx4_is_master(struct mlx4_dev *dev) { return dev->flags & MLX4_FLAG_MASTER; -- cgit v1.2.3-59-g8ed1b From 8651f97bd951d0bb1c10fa24e3fa3455193f3548 Mon Sep 17 00:00:00 2001 From: Preeti U Murthy Date: Mon, 9 Jul 2012 10:12:56 +0200 Subject: PM / cpuidle: System resume hang fix with cpuidle On certain bios, resume hangs if cpus are allowed to enter idle states during suspend [1]. This was fixed in apci idle driver [2].But intel_idle driver does not have this fix. Thus instead of replicating the fix in both the idle drivers, or in more platform specific idle drivers if needed, the more general cpuidle infrastructure could handle this. A suspend callback in cpuidle_driver could handle this fix. But a cpuidle_driver provides only basic functionalities like platform idle state detection capability and mechanisms to support entry and exit into CPU idle states. All other cpuidle functions are found in the cpuidle generic infrastructure for good reason that all cpuidle drivers, irrepective of their platforms will support these functions. One option therefore would be to register a suspend callback in cpuidle which handles this fix. This could be called through a PM_SUSPEND_PREPARE notifier. But this is too generic a notfier for a driver to handle. Also, ideally the job of cpuidle is not to handle side effects of suspend. It should expose the interfaces which "handle cpuidle 'during' suspend" or any other operation, which the subsystems call during that respective operation. The fix demands that during suspend, no cpus should be allowed to enter deep C-states. The interface cpuidle_uninstall_idle_handler() in cpuidle ensures that. Not just that it also kicks all the cpus which are already in idle out of their idle states which was being done during cpu hotplug through a CPU_DYING_FROZEN callbacks. Now the question arises about when during suspend should cpuidle_uninstall_idle_handler() be called. Since we are dealing with drivers it seems best to call this function during dpm_suspend(). Delaying the call till dpm_suspend_noirq() does no harm, as long as it is before cpu_hotplug_begin() to avoid race conditions with cpu hotpulg operations. In dpm_suspend_noirq(), it would be wise to place this call before suspend_device_irqs() to avoid ugly interactions with the same. Ananlogously, during resume. References: [1] https://bugs.launchpad.net/ubuntu/+source/linux/+bug/674075. [2] http://marc.info/?l=linux-pm&m=133958534231884&w=2 Reported-and-tested-by: Dave Hansen Signed-off-by: Preeti U Murthy Reviewed-by: Srivatsa S. Bhat Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_idle.c | 30 +----------------------------- drivers/base/power/main.c | 4 +++- drivers/cpuidle/cpuidle.c | 16 ++++++++++++++++ include/linux/cpuidle.h | 4 ++++ 4 files changed, 24 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 47a8caa89dbe..d8366ee75716 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -221,10 +221,6 @@ static void lapic_timer_state_broadcast(struct acpi_processor *pr, #endif -/* - * Suspend / resume control - */ -static int acpi_idle_suspend; static u32 saved_bm_rld; static void acpi_idle_bm_rld_save(void) @@ -243,21 +239,13 @@ static void acpi_idle_bm_rld_restore(void) int acpi_processor_suspend(struct acpi_device * device, pm_message_t state) { - if (acpi_idle_suspend == 1) - return 0; - acpi_idle_bm_rld_save(); - acpi_idle_suspend = 1; return 0; } int acpi_processor_resume(struct acpi_device * device) { - if (acpi_idle_suspend == 0) - return 0; - acpi_idle_bm_rld_restore(); - acpi_idle_suspend = 0; return 0; } @@ -763,11 +751,6 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, local_irq_disable(); - if (acpi_idle_suspend) { - local_irq_enable(); - cpu_relax(); - return -EBUSY; - } lapic_timer_state_broadcast(pr, cx, 1); kt1 = ktime_get_real(); @@ -838,11 +821,6 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, local_irq_disable(); - if (acpi_idle_suspend) { - local_irq_enable(); - cpu_relax(); - return -EBUSY; - } if (cx->entry_method != ACPI_CSTATE_FFH) { current_thread_info()->status &= ~TS_POLLING; @@ -928,8 +906,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, drv, drv->safe_state_index); } else { local_irq_disable(); - if (!acpi_idle_suspend) - acpi_safe_halt(); + acpi_safe_halt(); local_irq_enable(); return -EBUSY; } @@ -937,11 +914,6 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, local_irq_disable(); - if (acpi_idle_suspend) { - local_irq_enable(); - cpu_relax(); - return -EBUSY; - } if (cx->entry_method != ACPI_CSTATE_FFH) { current_thread_info()->status &= ~TS_POLLING; diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 9cb845e49334..63048f79de5f 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -28,7 +28,7 @@ #include #include #include - +#include #include "../base.h" #include "power.h" @@ -467,6 +467,7 @@ static void dpm_resume_noirq(pm_message_t state) mutex_unlock(&dpm_list_mtx); dpm_show_time(starttime, state, "noirq"); resume_device_irqs(); + cpuidle_resume(); } /** @@ -867,6 +868,7 @@ static int dpm_suspend_noirq(pm_message_t state) ktime_t starttime = ktime_get(); int error = 0; + cpuidle_pause(); suspend_device_irqs(); mutex_lock(&dpm_list_mtx); while (!list_empty(&dpm_late_early_list)) { diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 04e4b7674a47..efa9a2ca30e7 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -201,6 +201,22 @@ void cpuidle_resume_and_unlock(void) EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock); +/* Currently used in suspend/resume path to suspend cpuidle */ +void cpuidle_pause(void) +{ + mutex_lock(&cpuidle_lock); + cpuidle_uninstall_idle_handler(); + mutex_unlock(&cpuidle_lock); +} + +/* Currently used in suspend/resume path to resume cpuidle */ +void cpuidle_resume(void) +{ + mutex_lock(&cpuidle_lock); + cpuidle_install_idle_handler(); + mutex_unlock(&cpuidle_lock); +} + /** * cpuidle_wrap_enter - performs timekeeping and irqen around enter function * @dev: pointer to a valid cpuidle_device object diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 524bb6f3b6c4..ca6cdf55eb18 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -145,6 +145,8 @@ extern void cpuidle_unregister_device(struct cpuidle_device *dev); extern void cpuidle_pause_and_lock(void); extern void cpuidle_resume_and_unlock(void); +extern void cpuidle_pause(void); +extern void cpuidle_resume(void); extern int cpuidle_enable_device(struct cpuidle_device *dev); extern void cpuidle_disable_device(struct cpuidle_device *dev); extern int cpuidle_wrap_enter(struct cpuidle_device *dev, @@ -168,6 +170,8 @@ static inline void cpuidle_unregister_device(struct cpuidle_device *dev) { } static inline void cpuidle_pause_and_lock(void) { } static inline void cpuidle_resume_and_unlock(void) { } +static inline void cpuidle_pause(void) { } +static inline void cpuidle_resume(void) { } static inline int cpuidle_enable_device(struct cpuidle_device *dev) {return -ENODEV; } static inline void cpuidle_disable_device(struct cpuidle_device *dev) { } -- cgit v1.2.3-59-g8ed1b From ad7eee98bef92481581060801bdfd1b25a6106c0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 10 Jul 2012 06:18:44 +0000 Subject: etherdevice: introduce eth_broadcast_addr A lot of code has either the memset or an inefficient copy from a static array that contains the all-ones broadcast address. Introduce eth_broadcast_addr() to fill an address with all ones, making the code clearer and allowing us to get rid of some constant arrays. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- include/linux/etherdevice.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include/linux') diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index 3d406e0ede6d..98a27cccedfd 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -137,6 +137,17 @@ static inline void random_ether_addr(u8 *addr) addr [0] |= 0x02; /* set local assignment bit (IEEE802) */ } +/** + * eth_broadcast_addr - Assign broadcast address + * @addr: Pointer to a six-byte array containing the Ethernet address + * + * Assign the broadcast address to the given address array. + */ +static inline void eth_broadcast_addr(u8 *addr) +{ + memset(addr, 0xff, ETH_ALEN); +} + /** * eth_hw_addr_random - Generate software assigned random Ethernet and * set device flag -- cgit v1.2.3-59-g8ed1b From fd0ea65d3e675e479e022b6cfc9ebe1864c76afc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 30 Apr 2012 23:31:57 +0200 Subject: mmc: extend and rename cd-gpio helpers to handle more slot GPIO functions GPIOs can be used in MMC/SD-card slots not only for hotplug detection, but also to implement the write-protection pin. Rename cd-gpio helpers to slot-gpio to make addition of further slot GPIO functions possible. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/core/Makefile | 2 +- drivers/mmc/core/cd-gpio.c | 83 ----------------------------------------- drivers/mmc/core/slot-gpio.c | 83 +++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/tmio_mmc_pio.c | 6 +-- include/linux/mmc/cd-gpio.h | 18 --------- include/linux/mmc/slot-gpio.h | 18 +++++++++ 6 files changed, 105 insertions(+), 105 deletions(-) delete mode 100644 drivers/mmc/core/cd-gpio.c create mode 100644 drivers/mmc/core/slot-gpio.c delete mode 100644 include/linux/mmc/cd-gpio.h create mode 100644 include/linux/mmc/slot-gpio.h (limited to 'include/linux') diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index dca4428380f1..38ed210ce2f3 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -7,6 +7,6 @@ mmc_core-y := core.o bus.o host.o \ mmc.o mmc_ops.o sd.o sd_ops.o \ sdio.o sdio_ops.o sdio_bus.o \ sdio_cis.o sdio_io.o sdio_irq.o \ - quirks.o cd-gpio.o + quirks.o slot-gpio.o mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/mmc/core/cd-gpio.c b/drivers/mmc/core/cd-gpio.c deleted file mode 100644 index 8f5dc08d6598..000000000000 --- a/drivers/mmc/core/cd-gpio.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Generic GPIO card-detect helper - * - * Copyright (C) 2011, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -struct mmc_cd_gpio { - unsigned int gpio; - char label[0]; -}; - -static irqreturn_t mmc_cd_gpio_irqt(int irq, void *dev_id) -{ - /* Schedule a card detection after a debounce timeout */ - mmc_detect_change(dev_id, msecs_to_jiffies(100)); - return IRQ_HANDLED; -} - -int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio) -{ - size_t len = strlen(dev_name(host->parent)) + 4; - struct mmc_cd_gpio *cd; - int irq = gpio_to_irq(gpio); - int ret; - - if (irq < 0) - return irq; - - cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL); - if (!cd) - return -ENOMEM; - - snprintf(cd->label, len, "%s cd", dev_name(host->parent)); - - ret = gpio_request_one(gpio, GPIOF_DIR_IN, cd->label); - if (ret < 0) - goto egpioreq; - - ret = request_threaded_irq(irq, NULL, mmc_cd_gpio_irqt, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, cd->label, host); - if (ret < 0) - goto eirqreq; - - cd->gpio = gpio; - host->hotplug.irq = irq; - host->hotplug.handler_priv = cd; - - return 0; - -eirqreq: - gpio_free(gpio); -egpioreq: - kfree(cd); - return ret; -} -EXPORT_SYMBOL(mmc_cd_gpio_request); - -void mmc_cd_gpio_free(struct mmc_host *host) -{ - struct mmc_cd_gpio *cd = host->hotplug.handler_priv; - - if (!cd) - return; - - free_irq(host->hotplug.irq, host); - gpio_free(cd->gpio); - kfree(cd); -} -EXPORT_SYMBOL(mmc_cd_gpio_free); diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c new file mode 100644 index 000000000000..979671053436 --- /dev/null +++ b/drivers/mmc/core/slot-gpio.c @@ -0,0 +1,83 @@ +/* + * Generic GPIO card-detect helper + * + * Copyright (C) 2011, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct mmc_gpio { + unsigned int cd_gpio; + char cd_label[0]; +}; + +static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) +{ + /* Schedule a card detection after a debounce timeout */ + mmc_detect_change(dev_id, msecs_to_jiffies(100)); + return IRQ_HANDLED; +} + +int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) +{ + size_t len = strlen(dev_name(host->parent)) + 4; + struct mmc_gpio *ctx; + int irq = gpio_to_irq(gpio); + int ret; + + if (irq < 0) + return irq; + + ctx = kmalloc(sizeof(*ctx) + len, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent)); + + ret = gpio_request_one(gpio, GPIOF_DIR_IN, ctx->cd_label); + if (ret < 0) + goto egpioreq; + + ret = request_threaded_irq(irq, NULL, mmc_gpio_cd_irqt, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + ctx->cd_label, host); + if (ret < 0) + goto eirqreq; + + ctx->cd_gpio = gpio; + host->hotplug.irq = irq; + host->hotplug.handler_priv = ctx; + + return 0; + +eirqreq: + gpio_free(gpio); +egpioreq: + kfree(ctx); + return ret; +} +EXPORT_SYMBOL(mmc_gpio_request_cd); + +void mmc_gpio_free_cd(struct mmc_host *host) +{ + struct mmc_gpio *ctx = host->hotplug.handler_priv; + + if (!ctx) + return; + + free_irq(host->hotplug.irq, host); + gpio_free(ctx->cd_gpio); + kfree(ctx); +} +EXPORT_SYMBOL(mmc_gpio_free_cd); diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 0ad3917cafd1..7ffc489bed35 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -34,9 +34,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -977,7 +977,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, tmio_mmc_enable_mmc_irqs(_host, irq_mask); if (pdata->flags & TMIO_MMC_USE_GPIO_CD) { - ret = mmc_cd_gpio_request(mmc, pdata->cd_gpio); + ret = mmc_gpio_request_cd(mmc, pdata->cd_gpio); if (ret < 0) { tmio_mmc_host_remove(_host); return ret; @@ -1009,7 +1009,7 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) * This means we can miss a card-eject, but this is anyway * possible, because of delayed processing of hotplug events. */ - mmc_cd_gpio_free(mmc); + mmc_gpio_free_cd(mmc); if (!host->native_hotplug) pm_runtime_get_sync(&pdev->dev); diff --git a/include/linux/mmc/cd-gpio.h b/include/linux/mmc/cd-gpio.h deleted file mode 100644 index cefaba038ccb..000000000000 --- a/include/linux/mmc/cd-gpio.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Generic GPIO card-detect helper header - * - * Copyright (C) 2011, Guennadi Liakhovetski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef MMC_CD_GPIO_H -#define MMC_CD_GPIO_H - -struct mmc_host; -int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio); -void mmc_cd_gpio_free(struct mmc_host *host); - -#endif diff --git a/include/linux/mmc/slot-gpio.h b/include/linux/mmc/slot-gpio.h new file mode 100644 index 000000000000..edfaa3254373 --- /dev/null +++ b/include/linux/mmc/slot-gpio.h @@ -0,0 +1,18 @@ +/* + * Generic GPIO card-detect helper header + * + * Copyright (C) 2011, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MMC_SLOT_GPIO_H +#define MMC_SLOT_GPIO_H + +struct mmc_host; +int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio); +void mmc_gpio_free_cd(struct mmc_host *host); + +#endif -- cgit v1.2.3-59-g8ed1b From 475d0094293b51353e342d1198377967dbc48169 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 11 Jul 2012 15:16:37 +1000 Subject: of: Improve prom_update_property() function prom_update_property() currently fails if the property doesn't actually exist yet which isn't what we want. Change to add-or-update instead of update-only, then we can remove a lot duplicated lines. Suggested-by: Grant Likely Signed-off-by: Dong Aisheng Acked-by: Rob Herring Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/85xx/p1022_ds.c | 8 +------- arch/powerpc/platforms/pseries/mobility.c | 8 +------- arch/powerpc/platforms/pseries/reconfig.c | 16 ++++++---------- drivers/of/base.c | 15 +++++++++++---- fs/proc/proc_devtree.c | 5 +++++ include/linux/of.h | 3 +-- 6 files changed, 25 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/arch/powerpc/platforms/85xx/p1022_ds.c b/arch/powerpc/platforms/85xx/p1022_ds.c index 74e310b4b460..31d18b964f94 100644 --- a/arch/powerpc/platforms/85xx/p1022_ds.c +++ b/arch/powerpc/platforms/85xx/p1022_ds.c @@ -348,13 +348,7 @@ void __init p1022_ds_pic_init(void) */ static void __init disable_one_node(struct device_node *np, struct property *new) { - struct property *old; - - old = of_find_property(np, new->name, NULL); - if (old) - prom_update_property(np, new, old); - else - prom_add_property(np, new); + prom_update_property(np, new); } /* TRUE if there is a "video=fslfb" command-line parameter. */ diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c index 029a562af373..dd30b12edfe4 100644 --- a/arch/powerpc/platforms/pseries/mobility.c +++ b/arch/powerpc/platforms/pseries/mobility.c @@ -67,7 +67,6 @@ static int update_dt_property(struct device_node *dn, struct property **prop, const char *name, u32 vd, char *value) { struct property *new_prop = *prop; - struct property *old_prop; int more = 0; /* A negative 'vd' value indicates that only part of the new property @@ -117,12 +116,7 @@ static int update_dt_property(struct device_node *dn, struct property **prop, } if (!more) { - old_prop = of_find_property(dn, new_prop->name, NULL); - if (old_prop) - prom_update_property(dn, new_prop, old_prop); - else - prom_add_property(dn, new_prop); - + prom_update_property(dn, new_prop); new_prop = NULL; } diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index 7b3bf76ef834..39f71fba9b38 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c @@ -432,7 +432,7 @@ static int do_update_property(char *buf, size_t bufsize) unsigned char *value; char *name, *end, *next_prop; int rc, length; - struct property *newprop, *oldprop; + struct property *newprop; buf = parse_node(buf, bufsize, &np); end = buf + bufsize; @@ -443,6 +443,9 @@ static int do_update_property(char *buf, size_t bufsize) if (!next_prop) return -EINVAL; + if (!strlen(name)) + return -ENODEV; + newprop = new_property(name, length, value, NULL); if (!newprop) return -ENOMEM; @@ -450,18 +453,11 @@ static int do_update_property(char *buf, size_t bufsize) if (!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size")) slb_set_size(*(int *)value); - oldprop = of_find_property(np, name,NULL); - if (!oldprop) { - if (strlen(name)) - return prom_add_property(np, newprop); - return -ENODEV; - } - upd_value.node = np; upd_value.property = newprop; pSeries_reconfig_notify(PSERIES_UPDATE_PROPERTY, &upd_value); - rc = prom_update_property(np, newprop, oldprop); + rc = prom_update_property(np, newprop); if (rc) return rc; @@ -486,7 +482,7 @@ static int do_update_property(char *buf, size_t bufsize) rc = pSeries_reconfig_notify(action, value); if (rc) { - prom_update_property(np, oldprop, newprop); + prom_update_property(np, newprop); return rc; } } diff --git a/drivers/of/base.c b/drivers/of/base.c index eada3f4ef801..bc86ea2af668 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1073,7 +1073,8 @@ int prom_remove_property(struct device_node *np, struct property *prop) } /* - * prom_update_property - Update a property in a node. + * prom_update_property - Update a property in a node, if the property does + * not exist, add it. * * Note that we don't actually remove it, since we have given out * who-knows-how-many pointers to the data using get-property. @@ -1081,13 +1082,19 @@ int prom_remove_property(struct device_node *np, struct property *prop) * and add the new property to the property list */ int prom_update_property(struct device_node *np, - struct property *newprop, - struct property *oldprop) + struct property *newprop) { - struct property **next; + struct property **next, *oldprop; unsigned long flags; int found = 0; + if (!newprop->name) + return -EINVAL; + + oldprop = of_find_property(np, newprop->name, NULL); + if (!oldprop) + return prom_add_property(np, newprop); + write_lock_irqsave(&devtree_lock, flags); next = &np->properties; while (*next) { diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index 927cbd115e53..df7dd08d4391 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c @@ -101,6 +101,11 @@ void proc_device_tree_update_prop(struct proc_dir_entry *pde, { struct proc_dir_entry *ent; + if (!oldprop) { + proc_device_tree_add_prop(pde, newprop); + return; + } + for (ent = pde->subdir; ent != NULL; ent = ent->next) if (ent->data == oldprop) break; diff --git a/include/linux/of.h b/include/linux/of.h index 2ec1083af7ff..b27c87191df2 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -260,8 +260,7 @@ extern int of_machine_is_compatible(const char *compat); extern int prom_add_property(struct device_node* np, struct property* prop); extern int prom_remove_property(struct device_node *np, struct property *prop); extern int prom_update_property(struct device_node *np, - struct property *newprop, - struct property *oldprop); + struct property *newprop); #if defined(CONFIG_OF_DYNAMIC) /* For updating the device tree at runtime */ -- cgit v1.2.3-59-g8ed1b From b6242b9b45e84ef71c59002cd128c3197938cb2f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 10 Jul 2012 03:27:56 -0700 Subject: tcp: Remove tw->tw_peer No longer used. Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 - net/ipv4/tcp_minisocks.c | 16 ++-------------- 2 files changed, 2 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 7d3bcedc062a..2de9cf46f9fc 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -506,7 +506,6 @@ struct tcp_timewait_sock { u32 tw_rcv_wnd; u32 tw_ts_recent; long tw_ts_recent_stamp; - struct inet_peer *tw_peer; #ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *tw_md5_key; #endif diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index a51aa534dab1..65608863fdee 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -267,12 +267,9 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) const struct inet_connection_sock *icsk = inet_csk(sk); const struct tcp_sock *tp = tcp_sk(sk); bool recycle_ok = false; - bool recycle_on = false; - if (tcp_death_row.sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp) { + if (tcp_death_row.sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp) recycle_ok = tcp_remember_stamp(sk); - recycle_on = true; - } if (tcp_death_row.tw_count < tcp_death_row.sysctl_max_tw_buckets) tw = inet_twsk_alloc(sk, state); @@ -281,7 +278,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw); const int rto = (icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1); struct inet_sock *inet = inet_sk(sk); - struct inet_peer *peer = NULL; tw->tw_transparent = inet->transparent; tw->tw_rcv_wscale = tp->rx_opt.rcv_wscale; @@ -305,12 +301,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) } #endif - if (recycle_on) - peer = icsk->icsk_af_ops->get_peer(sk); - tcptw->tw_peer = peer; - if (peer) - atomic_inc(&peer->refcnt); - #ifdef CONFIG_TCP_MD5SIG /* * The timewait bucket does not have the key DB from the @@ -362,11 +352,9 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) void tcp_twsk_destructor(struct sock *sk) { +#ifdef CONFIG_TCP_MD5SIG struct tcp_timewait_sock *twsk = tcp_twsk(sk); - if (twsk->tw_peer) - inet_putpeer(twsk->tw_peer); -#ifdef CONFIG_TCP_MD5SIG if (twsk->tw_md5_key) { tcp_free_md5sig_pool(); kfree_rcu(twsk->tw_md5_key, rcu); -- cgit v1.2.3-59-g8ed1b From 87a50699cb6d169591cc776fb82683a2c77cecac Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 10 Jul 2012 05:06:14 -0700 Subject: rtnetlink: Remove ts/tsage args to rtnl_put_cacheinfo(). Nobody provides non-zero values any longer. Signed-off-by: David S. Miller --- include/linux/rtnetlink.h | 3 +-- net/core/rtnetlink.c | 4 +--- net/decnet/dn_route.c | 2 +- net/ipv4/route.c | 3 +-- net/ipv6/route.c | 3 +-- 5 files changed, 5 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index ea60b0854109..db71c4ad8624 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -619,8 +619,7 @@ extern void rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, extern void rtnl_set_sk_err(struct net *net, u32 group, int error); extern int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics); extern int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, - u32 id, u32 ts, u32 tsage, long expires, - u32 error); + u32 id, long expires, u32 error); extern void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 2b325c340b44..64127eee786d 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -615,7 +615,7 @@ nla_put_failure: EXPORT_SYMBOL(rtnetlink_put_metrics); int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id, - u32 ts, u32 tsage, long expires, u32 error) + long expires, u32 error) { struct rta_cacheinfo ci = { .rta_lastuse = jiffies_to_clock_t(jiffies - dst->lastuse), @@ -623,8 +623,6 @@ int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id, .rta_clntref = atomic_read(&(dst->__refcnt)), .rta_error = error, .rta_id = id, - .rta_ts = ts, - .rta_tsage = tsage, }; if (expires) diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 707027fae8ab..b5594cc73ee1 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1590,7 +1590,7 @@ static int dn_rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq, goto errout; expires = rt->dst.expires ? rt->dst.expires - jiffies : 0; - if (rtnl_put_cacheinfo(skb, &rt->dst, 0, 0, 0, expires, + if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) goto errout; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index d4834e2914a0..67b08745daf9 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2931,8 +2931,7 @@ static int rt_fill_info(struct net *net, goto nla_put_failure; } - if (rtnl_put_cacheinfo(skb, &rt->dst, id, 0, 0, - expires, error) < 0) + if (rtnl_put_cacheinfo(skb, &rt->dst, id, expires, error) < 0) goto nla_put_failure; return nlmsg_end(skb, nlh); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index b7eb51e1a0e1..563f12c1c99c 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2471,8 +2471,7 @@ static int rt6_fill_node(struct net *net, else expires = INT_MAX; - if (rtnl_put_cacheinfo(skb, &rt->dst, 0, 0, 0, - expires, rt->dst.error) < 0) + if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, rt->dst.error) < 0) goto nla_put_failure; return nlmsg_end(skb, nlh); -- cgit v1.2.3-59-g8ed1b From e1612de9e4cdf375c3cf1c72434ab8abdcb3927e Mon Sep 17 00:00:00 2001 From: Haren Myneni Date: Wed, 11 Jul 2012 15:18:44 +1000 Subject: powerpc: Disable /dev/port interface on systems without an ISA bridge Some power systems do not have legacy ISA devices. So, /dev/port is not a valid interface on these systems. User level tools such as kbdrate is trying to access the device using this interface which is causing the system crash. This patch will fix this issue by not creating this interface on these powerpc systems. Signed-off-by: Haren Myneni Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/io.h | 8 ++++++++ drivers/char/mem.c | 11 ++++++++++- include/linux/io.h | 9 +++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/arch/powerpc/include/asm/io.h b/arch/powerpc/include/asm/io.h index a3855b81eada..f94ef4213e9d 100644 --- a/arch/powerpc/include/asm/io.h +++ b/arch/powerpc/include/asm/io.h @@ -20,6 +20,14 @@ extern int check_legacy_ioport(unsigned long base_port); #define _PNPWRP 0xa79 #define PNPBIOS_BASE 0xf000 +#if defined(CONFIG_PPC64) && defined(CONFIG_PCI) +extern struct pci_dev *isa_bridge_pcidev; +/* + * has legacy ISA devices ? + */ +#define arch_has_dev_port() (isa_bridge_pcidev != NULL) +#endif + #include #include diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 67c3371723cc..e5eedfa24c91 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -27,14 +27,16 @@ #include #include #include +#include #include -#include #ifdef CONFIG_IA64 # include #endif +#define DEVPORT_MINOR 4 + static inline unsigned long size_inside_page(unsigned long start, unsigned long size) { @@ -894,6 +896,13 @@ static int __init chr_dev_init(void) for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) { if (!devlist[minor].name) continue; + + /* + * Create /dev/port? + */ + if ((minor == DEVPORT_MINOR) && !arch_has_dev_port()) + continue; + device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor), NULL, devlist[minor].name); } diff --git a/include/linux/io.h b/include/linux/io.h index 7fd2d2138bf3..069e4075f872 100644 --- a/include/linux/io.h +++ b/include/linux/io.h @@ -67,4 +67,13 @@ int check_signature(const volatile void __iomem *io_addr, const unsigned char *signature, int length); void devm_ioremap_release(struct device *dev, void *res); +/* + * Some systems do not have legacy ISA devices. + * /dev/port is not a valid interface on these systems. + * So for those archs, should define the following symbol. + */ +#ifndef arch_has_dev_port +#define arch_has_dev_port() (1) +#endif + #endif /* _LINUX_IO_H */ -- cgit v1.2.3-59-g8ed1b From 48ee3569f31d91084dc694fef5517eb782428083 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 11 Jul 2012 02:39:24 -0700 Subject: ipv6: Move ipv6 twsk accessors outside of CONFIG_IPV6 ifdefs. Fixes build when ipv6 is disabled. Reported-by: Fengguang Wu Signed-off-by: David S. Miller --- include/linux/ipv6.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 8260ef779762..bc6c8fd8ed01 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -410,6 +410,22 @@ struct tcp6_sock { extern int inet6_sk_rebuild_header(struct sock *sk); +struct inet6_timewait_sock { + struct in6_addr tw_v6_daddr; + struct in6_addr tw_v6_rcv_saddr; +}; + +struct tcp6_timewait_sock { + struct tcp_timewait_sock tcp6tw_tcp; + struct inet6_timewait_sock tcp6tw_inet6; +}; + +static inline struct inet6_timewait_sock *inet6_twsk(const struct sock *sk) +{ + return (struct inet6_timewait_sock *)(((u8 *)sk) + + inet_twsk(sk)->tw_ipv6_offset); +} + #if IS_ENABLED(CONFIG_IPV6) static inline struct ipv6_pinfo * inet6_sk(const struct sock *__sk) { @@ -459,28 +475,12 @@ static inline void inet_sk_copy_descendant(struct sock *sk_to, #define __ipv6_only_sock(sk) (inet6_sk(sk)->ipv6only) #define ipv6_only_sock(sk) ((sk)->sk_family == PF_INET6 && __ipv6_only_sock(sk)) -struct inet6_timewait_sock { - struct in6_addr tw_v6_daddr; - struct in6_addr tw_v6_rcv_saddr; -}; - -struct tcp6_timewait_sock { - struct tcp_timewait_sock tcp6tw_tcp; - struct inet6_timewait_sock tcp6tw_inet6; -}; - static inline u16 inet6_tw_offset(const struct proto *prot) { return prot->twsk_prot->twsk_obj_size - sizeof(struct inet6_timewait_sock); } -static inline struct inet6_timewait_sock *inet6_twsk(const struct sock *sk) -{ - return (struct inet6_timewait_sock *)(((u8 *)sk) + - inet_twsk(sk)->tw_ipv6_offset); -} - static inline struct in6_addr *__inet6_rcv_saddr(const struct sock *sk) { return likely(sk->sk_state != TCP_TIME_WAIT) ? -- cgit v1.2.3-59-g8ed1b From 0cd76dd13bdd2f7f02a2dc931e808e92b191082f Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Jan 2012 19:40:52 +0100 Subject: iommu: Add domain-attribute handlers This patch introduces an extension to the iommu-api to get and set attributes for an iommu_domain. Two functions are introduced for this: * iommu_domain_get_attr() * iommu_domain_set_attr() These functions will be used to make the iommu-api suitable for GART-like IOMMUs and to implement hardware-specifc api-extensions. Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 20 ++++++++++++++++++++ include/linux/iommu.h | 28 +++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 8b9ded88e6f5..c39972d8ded3 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -344,3 +344,23 @@ int iommu_device_group(struct device *dev, unsigned int *groupid) return -ENODEV; } EXPORT_SYMBOL_GPL(iommu_device_group); + +int iommu_domain_get_attr(struct iommu_domain *domain, + enum iommu_attr attr, void *data) +{ + if (!domain->ops->domain_get_attr) + return -EINVAL; + + return domain->ops->domain_get_attr(domain, attr, data); +} +EXPORT_SYMBOL_GPL(iommu_domain_get_attr); + +int iommu_domain_set_attr(struct iommu_domain *domain, + enum iommu_attr attr, void *data) +{ + if (!domain->ops->domain_set_attr) + return -EINVAL; + + return domain->ops->domain_set_attr(domain, attr, data); +} +EXPORT_SYMBOL_GPL(iommu_domain_set_attr); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 450293f6d68b..0eef096183e8 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -47,6 +47,10 @@ struct iommu_domain { #define IOMMU_CAP_CACHE_COHERENCY 0x1 #define IOMMU_CAP_INTR_REMAP 0x2 /* isolates device intrs */ +enum iommu_attr { + DOMAIN_ATTR_MAX, +}; + #ifdef CONFIG_IOMMU_API /** @@ -59,7 +63,8 @@ struct iommu_domain { * @unmap: unmap a physically contiguous memory region from an iommu domain * @iova_to_phys: translate iova to physical address * @domain_has_cap: domain capabilities query - * @commit: commit iommu domain + * @domain_get_attr: Query domain attributes + * @domain_set_attr: Change domain attributes * @pgsize_bitmap: bitmap of supported page sizes */ struct iommu_ops { @@ -76,6 +81,10 @@ struct iommu_ops { int (*domain_has_cap)(struct iommu_domain *domain, unsigned long cap); int (*device_group)(struct device *dev, unsigned int *groupid); + int (*domain_get_attr)(struct iommu_domain *domain, + enum iommu_attr attr, void *data); + int (*domain_set_attr)(struct iommu_domain *domain, + enum iommu_attr attr, void *data); unsigned long pgsize_bitmap; }; @@ -99,6 +108,11 @@ extern void iommu_set_fault_handler(struct iommu_domain *domain, iommu_fault_handler_t handler, void *token); extern int iommu_device_group(struct device *dev, unsigned int *groupid); +extern int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr, + void *data); +extern int iommu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr, + void *data); + /** * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework * @domain: the iommu domain where the fault has happened @@ -202,6 +216,18 @@ static inline int iommu_device_group(struct device *dev, unsigned int *groupid) return -ENODEV; } +static inline int iommu_domain_get_attr(struct iommu_domain *domain, + enum iommu_attr attr, void *data) +{ + return -EINVAL; +} + +static inline int iommu_domain_set_attr(struct iommu_domain *domain, + enum iommu_attr attr, void *data) +{ + return -EINVAL; +} + #endif /* CONFIG_IOMMU_API */ #endif /* __LINUX_IOMMU_H */ -- cgit v1.2.3-59-g8ed1b From 0ff64f80e075ae036a4c80c7d7752b1e07fed792 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Jan 2012 19:40:53 +0100 Subject: iommu/amd: Implement DOMAIN_ATTR_GEOMETRY attribute Implement the attribute itself and add the code for the AMD IOMMU driver. Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 4 ++++ drivers/iommu/iommu.c | 19 ++++++++++++++++--- include/linux/iommu.h | 8 ++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index a2e418cba0ff..259a6beddece 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3069,6 +3069,10 @@ static int amd_iommu_domain_init(struct iommu_domain *dom) dom->priv = domain; + dom->geometry.aperture_start = 0; + dom->geometry.aperture_end = ~0ULL; + dom->geometry.force_aperture = true; + return 0; out_free: diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index c39972d8ded3..ed5e0a553ca7 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -348,10 +348,23 @@ EXPORT_SYMBOL_GPL(iommu_device_group); int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr attr, void *data) { - if (!domain->ops->domain_get_attr) - return -EINVAL; + struct iommu_domain_geometry *geometry; + int ret = 0; + + switch (attr) { + case DOMAIN_ATTR_GEOMETRY: + geometry = data; + *geometry = domain->geometry; + + break; + default: + if (!domain->ops->domain_get_attr) + return -EINVAL; - return domain->ops->domain_get_attr(domain, attr, data); + ret = domain->ops->domain_get_attr(domain, attr, data); + } + + return ret; } EXPORT_SYMBOL_GPL(iommu_domain_get_attr); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 0eef096183e8..f7df4aa527f3 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -37,11 +37,18 @@ struct iommu_domain; typedef int (*iommu_fault_handler_t)(struct iommu_domain *, struct device *, unsigned long, int, void *); +struct iommu_domain_geometry { + dma_addr_t aperture_start; /* First address that can be mapped */ + dma_addr_t aperture_end; /* Last address that can be mapped */ + bool force_aperture; /* DMA only allowed in mappable range? */ +}; + struct iommu_domain { struct iommu_ops *ops; void *priv; iommu_fault_handler_t handler; void *handler_token; + struct iommu_domain_geometry geometry; }; #define IOMMU_CAP_CACHE_COHERENCY 0x1 @@ -49,6 +56,7 @@ struct iommu_domain { enum iommu_attr { DOMAIN_ATTR_MAX, + DOMAIN_ATTR_GEOMETRY, }; #ifdef CONFIG_IOMMU_API -- cgit v1.2.3-59-g8ed1b From 6751ed65dc6642af64f7b8a440a75563c8aab7ae Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Wed, 11 Jul 2012 10:20:47 -0700 Subject: x86/mce: Fix siginfo_t->si_addr value for non-recoverable memory faults In commit dad1743e5993f1 ("x86/mce: Only restart instruction after machine check recovery if it is safe") we fixed mce_notify_process() to force a signal to the current process if it was not restartable (RIPV bit not set in MCG_STATUS). But doing it here means that the process doesn't get told the virtual address of the fault via siginfo_t->si_addr. This would prevent application level recovery from the fault. Make a new MF_MUST_KILL flag bit for memory_failure() et al. to use so that we will provide the right information with the signal. Signed-off-by: Tony Luck Acked-by: Borislav Petkov Cc: stable@kernel.org # 3.4+ --- arch/x86/kernel/cpu/mcheck/mce.c | 6 ++++-- include/linux/mm.h | 1 + mm/memory-failure.c | 14 ++++++++------ 3 files changed, 13 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index da27c5d2168a..c46ed494f002 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -1186,6 +1186,7 @@ void mce_notify_process(void) { unsigned long pfn; struct mce_info *mi = mce_find_info(); + int flags = MF_ACTION_REQUIRED; if (!mi) mce_panic("Lost physical address for unconsumed uncorrectable error", NULL, NULL); @@ -1200,8 +1201,9 @@ void mce_notify_process(void) * doomed. We still need to mark the page as poisoned and alert any * other users of the page. */ - if (memory_failure(pfn, MCE_VECTOR, MF_ACTION_REQUIRED) < 0 || - mi->restartable == 0) { + if (!mi->restartable) + flags |= MF_MUST_KILL; + if (memory_failure(pfn, MCE_VECTOR, flags) < 0) { pr_err("Memory error not recovered"); force_sig(SIGBUS, current); } diff --git a/include/linux/mm.h b/include/linux/mm.h index b36d08ce5c57..f9f279cf5b1b 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1591,6 +1591,7 @@ void vmemmap_populate_print_last(void); enum mf_flags { MF_COUNT_INCREASED = 1 << 0, MF_ACTION_REQUIRED = 1 << 1, + MF_MUST_KILL = 1 << 2, }; extern int memory_failure(unsigned long pfn, int trapno, int flags); extern void memory_failure_queue(unsigned long pfn, int trapno, int flags); diff --git a/mm/memory-failure.c b/mm/memory-failure.c index ab1e7145e290..de4ce7058450 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -345,14 +345,14 @@ static void add_to_kill(struct task_struct *tsk, struct page *p, * Also when FAIL is set do a force kill because something went * wrong earlier. */ -static void kill_procs(struct list_head *to_kill, int doit, int trapno, +static void kill_procs(struct list_head *to_kill, int forcekill, int trapno, int fail, struct page *page, unsigned long pfn, int flags) { struct to_kill *tk, *next; list_for_each_entry_safe (tk, next, to_kill, nd) { - if (doit) { + if (forcekill) { /* * In case something went wrong with munmapping * make sure the process doesn't catch the @@ -858,7 +858,7 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn, struct address_space *mapping; LIST_HEAD(tokill); int ret; - int kill = 1; + int kill = 1, forcekill; struct page *hpage = compound_head(p); struct page *ppage; @@ -888,7 +888,7 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn, * be called inside page lock (it's recommended but not enforced). */ mapping = page_mapping(hpage); - if (!PageDirty(hpage) && mapping && + if (!(flags & MF_MUST_KILL) && !PageDirty(hpage) && mapping && mapping_cap_writeback_dirty(mapping)) { if (page_mkclean(hpage)) { SetPageDirty(hpage); @@ -965,12 +965,14 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn, * Now that the dirty bit has been propagated to the * struct page and all unmaps done we can decide if * killing is needed or not. Only kill when the page - * was dirty, otherwise the tokill list is merely + * was dirty or the process is not restartable, + * otherwise the tokill list is merely * freed. When there was a problem unmapping earlier * use a more force-full uncatchable kill to prevent * any accesses to the poisoned memory. */ - kill_procs(&tokill, !!PageDirty(ppage), trapno, + forcekill = PageDirty(ppage) || (flags & MF_MUST_KILL); + kill_procs(&tokill, forcekill, trapno, ret != SWAP_SUCCESS, p, pfn, flags); return ret; -- cgit v1.2.3-59-g8ed1b From 396f2feb05d7cc5549c611c05abfb4108cd1c6d6 Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Tue, 19 Jun 2012 11:21:42 +0300 Subject: mlx4_core: Implement mechanism for reserved Q_Keys The SR-IOV special QP tunneling mechanism uses proxy special QPs (instead of the real special QPs) for MADs on guests. These proxy QPs send their packets to a "tunnel" QP owned by the master. The master then forwards the MAD (after any required paravirtualization) to the real special QP, which sends out the MAD. For security reasons (i.e., to prevent guests from sending MADs to tunnel QPs belonging to other guests), each proxy-tunnel QP pair is assigned a unique, reserved, Q_Key. These Q_Keys are available only for proxy and tunnel QPs -- if the guest tries to use these Q_Keys with other QPs, it will fail. This patch introduces a mechanism for reserving a block of 64K Q_Keys for proxy/tunneling use. The patch introduces also two new fields into mlx4_dev: base_sqpn and base_tunnel_sqpn. In SR-IOV mode, the QP numbers for the "real," proxy, and tunnel sqps are added to the reserved QPN area (so that they will not change). There are 8 special QPs per port in the HCA, and each of them is assigned both a proxy and a tunnel QP, for each VF and for the PF as well in SR-IOV mode. The QPNs for these QPs are arranged as follows: 1. The real SQP numbers (8) 2. The proxy SQPs (8 * (max number of VFs + max number of PFs) 3. The tunnel SQPs (8 * (max number of VFs + max number of PFs) To support these QPs, two new fields are added to struct mlx4_dev: base_sqp: this is the QP number of the first of the real SQPs base_tunnel_sqp: this is the qp number of the first qp in the tunnel sqp region. (On guests, this is the first tunnel sqp of the 8 which are assigned to that guest). In addition, in SR-IOV mode, sqp_start is the number of the first proxy SQP in the proxy SQP region. (In guests, this is the first proxy SQP of the 8 which are assigned to that guest) Note that in non-SR-IOV mode, there are no proxies and no tunnels. In this case, sqp_start is set to sqp_base -- which minimizes code changes. Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/net/ethernet/mellanox/mlx4/main.c | 17 +++++++++++++++++ include/linux/mlx4/device.h | 11 +++++++++++ 2 files changed, 28 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 83afb1541a74..81154a16d6b8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -391,6 +391,23 @@ static int mlx4_how_many_lives_vf(struct mlx4_dev *dev) return ret; } +int mlx4_get_parav_qkey(struct mlx4_dev *dev, u32 qpn, u32 *qkey) +{ + u32 qk = MLX4_RESERVED_QKEY_BASE; + if (qpn >= dev->caps.base_tunnel_sqpn + 8 * MLX4_MFUNC_MAX || + qpn < dev->caps.sqp_start) + return -EINVAL; + + if (qpn >= dev->caps.base_tunnel_sqpn) + /* tunnel qp */ + qk += qpn - dev->caps.base_tunnel_sqpn; + else + qk += qpn - dev->caps.sqp_start; + *qkey = qk; + return 0; +} +EXPORT_SYMBOL(mlx4_get_parav_qkey); + int mlx4_is_slave_active(struct mlx4_dev *dev, int slave) { struct mlx4_priv *priv = mlx4_priv(dev); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 7fbdc89de495..c30a314e095c 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -56,6 +56,13 @@ enum { MLX4_MAX_PORTS = 2 }; +/* base qkey for use in sriov tunnel-qp/proxy-qp communication. + * These qkeys must not be allowed for general use. This is a 64k range, + * and to test for violation, we use the mask (protect against future chg). + */ +#define MLX4_RESERVED_QKEY_BASE (0xFFFF0000) +#define MLX4_RESERVED_QKEY_MASK (0xFFFF0000) + enum { MLX4_BOARD_ID_LEN = 64 }; @@ -293,6 +300,8 @@ struct mlx4_caps { int max_qp_init_rdma; int max_qp_dest_rdma; int sqp_start; + u32 base_sqpn; + u32 base_tunnel_sqpn; int num_srqs; int max_srq_wqes; int max_srq_sge; @@ -772,4 +781,6 @@ int mlx4_wol_write(struct mlx4_dev *dev, u64 config, int port); int mlx4_counter_alloc(struct mlx4_dev *dev, u32 *idx); void mlx4_counter_free(struct mlx4_dev *dev, u32 idx); +int mlx4_get_parav_qkey(struct mlx4_dev *dev, u32 qpn, u32 *qkey); + #endif /* MLX4_DEVICE_H */ -- cgit v1.2.3-59-g8ed1b From 6634961c14d38ef64ec284c07aecb03d3dd03b4a Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Tue, 19 Jun 2012 11:21:44 +0300 Subject: mlx4: Put physical GID and P_Key table sizes in mlx4_phys_caps struct and paravirtualize them To allow easy paravirtualization of P_Key and GID table sizes, keep paravirtualized sizes in mlx4_dev->caps, but save the actual physical sizes from FW in struct: mlx4_dev->phys_cap. In addition, in SR-IOV mode, do the following: 1. Reduce reported P_Key table size by 1. This is done to reserve the highest P_Key index for internal use, for declaring an invalid P_Key in P_Key paravirtualization. We require a P_Key index which always contain an invalid P_Key value for this purpose (i.e., one which cannot be modified by the subnet manager). The way to do this is to reduce the P_Key table size reported to the subnet manager by 1, so that it will not attempt to access the P_Key at index #127. 2. Paravirtualize the GID table size to 1. Thus, each guest sees only a single GID (at its paravirtualized index 0). In addition, since we are paravirtualizing the GID table size to 1, we add paravirtualization of the master GID event here (i.e., we do not do ib_dispatch_event() for the GUID change event on the master, since its (only) GUID never changes). Signed-off-by: Jack Morgenstein Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/mad.c | 10 ++++--- drivers/net/ethernet/mellanox/mlx4/fw.c | 43 +++++++++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/main.c | 32 ++++++++++++++++++++--- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 4 ++- drivers/net/ethernet/mellanox/mlx4/port.c | 11 ++++++-- include/linux/mlx4/device.h | 2 ++ 6 files changed, 92 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c index 58c45fb5bd31..c27141fef1ab 100644 --- a/drivers/infiniband/hw/mlx4/mad.c +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -184,8 +184,10 @@ static void smp_snoop(struct ib_device *ibdev, u8 port_num, struct ib_mad *mad, break; case IB_SMP_ATTR_GUID_INFO: - mlx4_ib_dispatch_event(dev, port_num, - IB_EVENT_GID_CHANGE); + /* paravirtualized master's guid is guid 0 -- does not change */ + if (!mlx4_is_master(dev->dev)) + mlx4_ib_dispatch_event(dev, port_num, + IB_EVENT_GID_CHANGE); break; default: break; @@ -487,7 +489,9 @@ void handle_port_mgmt_change_event(struct work_struct *work) mlx4_ib_dispatch_event(dev, port, IB_EVENT_PKEY_CHANGE); break; case MLX4_DEV_PMC_SUBTYPE_GUID_INFO: - mlx4_ib_dispatch_event(dev, port, IB_EVENT_GID_CHANGE); + /* paravirtualized master's guid is guid 0 -- does not change */ + if (!mlx4_is_master(dev->dev)) + mlx4_ib_dispatch_event(dev, port, IB_EVENT_GID_CHANGE); break; default: pr_warn("Unsupported subtype 0x%x for " diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 5549f6b3bb67..473d63b63b4e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -730,9 +730,12 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave, { u64 def_mac; u8 port_type; + u16 short_field; int err; #define MLX4_VF_PORT_NO_LINK_SENSE_MASK 0xE0 +#define QUERY_PORT_CUR_MAX_PKEY_OFFSET 0x0c +#define QUERY_PORT_CUR_MAX_GID_OFFSET 0x0e err = mlx4_cmd_box(dev, 0, outbox->dma, vhcr->in_modifier, 0, MLX4_CMD_QUERY_PORT, MLX4_CMD_TIME_CLASS_B, @@ -755,11 +758,51 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave, MLX4_PUT(outbox->buf, port_type, QUERY_PORT_SUPPORTED_TYPE_OFFSET); + + short_field = 1; /* slave max gids */ + MLX4_PUT(outbox->buf, short_field, + QUERY_PORT_CUR_MAX_GID_OFFSET); + + short_field = dev->caps.pkey_table_len[vhcr->in_modifier]; + MLX4_PUT(outbox->buf, short_field, + QUERY_PORT_CUR_MAX_PKEY_OFFSET); } return err; } +int mlx4_get_slave_pkey_gid_tbl_len(struct mlx4_dev *dev, u8 port, + int *gid_tbl_len, int *pkey_tbl_len) +{ + struct mlx4_cmd_mailbox *mailbox; + u32 *outbox; + u16 field; + int err; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + err = mlx4_cmd_box(dev, 0, mailbox->dma, port, 0, + MLX4_CMD_QUERY_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_WRAPPED); + if (err) + goto out; + + outbox = mailbox->buf; + + MLX4_GET(field, outbox, QUERY_PORT_CUR_MAX_GID_OFFSET); + *gid_tbl_len = field; + + MLX4_GET(field, outbox, QUERY_PORT_CUR_MAX_PKEY_OFFSET); + *pkey_tbl_len = field; + +out: + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_get_slave_pkey_gid_tbl_len); + int mlx4_map_cmd(struct mlx4_dev *dev, u16 op, struct mlx4_icm *icm, u64 virt) { struct mlx4_cmd_mailbox *mailbox; diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 58544b72bacb..5df3ac40a490 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -215,6 +215,10 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) for (i = 1; i <= dev->caps.num_ports; ++i) { dev->caps.vl_cap[i] = dev_cap->max_vl[i]; dev->caps.ib_mtu_cap[i] = dev_cap->ib_mtu[i]; + dev->phys_caps.gid_phys_table_len[i] = dev_cap->max_gids[i]; + dev->phys_caps.pkey_phys_table_len[i] = dev_cap->max_pkeys[i]; + /* set gid and pkey table operating lengths by default + * to non-sriov values */ dev->caps.gid_table_len[i] = dev_cap->max_gids[i]; dev->caps.pkey_table_len[i] = dev_cap->max_pkeys[i]; dev->caps.port_width_cap[i] = dev_cap->max_port_width[i]; @@ -498,8 +502,13 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) return -ENODEV; } - for (i = 1; i <= dev->caps.num_ports; ++i) + for (i = 1; i <= dev->caps.num_ports; ++i) { dev->caps.port_mask[i] = dev->caps.port_type[i]; + if (mlx4_get_slave_pkey_gid_tbl_len(dev, i, + &dev->caps.gid_table_len[i], + &dev->caps.pkey_table_len[i])) + return -ENODEV; + } if (dev->caps.uar_page_size * (dev->caps.num_uars - dev->caps.reserved_uars) > @@ -536,7 +545,7 @@ int mlx4_change_port_types(struct mlx4_dev *dev, for (port = 1; port <= dev->caps.num_ports; port++) { mlx4_CLOSE_PORT(dev, port); dev->caps.port_type[port] = port_types[port - 1]; - err = mlx4_SET_PORT(dev, port); + err = mlx4_SET_PORT(dev, port, -1); if (err) { mlx4_err(dev, "Failed to set port %d, " "aborting\n", port); @@ -722,7 +731,7 @@ static ssize_t set_port_ib_mtu(struct device *dev, mlx4_unregister_device(mdev); for (port = 1; port <= mdev->caps.num_ports; port++) { mlx4_CLOSE_PORT(mdev, port); - err = mlx4_SET_PORT(mdev, port); + err = mlx4_SET_PORT(mdev, port, -1); if (err) { mlx4_err(mdev, "Failed to set port %d, " "aborting\n", port); @@ -1173,6 +1182,17 @@ err: return -EIO; } +static void mlx4_parav_master_pf_caps(struct mlx4_dev *dev) +{ + int i; + + for (i = 1; i <= dev->caps.num_ports; i++) { + dev->caps.gid_table_len[i] = 1; + dev->caps.pkey_table_len[i] = + dev->phys_caps.pkey_phys_table_len[i] - 1; + } +} + static int mlx4_init_hca(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); @@ -1212,6 +1232,9 @@ static int mlx4_init_hca(struct mlx4_dev *dev) goto err_stop_fw; } + if (mlx4_is_master(dev)) + mlx4_parav_master_pf_caps(dev); + profile = default_profile; icm_size = mlx4_make_profile(dev, &profile, &dev_cap, @@ -1500,7 +1523,8 @@ static int mlx4_setup_hca(struct mlx4_dev *dev) else dev->caps.port_ib_mtu[port] = IB_MTU_4096; - err = mlx4_SET_PORT(dev, port); + err = mlx4_SET_PORT(dev, port, mlx4_is_master(dev) ? + dev->caps.pkey_table_len[port] : -1); if (err) { mlx4_err(dev, "Failed to set port %d, aborting\n", port); diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 4d11d12b9db4..cde6e511899f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -969,7 +969,7 @@ int mlx4_change_port_types(struct mlx4_dev *dev, void mlx4_init_mac_table(struct mlx4_dev *dev, struct mlx4_mac_table *table); void mlx4_init_vlan_table(struct mlx4_dev *dev, struct mlx4_vlan_table *table); -int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port); +int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port, int pkey_tbl_sz); /* resource tracker functions*/ int mlx4_get_slave_from_resource_id(struct mlx4_dev *dev, enum mlx4_resource resource_type, @@ -1012,6 +1012,8 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_cmd_info *cmd); int mlx4_get_port_ib_caps(struct mlx4_dev *dev, u8 port, __be32 *caps); +int mlx4_get_slave_pkey_gid_tbl_len(struct mlx4_dev *dev, u8 port, + int *gid_tbl_len, int *pkey_tbl_len); int mlx4_QP_ATTACH_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index a8fb52992c64..90dc47542b8b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -726,14 +726,15 @@ int mlx4_SET_PORT_wrapper(struct mlx4_dev *dev, int slave, enum { MLX4_SET_PORT_VL_CAP = 4, /* bits 7:4 */ MLX4_SET_PORT_MTU_CAP = 12, /* bits 15:12 */ + MLX4_CHANGE_PORT_PKEY_TBL_SZ = 20, MLX4_CHANGE_PORT_VL_CAP = 21, MLX4_CHANGE_PORT_MTU_CAP = 22, }; -int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port) +int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port, int pkey_tbl_sz) { struct mlx4_cmd_mailbox *mailbox; - int err, vl_cap; + int err, vl_cap, pkey_tbl_flag = 0; if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) return 0; @@ -746,11 +747,17 @@ int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port) ((__be32 *) mailbox->buf)[1] = dev->caps.ib_port_def_cap[port]; + if (pkey_tbl_sz >= 0 && mlx4_is_master(dev)) { + pkey_tbl_flag = 1; + ((__be16 *) mailbox->buf)[20] = cpu_to_be16(pkey_tbl_sz); + } + /* IB VL CAP enum isn't used by the firmware, just numerical values */ for (vl_cap = 8; vl_cap >= 1; vl_cap >>= 1) { ((__be32 *) mailbox->buf)[0] = cpu_to_be32( (1 << MLX4_CHANGE_PORT_MTU_CAP) | (1 << MLX4_CHANGE_PORT_VL_CAP) | + (pkey_tbl_flag << MLX4_CHANGE_PORT_PKEY_TBL_SZ) | (dev->caps.port_ib_mtu[port] << MLX4_SET_PORT_MTU_CAP) | (vl_cap << MLX4_SET_PORT_VL_CAP)); err = mlx4_cmd(dev, mailbox->dma, port, 0, MLX4_CMD_SET_PORT, diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index c30a314e095c..441caf1a497d 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -268,6 +268,8 @@ static inline u64 mlx4_fw_ver(u64 major, u64 minor, u64 subminor) } struct mlx4_phys_caps { + u32 gid_phys_table_len[MLX4_MAX_PORTS + 1]; + u32 pkey_phys_table_len[MLX4_MAX_PORTS + 1]; u32 num_phys_eqs; }; -- cgit v1.2.3-59-g8ed1b From 650cef38263c0f4c8970265354432be154eef425 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Mon, 9 Jul 2012 22:03:10 +0200 Subject: bcma: add PMU clock support for BCM4706 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hauke Mehrtens Tested-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/driver_chipcommon_pmu.c | 40 ++++++++++++++++++++++++++--- include/linux/bcma/bcma_driver_chipcommon.h | 14 ++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c index 74a87d530424..44326178db29 100644 --- a/drivers/bcma/driver_chipcommon_pmu.c +++ b/drivers/bcma/driver_chipcommon_pmu.c @@ -226,6 +226,36 @@ static u32 bcma_pmu_clock(struct bcma_drv_cc *cc, u32 pll0, u32 m) return (fc / div) * 1000000; } +static u32 bcma_pmu_clock_bcm4706(struct bcma_drv_cc *cc, u32 pll0, u32 m) +{ + u32 tmp, ndiv, p1div, p2div; + u32 clock; + + BUG_ON(!m || m > 4); + + /* Get N, P1 and P2 dividers to determine CPU clock */ + tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PMU6_4706_PROCPLL_OFF); + ndiv = (tmp & BCMA_CC_PMU6_4706_PROC_NDIV_INT_MASK) + >> BCMA_CC_PMU6_4706_PROC_NDIV_INT_SHIFT; + p1div = (tmp & BCMA_CC_PMU6_4706_PROC_P1DIV_MASK) + >> BCMA_CC_PMU6_4706_PROC_P1DIV_SHIFT; + p2div = (tmp & BCMA_CC_PMU6_4706_PROC_P2DIV_MASK) + >> BCMA_CC_PMU6_4706_PROC_P2DIV_SHIFT; + + tmp = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT); + if (tmp & BCMA_CC_CHIPST_4706_PKG_OPTION) + /* Low cost bonding: Fixed reference clock 25MHz and m = 4 */ + clock = (25000000 / 4) * ndiv * p2div / p1div; + else + /* Fixed reference clock 25MHz and m = 2 */ + clock = (25000000 / 2) * ndiv * p2div / p1div; + + if (m == BCMA_CC_PMU5_MAINPLL_SSB) + clock = clock / 4; + + return clock; +} + /* query bus clock frequency for PMU-enabled chipcommon */ u32 bcma_pmu_get_clockcontrol(struct bcma_drv_cc *cc) { @@ -245,8 +275,8 @@ u32 bcma_pmu_get_clockcontrol(struct bcma_drv_cc *cc) return bcma_pmu_clock(cc, BCMA_CC_PMU5357_MAINPLL_PLL0, BCMA_CC_PMU5_MAINPLL_SSB); case BCMA_CHIP_ID_BCM4706: - return bcma_pmu_clock(cc, BCMA_CC_PMU4706_MAINPLL_PLL0, - BCMA_CC_PMU5_MAINPLL_SSB); + return bcma_pmu_clock_bcm4706(cc, BCMA_CC_PMU4706_MAINPLL_PLL0, + BCMA_CC_PMU5_MAINPLL_SSB); case BCMA_CHIP_ID_BCM53572: return 75000000; default: @@ -267,6 +297,10 @@ u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc) if (cc->pmu.rev >= 5) { u32 pll; switch (bus->chipinfo.id) { + case BCMA_CHIP_ID_BCM4706: + return bcma_pmu_clock_bcm4706(cc, + BCMA_CC_PMU4706_MAINPLL_PLL0, + BCMA_CC_PMU5_MAINPLL_CPU); case BCMA_CHIP_ID_BCM5356: pll = BCMA_CC_PMU5356_MAINPLL_PLL0; break; @@ -279,8 +313,6 @@ u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc) break; } - /* TODO: if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) - return si_4706_pmu_clock(sih, osh, cc, PMU4706_MAINPLL_PLL0, PMU5_MAINPLL_CPU); */ return bcma_pmu_clock(cc, pll, BCMA_CC_PMU5_MAINPLL_CPU); } diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index 12975eac403f..fbd0d49dc4d2 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -88,6 +88,11 @@ #define BCMA_CC_CHIPST_4313_OTP_PRESENT 2 #define BCMA_CC_CHIPST_4331_SPROM_PRESENT 2 #define BCMA_CC_CHIPST_4331_OTP_PRESENT 4 +#define BCMA_CC_CHIPST_4706_PKG_OPTION BIT(0) /* 0: full-featured package 1: low-cost package */ +#define BCMA_CC_CHIPST_4706_SFLASH_PRESENT BIT(1) /* 0: parallel, 1: serial flash is present */ +#define BCMA_CC_CHIPST_4706_SFLASH_TYPE BIT(2) /* 0: 8b-p/ST-s flash, 1: 16b-p/Atmal-s flash */ +#define BCMA_CC_CHIPST_4706_MIPS_BENDIAN BIT(3) /* 0: little, 1: big endian */ +#define BCMA_CC_CHIPST_4706_PCIE1_DISABLE BIT(5) /* PCIE1 enable strap pin */ #define BCMA_CC_JCMD 0x0030 /* Rev >= 10 only */ #define BCMA_CC_JCMD_START 0x80000000 #define BCMA_CC_JCMD_BUSY 0x80000000 @@ -280,6 +285,15 @@ /* 4706 PMU */ #define BCMA_CC_PMU4706_MAINPLL_PLL0 0 +#define BCMA_CC_PMU6_4706_PROCPLL_OFF 4 /* The CPU PLL */ +#define BCMA_CC_PMU6_4706_PROC_P2DIV_MASK 0x000f0000 +#define BCMA_CC_PMU6_4706_PROC_P2DIV_SHIFT 16 +#define BCMA_CC_PMU6_4706_PROC_P1DIV_MASK 0x0000f000 +#define BCMA_CC_PMU6_4706_PROC_P1DIV_SHIFT 12 +#define BCMA_CC_PMU6_4706_PROC_NDIV_INT_MASK 0x00000ff8 +#define BCMA_CC_PMU6_4706_PROC_NDIV_INT_SHIFT 3 +#define BCMA_CC_PMU6_4706_PROC_NDIV_MODE_MASK 0x00000007 +#define BCMA_CC_PMU6_4706_PROC_NDIV_MODE_SHIFT 0 /* ALP clock on pre-PMU chips */ #define BCMA_CC_PMU_ALP_CLOCK 20000000 -- cgit v1.2.3-59-g8ed1b From f55a6faa384304c89cfef162768e88374d3312cb Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 10 Jul 2012 18:43:19 -0400 Subject: hrtimer: Provide clock_was_set_delayed() clock_was_set() cannot be called from hard interrupt context because it calls on_each_cpu(). For fixing the widely reported leap seconds issue it is necessary to call it from hard interrupt context, i.e. the timer tick code, which does the timekeeping updates. Provide a new function which denotes it in the hrtimer cpu base structure of the cpu on which it is called and raise the hrtimer softirq. We then execute the clock_was_set() notificiation from softirq context in run_hrtimer_softirq(). The hrtimer softirq is rarely used, so polling the flag there is not a performance issue. [ tglx: Made it depend on CONFIG_HIGH_RES_TIMERS. We really should get rid of all this ifdeffery ASAP ] Signed-off-by: John Stultz Reported-by: Jan Engelhardt Reviewed-by: Ingo Molnar Acked-by: Peter Zijlstra Acked-by: Prarit Bhargava Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/1341960205-56738-2-git-send-email-johnstul@us.ibm.com Signed-off-by: Thomas Gleixner --- include/linux/hrtimer.h | 9 ++++++++- kernel/hrtimer.c | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index fd0dc30c9f15..c9ec9400ee5b 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -165,6 +165,7 @@ enum hrtimer_base_type { * @lock: lock protecting the base and associated clock bases * and timers * @active_bases: Bitfield to mark bases with active timers + * @clock_was_set: Indicates that clock was set from irq context. * @expires_next: absolute time of the next event which was scheduled * via clock_set_next_event() * @hres_active: State of high resolution mode @@ -177,7 +178,8 @@ enum hrtimer_base_type { */ struct hrtimer_cpu_base { raw_spinlock_t lock; - unsigned long active_bases; + unsigned int active_bases; + unsigned int clock_was_set; #ifdef CONFIG_HIGH_RES_TIMERS ktime_t expires_next; int hres_active; @@ -286,6 +288,8 @@ extern void hrtimer_peek_ahead_timers(void); # define MONOTONIC_RES_NSEC HIGH_RES_NSEC # define KTIME_MONOTONIC_RES KTIME_HIGH_RES +extern void clock_was_set_delayed(void); + #else # define MONOTONIC_RES_NSEC LOW_RES_NSEC @@ -306,6 +310,9 @@ static inline int hrtimer_is_hres_active(struct hrtimer *timer) { return 0; } + +static inline void clock_was_set_delayed(void) { } + #endif extern void clock_was_set(void); diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index ae34bf51682b..3c24fb2c25c8 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -717,6 +717,19 @@ static int hrtimer_switch_to_hres(void) return 1; } +/* + * Called from timekeeping code to reprogramm the hrtimer interrupt + * device. If called from the timer interrupt context we defer it to + * softirq context. + */ +void clock_was_set_delayed(void) +{ + struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases); + + cpu_base->clock_was_set = 1; + __raise_softirq_irqoff(HRTIMER_SOFTIRQ); +} + #else static inline int hrtimer_hres_active(void) { return 0; } @@ -1395,6 +1408,13 @@ void hrtimer_peek_ahead_timers(void) static void run_hrtimer_softirq(struct softirq_action *h) { + struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases); + + if (cpu_base->clock_was_set) { + cpu_base->clock_was_set = 0; + clock_was_set(); + } + hrtimer_peek_ahead_timers(); } -- cgit v1.2.3-59-g8ed1b From f6c06abfb3972ad4914cef57d8348fcb2932bc3b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 10 Jul 2012 18:43:24 -0400 Subject: timekeeping: Provide hrtimer update function To finally fix the infamous leap second issue and other race windows caused by functions which change the offsets between the various time bases (CLOCK_MONOTONIC, CLOCK_REALTIME and CLOCK_BOOTTIME) we need a function which atomically gets the current monotonic time and updates the offsets of CLOCK_REALTIME and CLOCK_BOOTTIME with minimalistic overhead. The previous patch which provides ktime_t offsets allows us to make this function almost as cheap as ktime_get() which is going to be replaced in hrtimer_interrupt(). Signed-off-by: Thomas Gleixner Reviewed-by: Ingo Molnar Acked-by: Peter Zijlstra Acked-by: Prarit Bhargava Cc: stable@vger.kernel.org Signed-off-by: John Stultz Link: http://lkml.kernel.org/r/1341960205-56738-7-git-send-email-johnstul@us.ibm.com Signed-off-by: Thomas Gleixner --- include/linux/hrtimer.h | 1 + kernel/time/timekeeping.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'include/linux') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index c9ec9400ee5b..cc07d2777bbe 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -327,6 +327,7 @@ extern ktime_t ktime_get(void); extern ktime_t ktime_get_real(void); extern ktime_t ktime_get_boottime(void); extern ktime_t ktime_get_monotonic_offset(void); +extern ktime_t ktime_get_update_offsets(ktime_t *offs_real, ktime_t *offs_boot); DECLARE_PER_CPU(struct tick_device, tick_cpu_device); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 1c038dac71a2..269b1fe5f2ae 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1271,6 +1271,40 @@ void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, } while (read_seqretry(&timekeeper.lock, seq)); } +#ifdef CONFIG_HIGH_RES_TIMERS +/** + * ktime_get_update_offsets - hrtimer helper + * @offs_real: pointer to storage for monotonic -> realtime offset + * @offs_boot: pointer to storage for monotonic -> boottime offset + * + * Returns current monotonic time and updates the offsets + * Called from hrtimer_interupt() or retrigger_next_event() + */ +ktime_t ktime_get_update_offsets(ktime_t *offs_real, ktime_t *offs_boot) +{ + ktime_t now; + unsigned int seq; + u64 secs, nsecs; + + do { + seq = read_seqbegin(&timekeeper.lock); + + secs = timekeeper.xtime.tv_sec; + nsecs = timekeeper.xtime.tv_nsec; + nsecs += timekeeping_get_ns(); + /* If arch requires, add in gettimeoffset() */ + nsecs += arch_gettimeoffset(); + + *offs_real = timekeeper.offs_real; + *offs_boot = timekeeper.offs_boot; + } while (read_seqretry(&timekeeper.lock, seq)); + + now = ktime_add_ns(ktime_set(secs, 0), nsecs); + now = ktime_sub(now, *offs_real); + return now; +} +#endif + /** * ktime_get_monotonic_offset() - get wall_to_monotonic in ktime_t format */ -- cgit v1.2.3-59-g8ed1b From 357c3f0a6c7613f7230fcaf1eb16190ed2a4b0af Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Fri, 29 Jun 2012 19:06:32 +0530 Subject: clk: Add support for rate table based dividers Some divider clks do not have any obvious relationship between the divider and the value programmed in the register. For instance, say a value of 1 could signify divide by 6 and a value of 2 could signify divide by 4 etc. Also there are dividers where not all values possible based on the bitfield width are valid. For instance a 3 bit wide bitfield can be used to program a value from 0 to 7. However its possible that only 0 to 4 are valid values. All these cases need the platform code to pass a simple table of divider/value tuple, so the framework knows the exact value to be written based on the divider calculation and can also do better error checking. This patch adds support for such rate table based dividers and as part of the support adds a new registration function 'clk_register_divider_table()' and a new macro for static definition 'DEFINE_CLK_DIVIDER_TABLE'. Signed-off-by: Rajendra Nayak Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 125 +++++++++++++++++++++++++++++++++++++------ include/linux/clk-private.h | 20 ++++++- include/linux/clk-provider.h | 12 +++++ 3 files changed, 139 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index e548c4328f3c..02a4da98176b 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -32,30 +32,69 @@ #define div_mask(d) ((1 << (d->width)) - 1) #define is_power_of_two(i) !(i & ~i) +static unsigned int _get_table_maxdiv(const struct clk_div_table *table) +{ + unsigned int maxdiv = 0; + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div > maxdiv) + maxdiv = clkt->div; + return maxdiv; +} + static unsigned int _get_maxdiv(struct clk_divider *divider) { if (divider->flags & CLK_DIVIDER_ONE_BASED) return div_mask(divider); if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) return 1 << div_mask(divider); + if (divider->table) + return _get_table_maxdiv(divider->table); return div_mask(divider) + 1; } +static unsigned int _get_table_div(const struct clk_div_table *table, + unsigned int val) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->val == val) + return clkt->div; + return 0; +} + static unsigned int _get_div(struct clk_divider *divider, unsigned int val) { if (divider->flags & CLK_DIVIDER_ONE_BASED) return val; if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) return 1 << val; + if (divider->table) + return _get_table_div(divider->table, val); return val + 1; } +static unsigned int _get_table_val(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return clkt->val; + return 0; +} + static unsigned int _get_val(struct clk_divider *divider, u8 div) { if (divider->flags & CLK_DIVIDER_ONE_BASED) return div; if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) return __ffs(div); + if (divider->table) + return _get_table_val(divider->table, div); return div - 1; } @@ -84,6 +123,26 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, */ #define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1) +static bool _is_valid_table_div(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return true; + return false; +} + +static bool _is_valid_div(struct clk_divider *divider, unsigned int div) +{ + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) + return is_power_of_two(div); + if (divider->table) + return _is_valid_table_div(divider->table, div); + return true; +} + static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate) { @@ -111,8 +170,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, maxdiv = min(ULONG_MAX / rate, maxdiv); for (i = 1; i <= maxdiv; i++) { - if ((divider->flags & CLK_DIVIDER_POWER_OF_TWO) - && (!is_power_of_two(i))) + if (!_is_valid_div(divider, i)) continue; parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), MULT_ROUND_UP(rate, i)); @@ -176,22 +234,11 @@ const struct clk_ops clk_divider_ops = { }; EXPORT_SYMBOL_GPL(clk_divider_ops); -/** - * clk_register_divider - register a divider clock with the clock framework - * @dev: device registering this clock - * @name: name of this clock - * @parent_name: name of clock's parent - * @flags: framework-specific flags - * @reg: register address to adjust divider - * @shift: number of bits to shift the bitfield - * @width: width of the bitfield - * @clk_divider_flags: divider-specific flags for this clock - * @lock: shared register lock for this clock - */ -struct clk *clk_register_divider(struct device *dev, const char *name, +static struct clk *_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, - u8 clk_divider_flags, spinlock_t *lock) + u8 clk_divider_flags, const struct clk_div_table *table, + spinlock_t *lock) { struct clk_divider *div; struct clk *clk; @@ -217,6 +264,7 @@ struct clk *clk_register_divider(struct device *dev, const char *name, div->flags = clk_divider_flags; div->lock = lock; div->hw.init = &init; + div->table = table; /* register the clock */ clk = clk_register(dev, &div->hw); @@ -226,3 +274,48 @@ struct clk *clk_register_divider(struct device *dev, const char *name, return clk; } + +/** + * clk_register_divider - register a divider clock with the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust divider + * @shift: number of bits to shift the bitfield + * @width: width of the bitfield + * @clk_divider_flags: divider-specific flags for this clock + * @lock: shared register lock for this clock + */ +struct clk *clk_register_divider(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, spinlock_t *lock) +{ + return _register_divider(dev, name, parent_name, flags, reg, shift, + width, clk_divider_flags, NULL, lock); +} + +/** + * clk_register_divider_table - register a table based divider clock with + * the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust divider + * @shift: number of bits to shift the bitfield + * @width: width of the bitfield + * @clk_divider_flags: divider-specific flags for this clock + * @table: array of divider/value pairs ending with a div set to 0 + * @lock: shared register lock for this clock + */ +struct clk *clk_register_divider_table(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, const struct clk_div_table *table, + spinlock_t *lock) +{ + return _register_divider(dev, name, parent_name, flags, reg, shift, + width, clk_divider_flags, table, lock); +} diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h index eb3f84bc5325..cc9972d1429c 100644 --- a/include/linux/clk-private.h +++ b/include/linux/clk-private.h @@ -103,9 +103,9 @@ struct clk { DEFINE_CLK(_name, clk_gate_ops, _flags, \ _name##_parent_names, _name##_parents); -#define DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \ +#define _DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \ _flags, _reg, _shift, _width, \ - _divider_flags, _lock) \ + _divider_flags, _table, _lock) \ static struct clk _name; \ static const char *_name##_parent_names[] = { \ _parent_name, \ @@ -121,11 +121,27 @@ struct clk { .shift = _shift, \ .width = _width, \ .flags = _divider_flags, \ + .table = _table, \ .lock = _lock, \ }; \ DEFINE_CLK(_name, clk_divider_ops, _flags, \ _name##_parent_names, _name##_parents); +#define DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \ + _flags, _reg, _shift, _width, \ + _divider_flags, _lock) \ + _DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \ + _flags, _reg, _shift, _width, \ + _divider_flags, NULL, _lock) + +#define DEFINE_CLK_DIVIDER_TABLE(_name, _parent_name, \ + _parent_ptr, _flags, _reg, \ + _shift, _width, _divider_flags, \ + _table, _lock) \ + _DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \ + _flags, _reg, _shift, _width, \ + _divider_flags, _table, _lock) \ + #define DEFINE_CLK_MUX(_name, _parent_names, _parents, _flags, \ _reg, _shift, _width, \ _mux_flags, _lock) \ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 4a0b483986c3..79caee9f1489 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -203,6 +203,11 @@ struct clk *clk_register_gate(struct device *dev, const char *name, void __iomem *reg, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock); +struct clk_div_table { + unsigned int val; + unsigned int div; +}; + /** * struct clk_divider - adjustable divider clock * @@ -210,6 +215,7 @@ struct clk *clk_register_gate(struct device *dev, const char *name, * @reg: register containing the divider * @shift: shift to the divider bit field * @width: width of the divider bit field + * @table: array of value/divider pairs, last entry should have div = 0 * @lock: register lock * * Clock with an adjustable divider affecting its output frequency. Implements @@ -229,6 +235,7 @@ struct clk_divider { u8 shift; u8 width; u8 flags; + const struct clk_div_table *table; spinlock_t *lock; }; @@ -240,6 +247,11 @@ struct clk *clk_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags, spinlock_t *lock); +struct clk *clk_register_divider_table(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, const struct clk_div_table *table, + spinlock_t *lock); /** * struct clk_mux - multiplexer clock -- cgit v1.2.3-59-g8ed1b From f7d8caadfd2813cbada82ce9041b13c38e8e5282 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Fri, 1 Jun 2012 14:02:47 +0530 Subject: clk: Add CLK_IS_BASIC flag to identify basic clocks Most platforms end up using a mix of basic clock types and some which use clk_hw_foo struct for filling in custom platform information when the clocks don't fit into basic types supported. In platform code, its useful to know if a clock is using a basic type or clk_hw_foo, which helps platforms know if they can safely use to_clk_hw_foo to derive the clk_hw_foo pointer from clk_hw. Mark all basic clocks with a CLK_IS_BASIC flag. Signed-off-by: Rajendra Nayak Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 2 +- drivers/clk/clk-fixed-factor.c | 2 +- drivers/clk/clk-fixed-rate.c | 2 +- drivers/clk/clk-gate.c | 2 +- drivers/clk/clk-mux.c | 2 +- include/linux/clk-private.h | 2 +- include/linux/clk-provider.h | 1 + 7 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 02a4da98176b..a9204c69148d 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -253,7 +253,7 @@ static struct clk *_register_divider(struct device *dev, const char *name, init.name = name; init.ops = &clk_divider_ops; - init.flags = flags; + init.flags = flags | CLK_IS_BASIC; init.parent_names = (parent_name ? &parent_name: NULL); init.num_parents = (parent_name ? 1 : 0); diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index c8c003e217ad..a4899855c0f6 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -82,7 +82,7 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name, init.name = name; init.ops = &clk_fixed_factor_ops; - init.flags = flags; + init.flags = flags | CLK_IS_BASIC; init.parent_names = &parent_name; init.num_parents = 1; diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index cbd246229786..7e1464569727 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -63,7 +63,7 @@ struct clk *clk_register_fixed_rate(struct device *dev, const char *name, init.name = name; init.ops = &clk_fixed_rate_ops; - init.flags = flags; + init.flags = flags | CLK_IS_BASIC; init.parent_names = (parent_name ? &parent_name: NULL); init.num_parents = (parent_name ? 1 : 0); diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 578465e04be6..15114febfd92 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -130,7 +130,7 @@ struct clk *clk_register_gate(struct device *dev, const char *name, init.name = name; init.ops = &clk_gate_ops; - init.flags = flags; + init.flags = flags | CLK_IS_BASIC; init.parent_names = (parent_name ? &parent_name: NULL); init.num_parents = (parent_name ? 1 : 0); diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index fd36a8ea73d9..508c032edce4 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -106,7 +106,7 @@ struct clk *clk_register_mux(struct device *dev, const char *name, init.name = name; init.ops = &clk_mux_ops; - init.flags = flags; + init.flags = flags | CLK_IS_BASIC; init.parent_names = parent_names; init.num_parents = num_parents; diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h index cc9972d1429c..9c7f5807824b 100644 --- a/include/linux/clk-private.h +++ b/include/linux/clk-private.h @@ -64,7 +64,7 @@ struct clk { .parent_names = _parent_names, \ .num_parents = ARRAY_SIZE(_parent_names), \ .parents = _parents, \ - .flags = _flags, \ + .flags = _flags | CLK_IS_BASIC, \ } #define DEFINE_CLK_FIXED_RATE(_name, _flags, _rate, \ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 79caee9f1489..0236f58f3e65 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -25,6 +25,7 @@ #define CLK_SET_RATE_PARENT BIT(2) /* propagate rate change up one level */ #define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */ #define CLK_IS_ROOT BIT(4) /* root clk, has no parent */ +#define CLK_IS_BASIC BIT(5) /* Basic clk, can't do a to_clk_foo() */ struct clk_hw; -- cgit v1.2.3-59-g8ed1b From dc4cd941c900fda27f0146ab615122426229de73 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 14 May 2012 15:12:42 +0100 Subject: clk: Constify struct clk_init_data Allow drivers to declare their clk_init_data const, the framework really shouldn't be modifying the data. Signed-off-by: Mark Brown Signed-off-by: Mike Turquette --- include/linux/clk-provider.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 0236f58f3e65..06ad617664a2 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -144,7 +144,7 @@ struct clk_init_data { */ struct clk_hw { struct clk *clk; - struct clk_init_data *init; + const struct clk_init_data *init; }; /* -- cgit v1.2.3-59-g8ed1b From 50667d63085af108f625c83102430c1d74931d78 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 Jun 2012 23:44:25 +0200 Subject: ARM: u300: convert to common clock This converts the U300 clock implementation over to use the common struct clk and moves the implementation down into drivers/clk. Since VCO isn't used in tree it was removed, it's not hard to put it back in if need be. Signed-off-by: Linus Walleij [mturquette@linaro.org: trivial Makefile conflict] Signed-off-by: Mike Turquette --- arch/arm/Kconfig | 2 +- arch/arm/mach-u300/Makefile | 2 +- arch/arm/mach-u300/clock.c | 1504 -------------------------------- arch/arm/mach-u300/clock.h | 50 -- arch/arm/mach-u300/core.c | 21 +- arch/arm/mach-u300/timer.c | 2 +- drivers/clk/Makefile | 3 +- drivers/clk/clk-u300.c | 746 ++++++++++++++++ include/linux/platform_data/clk-u300.h | 1 + 9 files changed, 763 insertions(+), 1568 deletions(-) delete mode 100644 arch/arm/mach-u300/clock.c delete mode 100644 arch/arm/mach-u300/clock.h create mode 100644 drivers/clk/clk-u300.c create mode 100644 include/linux/platform_data/clk-u300.h (limited to 'include/linux') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index a91009c61870..c59853738967 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -888,7 +888,7 @@ config ARCH_U300 select ARM_VIC select GENERIC_CLOCKEVENTS select CLKDEV_LOOKUP - select HAVE_MACH_CLKDEV + select COMMON_CLK select GENERIC_GPIO select ARCH_REQUIRE_GPIOLIB help diff --git a/arch/arm/mach-u300/Makefile b/arch/arm/mach-u300/Makefile index fd3a5c382f47..7e47d37aeb0e 100644 --- a/arch/arm/mach-u300/Makefile +++ b/arch/arm/mach-u300/Makefile @@ -2,7 +2,7 @@ # Makefile for the linux kernel, U300 machine. # -obj-y := core.o clock.o timer.o +obj-y := core.o timer.o obj-m := obj-n := obj- := diff --git a/arch/arm/mach-u300/clock.c b/arch/arm/mach-u300/clock.c deleted file mode 100644 index 5535dd0a78c9..000000000000 --- a/arch/arm/mach-u300/clock.c +++ /dev/null @@ -1,1504 +0,0 @@ -/* - * - * arch/arm/mach-u300/clock.c - * - * - * Copyright (C) 2007-2009 ST-Ericsson AB - * License terms: GNU General Public License (GPL) version 2 - * Define clocks in the app platform. - * Author: Linus Walleij - * Author: Jonas Aaberg - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "clock.h" - -/* - * TODO: - * - move all handling of the CCR register into this file and create - * a spinlock for the CCR register - * - switch to the clkdevice lookup mechanism that maps clocks to - * device ID:s instead when it becomes available in kernel 2.6.29. - * - implement rate get/set for all clocks that need it. - */ - -/* - * Syscon clock I/O registers lock so clock requests don't collide - * NOTE: this is a local lock only used to lock access to clock and - * reset registers in syscon. - */ -static DEFINE_SPINLOCK(syscon_clkreg_lock); -static DEFINE_SPINLOCK(syscon_resetreg_lock); - -/* - * The clocking hierarchy currently looks like this. - * NOTE: the idea is NOT to show how the clocks are routed on the chip! - * The ideas is to show dependencies, so a clock higher up in the - * hierarchy has to be on in order for another clock to be on. Now, - * both CPU and DMA can actually be on top of the hierarchy, and that - * is not modeled currently. Instead we have the backbone AMBA bus on - * top. This bus cannot be programmed in any way but conceptually it - * needs to be active for the bridges and devices to transport data. - * - * Please be aware that a few clocks are hw controlled, which mean that - * the hw itself can turn on/off or change the rate of the clock when - * needed! - * - * AMBA bus - * | - * +- CPU - * +- FSMC NANDIF NAND Flash interface - * +- SEMI Shared Memory interface - * +- ISP Image Signal Processor (U335 only) - * +- CDS (U335 only) - * +- DMA Direct Memory Access Controller - * +- AAIF APP/ACC Inteface (Mobile Scalable Link, MSL) - * +- APEX - * +- VIDEO_ENC AVE2/3 Video Encoder - * +- XGAM Graphics Accelerator Controller - * +- AHB - * | - * +- ahb:0 AHB Bridge - * | | - * | +- ahb:1 INTCON Interrupt controller - * | +- ahb:3 MSPRO Memory Stick Pro controller - * | +- ahb:4 EMIF External Memory interface - * | - * +- fast:0 FAST bridge - * | | - * | +- fast:1 MMCSD MMC/SD card reader controller - * | +- fast:2 I2S0 PCM I2S channel 0 controller - * | +- fast:3 I2S1 PCM I2S channel 1 controller - * | +- fast:4 I2C0 I2C channel 0 controller - * | +- fast:5 I2C1 I2C channel 1 controller - * | +- fast:6 SPI SPI controller - * | +- fast:7 UART1 Secondary UART (U335 only) - * | - * +- slow:0 SLOW bridge - * | - * +- slow:1 SYSCON (not possible to control) - * +- slow:2 WDOG Watchdog - * +- slow:3 UART0 primary UART - * +- slow:4 TIMER_APP Application timer - used in Linux - * +- slow:5 KEYPAD controller - * +- slow:6 GPIO controller - * +- slow:7 RTC controller - * +- slow:8 BT Bus Tracer (not used currently) - * +- slow:9 EH Event Handler (not used currently) - * +- slow:a TIMER_ACC Access style timer (not used currently) - * +- slow:b PPM (U335 only, what is that?) - */ - -/* - * Reset control functions. We remember if a block has been - * taken out of reset and don't remove the reset assertion again - * and vice versa. Currently we only remove resets so the - * enablement function is defined out. - */ -static void syscon_block_reset_enable(struct clk *clk) -{ - u16 val; - unsigned long iflags; - - /* Not all blocks support resetting */ - if (!clk->res_reg || !clk->res_mask) - return; - spin_lock_irqsave(&syscon_resetreg_lock, iflags); - val = readw(clk->res_reg); - val |= clk->res_mask; - writew(val, clk->res_reg); - spin_unlock_irqrestore(&syscon_resetreg_lock, iflags); - clk->reset = true; -} - -static void syscon_block_reset_disable(struct clk *clk) -{ - u16 val; - unsigned long iflags; - - /* Not all blocks support resetting */ - if (!clk->res_reg || !clk->res_mask) - return; - spin_lock_irqsave(&syscon_resetreg_lock, iflags); - val = readw(clk->res_reg); - val &= ~clk->res_mask; - writew(val, clk->res_reg); - spin_unlock_irqrestore(&syscon_resetreg_lock, iflags); - clk->reset = false; -} - -int __clk_get(struct clk *clk) -{ - u16 val; - - /* The MMC and MSPRO clocks need some special set-up */ - if (!strcmp(clk->name, "MCLK")) { - /* Set default MMC clock divisor to 18.9 MHz */ - writew(0x0054U, U300_SYSCON_VBASE + U300_SYSCON_MMF0R); - val = readw(U300_SYSCON_VBASE + U300_SYSCON_MMCR); - /* Disable the MMC feedback clock */ - val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE; - /* Disable MSPRO frequency */ - val &= ~U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_MMCR); - } - if (!strcmp(clk->name, "MSPRO")) { - val = readw(U300_SYSCON_VBASE + U300_SYSCON_MMCR); - /* Disable the MMC feedback clock */ - val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE; - /* Enable MSPRO frequency */ - val |= U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_MMCR); - } - return 1; -} -EXPORT_SYMBOL(__clk_get); - -void __clk_put(struct clk *clk) -{ -} -EXPORT_SYMBOL(__clk_put); - -static void syscon_clk_disable(struct clk *clk) -{ - unsigned long iflags; - - /* Don't touch the hardware controlled clocks */ - if (clk->hw_ctrld) - return; - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - writew(clk->clk_val, U300_SYSCON_VBASE + U300_SYSCON_SBCDR); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} - -static void syscon_clk_enable(struct clk *clk) -{ - unsigned long iflags; - - /* Don't touch the hardware controlled clocks */ - if (clk->hw_ctrld) - return; - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - writew(clk->clk_val, U300_SYSCON_VBASE + U300_SYSCON_SBCER); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} - -static u16 syscon_clk_get_rate(void) -{ - u16 val; - unsigned long iflags; - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); - val &= U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); - return val; -} - -#ifdef CONFIG_MACH_U300_USE_I2S_AS_MASTER -static void enable_i2s0_vcxo(void) -{ - u16 val; - unsigned long iflags; - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - /* Set I2S0 to use the VCXO 26 MHz clock */ - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); - val |= U300_SYSCON_CCR_TURN_VCXO_ON; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - val |= U300_SYSCON_CCR_I2S0_USE_VCXO; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); - val |= U300_SYSCON_CEFR_I2S0_CLK_EN; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} - -static void enable_i2s1_vcxo(void) -{ - u16 val; - unsigned long iflags; - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - /* Set I2S1 to use the VCXO 26 MHz clock */ - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); - val |= U300_SYSCON_CCR_TURN_VCXO_ON; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - val |= U300_SYSCON_CCR_I2S1_USE_VCXO; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); - val |= U300_SYSCON_CEFR_I2S1_CLK_EN; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} - -static void disable_i2s0_vcxo(void) -{ - u16 val; - unsigned long iflags; - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - /* Disable I2S0 use of the VCXO 26 MHz clock */ - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); - val &= ~U300_SYSCON_CCR_I2S0_USE_VCXO; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - /* Deactivate VCXO if no one else is using VCXO */ - if (!(val & U300_SYSCON_CCR_I2S1_USE_VCXO)) - val &= ~U300_SYSCON_CCR_TURN_VCXO_ON; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); - val &= ~U300_SYSCON_CEFR_I2S0_CLK_EN; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} - -static void disable_i2s1_vcxo(void) -{ - u16 val; - unsigned long iflags; - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - /* Disable I2S1 use of the VCXO 26 MHz clock */ - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); - val &= ~U300_SYSCON_CCR_I2S1_USE_VCXO; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - /* Deactivate VCXO if no one else is using VCXO */ - if (!(val & U300_SYSCON_CCR_I2S0_USE_VCXO)) - val &= ~U300_SYSCON_CCR_TURN_VCXO_ON; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CEFR); - val &= ~U300_SYSCON_CEFR_I2S0_CLK_EN; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CEFR); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} -#endif /* CONFIG_MACH_U300_USE_I2S_AS_MASTER */ - - -static void syscon_clk_rate_set_mclk(unsigned long rate) -{ - u16 val; - u32 reg; - unsigned long iflags; - - switch (rate) { - case 18900000: - val = 0x0054; - break; - case 20800000: - val = 0x0044; - break; - case 23100000: - val = 0x0043; - break; - case 26000000: - val = 0x0033; - break; - case 29700000: - val = 0x0032; - break; - case 34700000: - val = 0x0022; - break; - case 41600000: - val = 0x0021; - break; - case 52000000: - val = 0x0011; - break; - case 104000000: - val = 0x0000; - break; - default: - printk(KERN_ERR "Trying to set MCLK to unknown speed! %ld\n", - rate); - return; - } - - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - reg = readw(U300_SYSCON_VBASE + U300_SYSCON_MMF0R) & - ~U300_SYSCON_MMF0R_MASK; - writew(reg | val, U300_SYSCON_VBASE + U300_SYSCON_MMF0R); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} - -void syscon_clk_rate_set_cpuclk(unsigned long rate) -{ - u16 val; - unsigned long iflags; - - switch (rate) { - case 13000000: - val = U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER; - break; - case 52000000: - val = U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE; - break; - case 104000000: - val = U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH; - break; - case 208000000: - val = U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST; - break; - default: - return; - } - spin_lock_irqsave(&syscon_clkreg_lock, iflags); - val |= readw(U300_SYSCON_VBASE + U300_SYSCON_CCR) & - ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK ; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - spin_unlock_irqrestore(&syscon_clkreg_lock, iflags); -} -EXPORT_SYMBOL(syscon_clk_rate_set_cpuclk); - -void clk_disable(struct clk *clk) -{ - unsigned long iflags; - - spin_lock_irqsave(&clk->lock, iflags); - if (clk->usecount > 0 && !(--clk->usecount)) { - /* some blocks lack clocking registers and cannot be disabled */ - if (clk->disable) - clk->disable(clk); - if (likely((u32)clk->parent)) - clk_disable(clk->parent); - } -#ifdef CONFIG_MACH_U300_USE_I2S_AS_MASTER - if (unlikely(!strcmp(clk->name, "I2S0"))) - disable_i2s0_vcxo(); - if (unlikely(!strcmp(clk->name, "I2S1"))) - disable_i2s1_vcxo(); -#endif - spin_unlock_irqrestore(&clk->lock, iflags); -} -EXPORT_SYMBOL(clk_disable); - -int clk_enable(struct clk *clk) -{ - int ret = 0; - unsigned long iflags; - - spin_lock_irqsave(&clk->lock, iflags); - if (clk->usecount++ == 0) { - if (likely((u32)clk->parent)) - ret = clk_enable(clk->parent); - - if (unlikely(ret != 0)) - clk->usecount--; - else { - /* remove reset line (we never enable reset again) */ - syscon_block_reset_disable(clk); - /* clocks without enable function are always on */ - if (clk->enable) - clk->enable(clk); -#ifdef CONFIG_MACH_U300_USE_I2S_AS_MASTER - if (unlikely(!strcmp(clk->name, "I2S0"))) - enable_i2s0_vcxo(); - if (unlikely(!strcmp(clk->name, "I2S1"))) - enable_i2s1_vcxo(); -#endif - } - } - spin_unlock_irqrestore(&clk->lock, iflags); - return ret; - -} -EXPORT_SYMBOL(clk_enable); - -/* Returns the clock rate in Hz */ -static unsigned long clk_get_rate_cpuclk(struct clk *clk) -{ - u16 val; - - val = syscon_clk_get_rate(); - - switch (val) { - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: - return 13000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: - return 52000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: - return 104000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: - return 208000000; - default: - break; - } - return clk->rate; -} - -static unsigned long clk_get_rate_ahb_clk(struct clk *clk) -{ - u16 val; - - val = syscon_clk_get_rate(); - - switch (val) { - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: - return 6500000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: - return 26000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: - return 52000000; - default: - break; - } - return clk->rate; - -} - -static unsigned long clk_get_rate_emif_clk(struct clk *clk) -{ - u16 val; - - val = syscon_clk_get_rate(); - - switch (val) { - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: - return 13000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: - return 52000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: - return 104000000; - default: - break; - } - return clk->rate; - -} - -static unsigned long clk_get_rate_xgamclk(struct clk *clk) -{ - u16 val; - - val = syscon_clk_get_rate(); - - switch (val) { - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: - return 6500000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: - return 26000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: - return 52000000; - default: - break; - } - - return clk->rate; -} - -static unsigned long clk_get_rate_mclk(struct clk *clk) -{ - u16 val; - - val = syscon_clk_get_rate(); - - switch (val) { - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: - /* - * Here, the 208 MHz PLL gets shut down and the always - * on 13 MHz PLL used for RTC etc kicks into use - * instead. - */ - return 13000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: - { - /* - * This clock is under program control. The register is - * divided in two nybbles, bit 7-4 gives cycles-1 to count - * high, bit 3-0 gives cycles-1 to count low. Distribute - * these with no more than 1 cycle difference between - * low and high and add low and high to get the actual - * divisor. The base PLL is 208 MHz. Writing 0x00 will - * divide by 1 and 1 so the highest frequency possible - * is 104 MHz. - * - * e.g. 0x54 => - * f = 208 / ((5+1) + (4+1)) = 208 / 11 = 18.9 MHz - */ - u16 val = readw(U300_SYSCON_VBASE + U300_SYSCON_MMF0R) & - U300_SYSCON_MMF0R_MASK; - switch (val) { - case 0x0054: - return 18900000; - case 0x0044: - return 20800000; - case 0x0043: - return 23100000; - case 0x0033: - return 26000000; - case 0x0032: - return 29700000; - case 0x0022: - return 34700000; - case 0x0021: - return 41600000; - case 0x0011: - return 52000000; - case 0x0000: - return 104000000; - default: - break; - } - } - default: - break; - } - - return clk->rate; -} - -static unsigned long clk_get_rate_i2s_i2c_spi(struct clk *clk) -{ - u16 val; - - val = syscon_clk_get_rate(); - - switch (val) { - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: - return 13000000; - case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: - case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: - return 26000000; - default: - break; - } - - return clk->rate; -} - -unsigned long clk_get_rate(struct clk *clk) -{ - if (clk->get_rate) - return clk->get_rate(clk); - else - return clk->rate; -} -EXPORT_SYMBOL(clk_get_rate); - -static unsigned long clk_round_rate_mclk(struct clk *clk, unsigned long rate) -{ - if (rate <= 18900000) - return 18900000; - if (rate <= 20800000) - return 20800000; - if (rate <= 23100000) - return 23100000; - if (rate <= 26000000) - return 26000000; - if (rate <= 29700000) - return 29700000; - if (rate <= 34700000) - return 34700000; - if (rate <= 41600000) - return 41600000; - if (rate <= 52000000) - return 52000000; - return -EINVAL; -} - -static unsigned long clk_round_rate_cpuclk(struct clk *clk, unsigned long rate) -{ - if (rate <= 13000000) - return 13000000; - if (rate <= 52000000) - return 52000000; - if (rate <= 104000000) - return 104000000; - if (rate <= 208000000) - return 208000000; - return -EINVAL; -} - -/* - * This adjusts a requested rate to the closest exact rate - * a certain clock can provide. For a fixed clock it's - * mostly clk->rate. - */ -long clk_round_rate(struct clk *clk, unsigned long rate) -{ - /* TODO: get appropriate switches for EMIFCLK, AHBCLK and MCLK */ - /* Else default to fixed value */ - - if (clk->round_rate) { - return (long) clk->round_rate(clk, rate); - } else { - printk(KERN_ERR "clock: Failed to round rate of %s\n", - clk->name); - } - return (long) clk->rate; -} -EXPORT_SYMBOL(clk_round_rate); - -static int clk_set_rate_mclk(struct clk *clk, unsigned long rate) -{ - syscon_clk_rate_set_mclk(clk_round_rate(clk, rate)); - return 0; -} - -static int clk_set_rate_cpuclk(struct clk *clk, unsigned long rate) -{ - syscon_clk_rate_set_cpuclk(clk_round_rate(clk, rate)); - return 0; -} - -int clk_set_rate(struct clk *clk, unsigned long rate) -{ - /* TODO: set for EMIFCLK and AHBCLK */ - /* Else assume the clock is fixed and fail */ - if (clk->set_rate) { - return clk->set_rate(clk, rate); - } else { - printk(KERN_ERR "clock: Failed to set %s to %ld hz\n", - clk->name, rate); - return -EINVAL; - } -} -EXPORT_SYMBOL(clk_set_rate); - -/* - * Clock definitions. The clock parents are set to respective - * bridge and the clock framework makes sure that the clocks have - * parents activated and are brought out of reset when in use. - * - * Clocks that have hw_ctrld = true are hw controlled, and the hw - * can by itself turn these clocks on and off. - * So in other words, we don't really have to care about them. - */ - -static struct clk amba_clk = { - .name = "AMBA", - .rate = 52000000, /* this varies! */ - .hw_ctrld = true, - .reset = false, - .lock = __SPIN_LOCK_UNLOCKED(amba_clk.lock), -}; - -/* - * These blocks are connected directly to the AMBA bus - * with no bridge. - */ - -static struct clk cpu_clk = { - .name = "CPU", - .parent = &amba_clk, - .rate = 208000000, /* this varies! */ - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_CPU_RESET_EN, - .set_rate = clk_set_rate_cpuclk, - .get_rate = clk_get_rate_cpuclk, - .round_rate = clk_round_rate_cpuclk, - .lock = __SPIN_LOCK_UNLOCKED(cpu_clk.lock), -}; - -static struct clk nandif_clk = { - .name = "FSMC", - .parent = &amba_clk, - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_NANDIF_RESET_EN, - .clk_val = U300_SYSCON_SBCER_NANDIF_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(nandif_clk.lock), -}; - -static struct clk semi_clk = { - .name = "SEMI", - .parent = &amba_clk, - .rate = 0, /* FIXME */ - /* It is not possible to reset SEMI */ - .hw_ctrld = false, - .reset = false, - .clk_val = U300_SYSCON_SBCER_SEMI_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(semi_clk.lock), -}; - -#ifdef CONFIG_MACH_U300_BS335 -static struct clk isp_clk = { - .name = "ISP", - .parent = &amba_clk, - .rate = 0, /* FIXME */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_ISP_RESET_EN, - .clk_val = U300_SYSCON_SBCER_ISP_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(isp_clk.lock), -}; - -static struct clk cds_clk = { - .name = "CDS", - .parent = &amba_clk, - .rate = 0, /* FIXME */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_CDS_RESET_EN, - .clk_val = U300_SYSCON_SBCER_CDS_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(cds_clk.lock), -}; -#endif - -static struct clk dma_clk = { - .name = "DMA", - .parent = &amba_clk, - .rate = 52000000, /* this varies! */ - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_DMAC_RESET_EN, - .clk_val = U300_SYSCON_SBCER_DMAC_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(dma_clk.lock), -}; - -static struct clk aaif_clk = { - .name = "AAIF", - .parent = &amba_clk, - .rate = 52000000, /* this varies! */ - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_AAIF_RESET_EN, - .clk_val = U300_SYSCON_SBCER_AAIF_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(aaif_clk.lock), -}; - -static struct clk apex_clk = { - .name = "APEX", - .parent = &amba_clk, - .rate = 0, /* FIXME */ - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_APEX_RESET_EN, - .clk_val = U300_SYSCON_SBCER_APEX_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(apex_clk.lock), -}; - -static struct clk video_enc_clk = { - .name = "VIDEO_ENC", - .parent = &amba_clk, - .rate = 208000000, /* this varies! */ - .hw_ctrld = false, - .reset = false, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - /* This has XGAM in the name but refers to the video encoder */ - .res_mask = U300_SYSCON_RRR_XGAM_VC_SYNC_RESET_EN, - .clk_val = U300_SYSCON_SBCER_VIDEO_ENC_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(video_enc_clk.lock), -}; - -static struct clk xgam_clk = { - .name = "XGAMCLK", - .parent = &amba_clk, - .rate = 52000000, /* this varies! */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_XGAM_RESET_EN, - .clk_val = U300_SYSCON_SBCER_XGAM_CLK_EN, - .get_rate = clk_get_rate_xgamclk, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(xgam_clk.lock), -}; - -/* This clock is used to activate the video encoder */ -static struct clk ahb_clk = { - .name = "AHB", - .parent = &amba_clk, - .rate = 52000000, /* this varies! */ - .hw_ctrld = false, /* This one is set to false due to HW bug */ - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_AHB_RESET_EN, - .clk_val = U300_SYSCON_SBCER_AHB_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_ahb_clk, - .lock = __SPIN_LOCK_UNLOCKED(ahb_clk.lock), -}; - - -/* - * Clocks on the AHB bridge - */ - -static struct clk ahb_subsys_clk = { - .name = "AHB_SUBSYS", - .parent = &amba_clk, - .rate = 52000000, /* this varies! */ - .hw_ctrld = true, - .reset = false, - .clk_val = U300_SYSCON_SBCER_AHB_SUBSYS_BRIDGE_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_ahb_clk, - .lock = __SPIN_LOCK_UNLOCKED(ahb_subsys_clk.lock), -}; - -static struct clk intcon_clk = { - .name = "INTCON", - .parent = &ahb_subsys_clk, - .rate = 52000000, /* this varies! */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_INTCON_RESET_EN, - /* INTCON can be reset but not clock-gated */ - .lock = __SPIN_LOCK_UNLOCKED(intcon_clk.lock), - -}; - -static struct clk mspro_clk = { - .name = "MSPRO", - .parent = &ahb_subsys_clk, - .rate = 0, /* FIXME */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_MSPRO_RESET_EN, - .clk_val = U300_SYSCON_SBCER_MSPRO_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(mspro_clk.lock), -}; - -static struct clk emif_clk = { - .name = "EMIF", - .parent = &ahb_subsys_clk, - .rate = 104000000, /* this varies! */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RRR, - .res_mask = U300_SYSCON_RRR_EMIF_RESET_EN, - .clk_val = U300_SYSCON_SBCER_EMIF_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_emif_clk, - .lock = __SPIN_LOCK_UNLOCKED(emif_clk.lock), -}; - - -/* - * Clocks on the FAST bridge - */ -static struct clk fast_clk = { - .name = "FAST_BRIDGE", - .parent = &amba_clk, - .rate = 13000000, /* this varies! */ - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_FAST_BRIDGE_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(fast_clk.lock), -}; - -/* - * The MMCI apb_pclk is hardwired to the same terminal as the - * external MCI clock. Thus this will be referenced twice. - */ -static struct clk mmcsd_clk = { - .name = "MCLK", - .parent = &fast_clk, - .rate = 18900000, /* this varies! */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_MMC_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_MMC_CLK_EN, - .get_rate = clk_get_rate_mclk, - .set_rate = clk_set_rate_mclk, - .round_rate = clk_round_rate_mclk, - .disable = syscon_clk_disable, - .enable = syscon_clk_enable, - .lock = __SPIN_LOCK_UNLOCKED(mmcsd_clk.lock), -}; - -static struct clk i2s0_clk = { - .name = "i2s0", - .parent = &fast_clk, - .rate = 26000000, /* this varies! */ - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_PCM_I2S0_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_I2S0_CORE_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_i2s_i2c_spi, - .lock = __SPIN_LOCK_UNLOCKED(i2s0_clk.lock), -}; - -static struct clk i2s1_clk = { - .name = "i2s1", - .parent = &fast_clk, - .rate = 26000000, /* this varies! */ - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_PCM_I2S1_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_I2S1_CORE_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_i2s_i2c_spi, - .lock = __SPIN_LOCK_UNLOCKED(i2s1_clk.lock), -}; - -static struct clk i2c0_clk = { - .name = "I2C0", - .parent = &fast_clk, - .rate = 26000000, /* this varies! */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_I2C0_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_I2C0_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_i2s_i2c_spi, - .lock = __SPIN_LOCK_UNLOCKED(i2c0_clk.lock), -}; - -static struct clk i2c1_clk = { - .name = "I2C1", - .parent = &fast_clk, - .rate = 26000000, /* this varies! */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_I2C1_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_I2C1_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_i2s_i2c_spi, - .lock = __SPIN_LOCK_UNLOCKED(i2c1_clk.lock), -}; - -/* - * The SPI apb_pclk is hardwired to the same terminal as the - * external SPI clock. Thus this will be referenced twice. - */ -static struct clk spi_clk = { - .name = "SPI", - .parent = &fast_clk, - .rate = 26000000, /* this varies! */ - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_SPI_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_SPI_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .get_rate = clk_get_rate_i2s_i2c_spi, - .lock = __SPIN_LOCK_UNLOCKED(spi_clk.lock), -}; - -#ifdef CONFIG_MACH_U300_BS335 -static struct clk uart1_pclk = { - .name = "UART1_PCLK", - .parent = &fast_clk, - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR, - .res_mask = U300_SYSCON_RFR_UART1_RESET_ENABLE, - .clk_val = U300_SYSCON_SBCER_UART1_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(uart1_pclk.lock), -}; - -/* This one is hardwired to PLL13 */ -static struct clk uart1_clk = { - .name = "UART1_CLK", - .rate = 13000000, - .hw_ctrld = true, - .lock = __SPIN_LOCK_UNLOCKED(uart1_clk.lock), -}; -#endif - - -/* - * Clocks on the SLOW bridge - */ -static struct clk slow_clk = { - .name = "SLOW_BRIDGE", - .parent = &amba_clk, - .rate = 13000000, - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_SLOW_BRIDGE_RESET_EN, - .clk_val = U300_SYSCON_SBCER_SLOW_BRIDGE_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(slow_clk.lock), -}; - -/* TODO: implement SYSCON clock? */ - -static struct clk wdog_clk = { - .name = "WDOG", - .parent = &slow_clk, - .hw_ctrld = false, - .rate = 32768, - .reset = false, - /* This is always on, cannot be enabled/disabled or reset */ - .lock = __SPIN_LOCK_UNLOCKED(wdog_clk.lock), -}; - -static struct clk uart0_pclk = { - .name = "UART0_PCLK", - .parent = &slow_clk, - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_UART_RESET_EN, - .clk_val = U300_SYSCON_SBCER_UART_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(uart0_pclk.lock), -}; - -/* This one is hardwired to PLL13 */ -static struct clk uart0_clk = { - .name = "UART0_CLK", - .parent = &slow_clk, - .rate = 13000000, - .hw_ctrld = true, - .lock = __SPIN_LOCK_UNLOCKED(uart0_clk.lock), -}; - -static struct clk keypad_clk = { - .name = "KEYPAD", - .parent = &slow_clk, - .rate = 32768, - .hw_ctrld = false, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_KEYPAD_RESET_EN, - .clk_val = U300_SYSCON_SBCER_KEYPAD_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(keypad_clk.lock), -}; - -static struct clk gpio_clk = { - .name = "GPIO", - .parent = &slow_clk, - .rate = 13000000, - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_GPIO_RESET_EN, - .clk_val = U300_SYSCON_SBCER_GPIO_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(gpio_clk.lock), -}; - -static struct clk rtc_clk = { - .name = "RTC", - .parent = &slow_clk, - .rate = 32768, - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_RTC_RESET_EN, - /* This clock is always on, cannot be enabled/disabled */ - .lock = __SPIN_LOCK_UNLOCKED(rtc_clk.lock), -}; - -static struct clk bustr_clk = { - .name = "BUSTR", - .parent = &slow_clk, - .rate = 13000000, - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_BTR_RESET_EN, - .clk_val = U300_SYSCON_SBCER_BTR_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(bustr_clk.lock), -}; - -static struct clk evhist_clk = { - .name = "EVHIST", - .parent = &slow_clk, - .rate = 13000000, - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_EH_RESET_EN, - .clk_val = U300_SYSCON_SBCER_EH_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(evhist_clk.lock), -}; - -static struct clk timer_clk = { - .name = "TIMER", - .parent = &slow_clk, - .rate = 13000000, - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_ACC_TMR_RESET_EN, - .clk_val = U300_SYSCON_SBCER_ACC_TMR_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(timer_clk.lock), -}; - -/* - * There is a binary divider in the hardware that divides - * the 13MHz PLL by 13 down to 1 MHz. - */ -static struct clk app_timer_clk = { - .name = "TIMER_APP", - .parent = &slow_clk, - .rate = 1000000, - .hw_ctrld = true, - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_APP_TMR_RESET_EN, - .clk_val = U300_SYSCON_SBCER_APP_TMR_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(app_timer_clk.lock), -}; - -#ifdef CONFIG_MACH_U300_BS335 -static struct clk ppm_clk = { - .name = "PPM", - .parent = &slow_clk, - .rate = 0, /* FIXME */ - .hw_ctrld = true, /* TODO: Look up if it is hw ctrld or not */ - .reset = true, - .res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR, - .res_mask = U300_SYSCON_RSR_PPM_RESET_EN, - .clk_val = U300_SYSCON_SBCER_PPM_CLK_EN, - .enable = syscon_clk_enable, - .disable = syscon_clk_disable, - .lock = __SPIN_LOCK_UNLOCKED(ppm_clk.lock), -}; -#endif - -#define DEF_LOOKUP(devid, clkref) \ - { \ - .dev_id = devid, \ - .clk = clkref, \ - } - -#define DEF_LOOKUP_CON(devid, conid, clkref) \ - { \ - .dev_id = devid, \ - .con_id = conid, \ - .clk = clkref, \ - } - -/* - * Here we only define clocks that are meaningful to - * look up through clockdevice. - */ -static struct clk_lookup lookups[] = { - /* Connected directly to the AMBA bus */ - DEF_LOOKUP("amba", &amba_clk), - DEF_LOOKUP("cpu", &cpu_clk), - DEF_LOOKUP("fsmc-nand", &nandif_clk), - DEF_LOOKUP("semi", &semi_clk), -#ifdef CONFIG_MACH_U300_BS335 - DEF_LOOKUP("isp", &isp_clk), - DEF_LOOKUP("cds", &cds_clk), -#endif - DEF_LOOKUP("dma", &dma_clk), - DEF_LOOKUP("msl", &aaif_clk), - DEF_LOOKUP("apex", &apex_clk), - DEF_LOOKUP("video_enc", &video_enc_clk), - DEF_LOOKUP("xgam", &xgam_clk), - DEF_LOOKUP("ahb", &ahb_clk), - /* AHB bridge clocks */ - DEF_LOOKUP("ahb_subsys", &ahb_subsys_clk), - DEF_LOOKUP("intcon", &intcon_clk), - DEF_LOOKUP_CON("intcon", "apb_pclk", &intcon_clk), - DEF_LOOKUP("mspro", &mspro_clk), - DEF_LOOKUP("pl172", &emif_clk), - DEF_LOOKUP_CON("pl172", "apb_pclk", &emif_clk), - /* FAST bridge clocks */ - DEF_LOOKUP("fast", &fast_clk), - DEF_LOOKUP("mmci", &mmcsd_clk), - DEF_LOOKUP_CON("mmci", "apb_pclk", &mmcsd_clk), - /* - * The .0 and .1 identifiers on these comes from the platform device - * .id field and are assigned when the platform devices are registered. - */ - DEF_LOOKUP("i2s.0", &i2s0_clk), - DEF_LOOKUP("i2s.1", &i2s1_clk), - DEF_LOOKUP("stu300.0", &i2c0_clk), - DEF_LOOKUP("stu300.1", &i2c1_clk), - DEF_LOOKUP("pl022", &spi_clk), - DEF_LOOKUP_CON("pl022", "apb_pclk", &spi_clk), -#ifdef CONFIG_MACH_U300_BS335 - DEF_LOOKUP("uart1", &uart1_clk), - DEF_LOOKUP_CON("uart1", "apb_pclk", &uart1_pclk), -#endif - /* SLOW bridge clocks */ - DEF_LOOKUP("slow", &slow_clk), - DEF_LOOKUP("coh901327_wdog", &wdog_clk), - DEF_LOOKUP("uart0", &uart0_clk), - DEF_LOOKUP_CON("uart0", "apb_pclk", &uart0_pclk), - DEF_LOOKUP("apptimer", &app_timer_clk), - DEF_LOOKUP("coh901461-keypad", &keypad_clk), - DEF_LOOKUP("u300-gpio", &gpio_clk), - DEF_LOOKUP("rtc-coh901331", &rtc_clk), - DEF_LOOKUP("bustr", &bustr_clk), - DEF_LOOKUP("evhist", &evhist_clk), - DEF_LOOKUP("timer", &timer_clk), -#ifdef CONFIG_MACH_U300_BS335 - DEF_LOOKUP("ppm", &ppm_clk), -#endif -}; - -static void __init clk_register(void) -{ - /* Register the lookups */ - clkdev_add_table(lookups, ARRAY_SIZE(lookups)); -} - -#if (defined(CONFIG_DEBUG_FS) && defined(CONFIG_U300_DEBUG)) -/* - * The following makes it possible to view the status (especially - * reference count and reset status) for the clocks in the platform - * by looking into the special file /u300_clocks - */ - -/* A list of all clocks in the platform */ -static struct clk *clks[] = { - /* Top node clock for the AMBA bus */ - &amba_clk, - /* Connected directly to the AMBA bus */ - &cpu_clk, - &nandif_clk, - &semi_clk, -#ifdef CONFIG_MACH_U300_BS335 - &isp_clk, - &cds_clk, -#endif - &dma_clk, - &aaif_clk, - &apex_clk, - &video_enc_clk, - &xgam_clk, - &ahb_clk, - - /* AHB bridge clocks */ - &ahb_subsys_clk, - &intcon_clk, - &mspro_clk, - &emif_clk, - /* FAST bridge clocks */ - &fast_clk, - &mmcsd_clk, - &i2s0_clk, - &i2s1_clk, - &i2c0_clk, - &i2c1_clk, - &spi_clk, -#ifdef CONFIG_MACH_U300_BS335 - &uart1_clk, - &uart1_pclk, -#endif - /* SLOW bridge clocks */ - &slow_clk, - &wdog_clk, - &uart0_clk, - &uart0_pclk, - &app_timer_clk, - &keypad_clk, - &gpio_clk, - &rtc_clk, - &bustr_clk, - &evhist_clk, - &timer_clk, -#ifdef CONFIG_MACH_U300_BS335 - &ppm_clk, -#endif -}; - -static int u300_clocks_show(struct seq_file *s, void *data) -{ - struct clk *clk; - int i; - - seq_printf(s, "CLOCK DEVICE RESET STATE\t" \ - "ACTIVE\tUSERS\tHW CTRL FREQ\n"); - seq_printf(s, "---------------------------------------------" \ - "-----------------------------------------\n"); - for (i = 0; i < ARRAY_SIZE(clks); i++) { - clk = clks[i]; - if (clk != ERR_PTR(-ENOENT)) { - /* Format clock and device name nicely */ - char cdp[33]; - int chars; - - chars = snprintf(&cdp[0], 17, "%s", clk->name); - while (chars < 16) { - cdp[chars] = ' '; - chars++; - } - chars = snprintf(&cdp[16], 17, "%s", clk->dev ? - dev_name(clk->dev) : "N/A"); - while (chars < 16) { - cdp[chars+16] = ' '; - chars++; - } - cdp[32] = '\0'; - if (clk->get_rate || clk->rate != 0) - seq_printf(s, - "%s%s\t%s\t%d\t%s\t%lu Hz\n", - &cdp[0], - clk->reset ? - "ASSERTED" : "RELEASED", - clk->usecount ? "ON" : "OFF", - clk->usecount, - clk->hw_ctrld ? "YES" : "NO ", - clk_get_rate(clk)); - else - seq_printf(s, - "%s%s\t%s\t%d\t%s\t" \ - "(unknown rate)\n", - &cdp[0], - clk->reset ? - "ASSERTED" : "RELEASED", - clk->usecount ? "ON" : "OFF", - clk->usecount, - clk->hw_ctrld ? "YES" : "NO "); - } - } - return 0; -} - -static int u300_clocks_open(struct inode *inode, struct file *file) -{ - return single_open(file, u300_clocks_show, NULL); -} - -static const struct file_operations u300_clocks_operations = { - .open = u300_clocks_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int __init init_clk_read_debugfs(void) -{ - /* Expose a simple debugfs interface to view all clocks */ - (void) debugfs_create_file("u300_clocks", S_IFREG | S_IRUGO, - NULL, NULL, - &u300_clocks_operations); - return 0; -} -/* - * This needs to come in after the core_initcall() for the - * overall clocks, because debugfs is not available until - * the subsystems come up. - */ -module_init(init_clk_read_debugfs); -#endif - -int __init u300_clock_init(void) -{ - u16 val; - - /* - * FIXME: shall all this powermanagement stuff really live here??? - */ - - /* Set system to run at PLL208, max performance, a known state. */ - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); - val &= ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - /* Wait for the PLL208 to lock if not locked in yet */ - while (!(readw(U300_SYSCON_VBASE + U300_SYSCON_CSR) & - U300_SYSCON_CSR_PLL208_LOCK_IND)); - - /* Power management enable */ - val = readw(U300_SYSCON_VBASE + U300_SYSCON_PMCR); - val |= U300_SYSCON_PMCR_PWR_MGNT_ENABLE; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_PMCR); - - clk_register(); - - /* - * Some of these may be on when we boot the system so make sure they - * are turned OFF. - */ - syscon_block_reset_enable(&timer_clk); - timer_clk.disable(&timer_clk); - - /* - * These shall be turned on by default when we boot the system - * so make sure they are ON. (Adding CPU here is a bit too much.) - * These clocks will be claimed by drivers later. - */ - syscon_block_reset_disable(&semi_clk); - syscon_block_reset_disable(&emif_clk); - clk_enable(&semi_clk); - clk_enable(&emif_clk); - - return 0; -} diff --git a/arch/arm/mach-u300/clock.h b/arch/arm/mach-u300/clock.h deleted file mode 100644 index 4f50ca8f901e..000000000000 --- a/arch/arm/mach-u300/clock.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * arch/arm/mach-u300/include/mach/clock.h - * - * Copyright (C) 2004 - 2005 Nokia corporation - * Written by Tuukka Tikkanen - * Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, Inc - * Copyright (C) 2007-2009 ST-Ericsson AB - * Adopted to ST-Ericsson U300 platforms by - * Jonas Aaberg - * - * 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 __MACH_CLOCK_H -#define __MACH_CLOCK_H - -#include - -struct clk { - struct list_head node; - struct module *owner; - struct device *dev; - const char *name; - struct clk *parent; - - spinlock_t lock; - unsigned long rate; - bool reset; - __u16 clk_val; - __s8 usecount; - void __iomem * res_reg; - __u16 res_mask; - - bool hw_ctrld; - - void (*recalc) (struct clk *); - int (*set_rate) (struct clk *, unsigned long); - unsigned long (*get_rate) (struct clk *); - unsigned long (*round_rate) (struct clk *, unsigned long); - void (*init) (struct clk *); - void (*enable) (struct clk *); - void (*disable) (struct clk *); -}; - -int u300_clock_init(void); - -#endif diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index 33339745d432..03acf1883ec7 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -44,7 +45,6 @@ #include #include -#include "clock.h" #include "spi.h" #include "i2c.h" #include "u300-gpio.h" @@ -1658,12 +1658,20 @@ void __init u300_init_irq(void) int i; /* initialize clocking early, we want to clock the INTCON */ - u300_clock_init(); + u300_clk_init(U300_SYSCON_VBASE); + + /* Bootstrap EMIF and SEMI clocks */ + clk = clk_get_sys("pl172", NULL); + BUG_ON(IS_ERR(clk)); + clk_prepare_enable(clk); + clk = clk_get_sys("semi", NULL); + BUG_ON(IS_ERR(clk)); + clk_prepare_enable(clk); /* Clock the interrupt controller */ clk = clk_get_sys("intcon", NULL); BUG_ON(IS_ERR(clk)); - clk_enable(clk); + clk_prepare_enable(clk); for (i = 0; i < U300_VIC_IRQS_END; i++) set_bit(i, (unsigned long *) &mask[0]); @@ -1811,13 +1819,6 @@ void __init u300_init_devices(void) /* Check what platform we run and print some status information */ u300_init_check_chip(); - /* Set system to run at PLL208, max performance, a known state. */ - val = readw(U300_SYSCON_VBASE + U300_SYSCON_CCR); - val &= ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; - writew(val, U300_SYSCON_VBASE + U300_SYSCON_CCR); - /* Wait for the PLL208 to lock if not locked in yet */ - while (!(readw(U300_SYSCON_VBASE + U300_SYSCON_CSR) & - U300_SYSCON_CSR_PLL208_LOCK_IND)); /* Initialize SPI device with some board specifics */ u300_spi_init(&pl022_device); diff --git a/arch/arm/mach-u300/timer.c b/arch/arm/mach-u300/timer.c index bc1c7897e82d..56ac06d38ec1 100644 --- a/arch/arm/mach-u300/timer.c +++ b/arch/arm/mach-u300/timer.c @@ -354,7 +354,7 @@ static void __init u300_timer_init(void) /* Clock the interrupt controller */ clk = clk_get_sys("apptimer", NULL); BUG_ON(IS_ERR(clk)); - clk_enable(clk); + clk_prepare_enable(clk); rate = clk_get_rate(clk); setup_sched_clock(u300_read_sched_clock, 32, rate); diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index b679f117f3cc..35d9fb44c814 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -1,10 +1,11 @@ - +# common clock types obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \ clk-mux.o clk-divider.o clk-fixed-factor.o # SoCs specific obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_PLAT_SPEAR) += spear/ +obj-$(CONFIG_ARCH_U300) += clk-u300.o # Chip specific obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o diff --git a/drivers/clk/clk-u300.c b/drivers/clk/clk-u300.c new file mode 100644 index 000000000000..a15f7928fb11 --- /dev/null +++ b/drivers/clk/clk-u300.c @@ -0,0 +1,746 @@ +/* + * U300 clock implementation + * Copyright (C) 2007-2012 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * Author: Linus Walleij + * Author: Jonas Aaberg + */ +#include +#include +#include +#include +#include +#include +#include + +/* + * The clocking hierarchy currently looks like this. + * NOTE: the idea is NOT to show how the clocks are routed on the chip! + * The ideas is to show dependencies, so a clock higher up in the + * hierarchy has to be on in order for another clock to be on. Now, + * both CPU and DMA can actually be on top of the hierarchy, and that + * is not modeled currently. Instead we have the backbone AMBA bus on + * top. This bus cannot be programmed in any way but conceptually it + * needs to be active for the bridges and devices to transport data. + * + * Please be aware that a few clocks are hw controlled, which mean that + * the hw itself can turn on/off or change the rate of the clock when + * needed! + * + * AMBA bus + * | + * +- CPU + * +- FSMC NANDIF NAND Flash interface + * +- SEMI Shared Memory interface + * +- ISP Image Signal Processor (U335 only) + * +- CDS (U335 only) + * +- DMA Direct Memory Access Controller + * +- AAIF APP/ACC Inteface (Mobile Scalable Link, MSL) + * +- APEX + * +- VIDEO_ENC AVE2/3 Video Encoder + * +- XGAM Graphics Accelerator Controller + * +- AHB + * | + * +- ahb:0 AHB Bridge + * | | + * | +- ahb:1 INTCON Interrupt controller + * | +- ahb:3 MSPRO Memory Stick Pro controller + * | +- ahb:4 EMIF External Memory interface + * | + * +- fast:0 FAST bridge + * | | + * | +- fast:1 MMCSD MMC/SD card reader controller + * | +- fast:2 I2S0 PCM I2S channel 0 controller + * | +- fast:3 I2S1 PCM I2S channel 1 controller + * | +- fast:4 I2C0 I2C channel 0 controller + * | +- fast:5 I2C1 I2C channel 1 controller + * | +- fast:6 SPI SPI controller + * | +- fast:7 UART1 Secondary UART (U335 only) + * | + * +- slow:0 SLOW bridge + * | + * +- slow:1 SYSCON (not possible to control) + * +- slow:2 WDOG Watchdog + * +- slow:3 UART0 primary UART + * +- slow:4 TIMER_APP Application timer - used in Linux + * +- slow:5 KEYPAD controller + * +- slow:6 GPIO controller + * +- slow:7 RTC controller + * +- slow:8 BT Bus Tracer (not used currently) + * +- slow:9 EH Event Handler (not used currently) + * +- slow:a TIMER_ACC Access style timer (not used currently) + * +- slow:b PPM (U335 only, what is that?) + */ + +/* Global syscon virtual base */ +static void __iomem *syscon_vbase; + +/** + * struct clk_syscon - U300 syscon clock + * @hw: corresponding clock hardware entry + * @hw_ctrld: whether this clock is hardware controlled (for refcount etc) + * and does not need any magic pokes to be enabled/disabled + * @reset: state holder, whether this block's reset line is asserted or not + * @res_reg: reset line enable/disable flag register + * @res_bit: bit for resetting or taking this consumer out of reset + * @en_reg: clock line enable/disable flag register + * @en_bit: bit for enabling/disabling this consumer clock line + * @clk_val: magic value to poke in the register to enable/disable + * this one clock + */ +struct clk_syscon { + struct clk_hw hw; + bool hw_ctrld; + bool reset; + void __iomem *res_reg; + u8 res_bit; + void __iomem *en_reg; + u8 en_bit; + u16 clk_val; +}; + +#define to_syscon(_hw) container_of(_hw, struct clk_syscon, hw) + +static DEFINE_SPINLOCK(syscon_resetreg_lock); + +/* + * Reset control functions. We remember if a block has been + * taken out of reset and don't remove the reset assertion again + * and vice versa. Currently we only remove resets so the + * enablement function is defined out. + */ +static void syscon_block_reset_enable(struct clk_syscon *sclk) +{ + unsigned long iflags; + u16 val; + + /* Not all blocks support resetting */ + if (!sclk->res_reg) + return; + spin_lock_irqsave(&syscon_resetreg_lock, iflags); + val = readw(sclk->res_reg); + val |= BIT(sclk->res_bit); + writew(val, sclk->res_reg); + spin_unlock_irqrestore(&syscon_resetreg_lock, iflags); + sclk->reset = true; +} + +static void syscon_block_reset_disable(struct clk_syscon *sclk) +{ + unsigned long iflags; + u16 val; + + /* Not all blocks support resetting */ + if (!sclk->res_reg) + return; + spin_lock_irqsave(&syscon_resetreg_lock, iflags); + val = readw(sclk->res_reg); + val &= ~BIT(sclk->res_bit); + writew(val, sclk->res_reg); + spin_unlock_irqrestore(&syscon_resetreg_lock, iflags); + sclk->reset = false; +} + +static int syscon_clk_prepare(struct clk_hw *hw) +{ + struct clk_syscon *sclk = to_syscon(hw); + + /* If the block is in reset, bring it out */ + if (sclk->reset) + syscon_block_reset_disable(sclk); + return 0; +} + +static void syscon_clk_unprepare(struct clk_hw *hw) +{ + struct clk_syscon *sclk = to_syscon(hw); + + /* Please don't force the console into reset */ + if (sclk->clk_val == U300_SYSCON_SBCER_UART_CLK_EN) + return; + /* When unpreparing, force block into reset */ + if (!sclk->reset) + syscon_block_reset_enable(sclk); +} + +static int syscon_clk_enable(struct clk_hw *hw) +{ + struct clk_syscon *sclk = to_syscon(hw); + + /* Don't touch the hardware controlled clocks */ + if (sclk->hw_ctrld) + return 0; + /* These cannot be controlled */ + if (sclk->clk_val == 0xFFFFU) + return 0; + + writew(sclk->clk_val, syscon_vbase + U300_SYSCON_SBCER); + return 0; +} + +static void syscon_clk_disable(struct clk_hw *hw) +{ + struct clk_syscon *sclk = to_syscon(hw); + + /* Don't touch the hardware controlled clocks */ + if (sclk->hw_ctrld) + return; + if (sclk->clk_val == 0xFFFFU) + return; + /* Please don't disable the console port */ + if (sclk->clk_val == U300_SYSCON_SBCER_UART_CLK_EN) + return; + + writew(sclk->clk_val, syscon_vbase + U300_SYSCON_SBCDR); +} + +static int syscon_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_syscon *sclk = to_syscon(hw); + u16 val; + + /* If no enable register defined, it's always-on */ + if (!sclk->en_reg) + return 1; + + val = readw(sclk->en_reg); + val &= BIT(sclk->en_bit); + + return val ? 1 : 0; +} + +static u16 syscon_get_perf(void) +{ + u16 val; + + val = readw(syscon_vbase + U300_SYSCON_CCR); + val &= U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; + return val; +} + +static unsigned long +syscon_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_syscon *sclk = to_syscon(hw); + u16 perf = syscon_get_perf(); + + switch(sclk->clk_val) { + case U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN: + case U300_SYSCON_SBCER_I2C0_CLK_EN: + case U300_SYSCON_SBCER_I2C1_CLK_EN: + case U300_SYSCON_SBCER_MMC_CLK_EN: + case U300_SYSCON_SBCER_SPI_CLK_EN: + /* The FAST clocks have one progression */ + switch(perf) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 13000000; + default: + return parent_rate; /* 26 MHz */ + } + case U300_SYSCON_SBCER_DMAC_CLK_EN: + case U300_SYSCON_SBCER_NANDIF_CLK_EN: + case U300_SYSCON_SBCER_XGAM_CLK_EN: + /* AMBA interconnect peripherals */ + switch(perf) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 6500000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + return 26000000; + default: + return parent_rate; /* 52 MHz */ + } + case U300_SYSCON_SBCER_SEMI_CLK_EN: + case U300_SYSCON_SBCER_EMIF_CLK_EN: + /* EMIF speeds */ + switch(perf) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 13000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + return 52000000; + default: + return 104000000; + } + case U300_SYSCON_SBCER_CPU_CLK_EN: + /* And the fast CPU clock */ + switch(perf) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + return 13000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + return 52000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: + return 104000000; + default: + return parent_rate; /* 208 MHz */ + } + default: + /* + * The SLOW clocks and default just inherit the rate of + * their parent (typically PLL13 13 MHz). + */ + return parent_rate; + } +} + +static long +syscon_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_syscon *sclk = to_syscon(hw); + + if (sclk->clk_val != U300_SYSCON_SBCER_CPU_CLK_EN) + return *prate; + /* We really only support setting the rate of the CPU clock */ + if (rate <= 13000000) + return 13000000; + if (rate <= 52000000) + return 52000000; + if (rate <= 104000000) + return 104000000; + return 208000000; +} + +static int syscon_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_syscon *sclk = to_syscon(hw); + u16 val; + + /* We only support setting the rate of the CPU clock */ + if (sclk->clk_val != U300_SYSCON_SBCER_CPU_CLK_EN) + return -EINVAL; + switch (rate) { + case 13000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER; + break; + case 52000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE; + break; + case 104000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH; + break; + case 208000000: + val = U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST; + break; + default: + return -EINVAL; + } + val |= readw(syscon_vbase + U300_SYSCON_CCR) & + ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK ; + writew(val, syscon_vbase + U300_SYSCON_CCR); + return 0; +} + +static const struct clk_ops syscon_clk_ops = { + .prepare = syscon_clk_prepare, + .unprepare = syscon_clk_unprepare, + .enable = syscon_clk_enable, + .disable = syscon_clk_disable, + .is_enabled = syscon_clk_is_enabled, + .recalc_rate = syscon_clk_recalc_rate, + .round_rate = syscon_clk_round_rate, + .set_rate = syscon_clk_set_rate, +}; + +static struct clk * __init +syscon_clk_register(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + bool hw_ctrld, + void __iomem *res_reg, u8 res_bit, + void __iomem *en_reg, u8 en_bit, + u16 clk_val) +{ + struct clk *clk; + struct clk_syscon *sclk; + struct clk_init_data init; + + sclk = kzalloc(sizeof(struct clk_syscon), GFP_KERNEL); + if (!sclk) { + pr_err("could not allocate syscon clock %s\n", + name); + return ERR_PTR(-ENOMEM); + } + init.name = name; + init.ops = &syscon_clk_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + sclk->hw.init = &init; + sclk->hw_ctrld = hw_ctrld; + /* Assume the block is in reset at registration */ + sclk->reset = true; + sclk->res_reg = res_reg; + sclk->res_bit = res_bit; + sclk->en_reg = en_reg; + sclk->en_bit = en_bit; + sclk->clk_val = clk_val; + + clk = clk_register(dev, &sclk->hw); + if (IS_ERR(clk)) + kfree(sclk); + + return clk; +} + +/** + * struct clk_mclk - U300 MCLK clock (MMC/SD clock) + * @hw: corresponding clock hardware entry + * @is_mspro: if this is the memory stick clock rather than MMC/SD + */ +struct clk_mclk { + struct clk_hw hw; + bool is_mspro; +}; + +#define to_mclk(_hw) container_of(_hw, struct clk_mclk, hw) + +static int mclk_clk_prepare(struct clk_hw *hw) +{ + struct clk_mclk *mclk = to_mclk(hw); + u16 val; + + /* The MMC and MSPRO clocks need some special set-up */ + if (!mclk->is_mspro) { + /* Set default MMC clock divisor to 18.9 MHz */ + writew(0x0054U, syscon_vbase + U300_SYSCON_MMF0R); + val = readw(syscon_vbase + U300_SYSCON_MMCR); + /* Disable the MMC feedback clock */ + val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE; + /* Disable MSPRO frequency */ + val &= ~U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE; + writew(val, syscon_vbase + U300_SYSCON_MMCR); + } else { + val = readw(syscon_vbase + U300_SYSCON_MMCR); + /* Disable the MMC feedback clock */ + val &= ~U300_SYSCON_MMCR_MMC_FB_CLK_SEL_ENABLE; + /* Enable MSPRO frequency */ + val |= U300_SYSCON_MMCR_MSPRO_FREQSEL_ENABLE; + writew(val, syscon_vbase + U300_SYSCON_MMCR); + } + + return 0; +} + +static unsigned long +mclk_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u16 perf = syscon_get_perf(); + + switch (perf) { + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW_POWER: + /* + * Here, the 208 MHz PLL gets shut down and the always + * on 13 MHz PLL used for RTC etc kicks into use + * instead. + */ + return 13000000; + case U300_SYSCON_CCR_CLKING_PERFORMANCE_LOW: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_INTERMEDIATE: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_HIGH: + case U300_SYSCON_CCR_CLKING_PERFORMANCE_BEST: + { + /* + * This clock is under program control. The register is + * divided in two nybbles, bit 7-4 gives cycles-1 to count + * high, bit 3-0 gives cycles-1 to count low. Distribute + * these with no more than 1 cycle difference between + * low and high and add low and high to get the actual + * divisor. The base PLL is 208 MHz. Writing 0x00 will + * divide by 1 and 1 so the highest frequency possible + * is 104 MHz. + * + * e.g. 0x54 => + * f = 208 / ((5+1) + (4+1)) = 208 / 11 = 18.9 MHz + */ + u16 val = readw(syscon_vbase + U300_SYSCON_MMF0R) & + U300_SYSCON_MMF0R_MASK; + switch (val) { + case 0x0054: + return 18900000; + case 0x0044: + return 20800000; + case 0x0043: + return 23100000; + case 0x0033: + return 26000000; + case 0x0032: + return 29700000; + case 0x0022: + return 34700000; + case 0x0021: + return 41600000; + case 0x0011: + return 52000000; + case 0x0000: + return 104000000; + default: + break; + } + } + default: + break; + } + return parent_rate; +} + +static long +mclk_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + if (rate <= 18900000) + return 18900000; + if (rate <= 20800000) + return 20800000; + if (rate <= 23100000) + return 23100000; + if (rate <= 26000000) + return 26000000; + if (rate <= 29700000) + return 29700000; + if (rate <= 34700000) + return 34700000; + if (rate <= 41600000) + return 41600000; + /* Highest rate */ + return 52000000; +} + +static int mclk_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u16 val; + u16 reg; + + switch (rate) { + case 18900000: + val = 0x0054; + break; + case 20800000: + val = 0x0044; + break; + case 23100000: + val = 0x0043; + break; + case 26000000: + val = 0x0033; + break; + case 29700000: + val = 0x0032; + break; + case 34700000: + val = 0x0022; + break; + case 41600000: + val = 0x0021; + break; + case 52000000: + val = 0x0011; + break; + case 104000000: + val = 0x0000; + break; + default: + return -EINVAL; + } + + reg = readw(syscon_vbase + U300_SYSCON_MMF0R) & + ~U300_SYSCON_MMF0R_MASK; + writew(reg | val, syscon_vbase + U300_SYSCON_MMF0R); + return 0; +} + +static const struct clk_ops mclk_ops = { + .prepare = mclk_clk_prepare, + .recalc_rate = mclk_clk_recalc_rate, + .round_rate = mclk_clk_round_rate, + .set_rate = mclk_clk_set_rate, +}; + +static struct clk * __init +mclk_clk_register(struct device *dev, const char *name, + const char *parent_name, bool is_mspro) +{ + struct clk *clk; + struct clk_mclk *mclk; + struct clk_init_data init; + + mclk = kzalloc(sizeof(struct clk_mclk), GFP_KERNEL); + if (!mclk) { + pr_err("could not allocate MMC/SD clock %s\n", + name); + return ERR_PTR(-ENOMEM); + } + init.name = "mclk"; + init.ops = &mclk_ops; + init.flags = 0; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + mclk->hw.init = &init; + mclk->is_mspro = is_mspro; + + clk = clk_register(dev, &mclk->hw); + if (IS_ERR(clk)) + kfree(mclk); + + return clk; +} + +void __init u300_clk_init(void __iomem *base) +{ + u16 val; + struct clk *clk; + + syscon_vbase = base; + + /* Set system to run at PLL208, max performance, a known state. */ + val = readw(syscon_vbase + U300_SYSCON_CCR); + val &= ~U300_SYSCON_CCR_CLKING_PERFORMANCE_MASK; + writew(val, syscon_vbase + U300_SYSCON_CCR); + /* Wait for the PLL208 to lock if not locked in yet */ + while (!(readw(syscon_vbase + U300_SYSCON_CSR) & + U300_SYSCON_CSR_PLL208_LOCK_IND)); + + /* Power management enable */ + val = readw(syscon_vbase + U300_SYSCON_PMCR); + val |= U300_SYSCON_PMCR_PWR_MGNT_ENABLE; + writew(val, syscon_vbase + U300_SYSCON_PMCR); + + /* These are always available (RTC and PLL13) */ + clk = clk_register_fixed_rate(NULL, "app_32_clk", NULL, + CLK_IS_ROOT, 32768); + /* The watchdog sits directly on the 32 kHz clock */ + clk_register_clkdev(clk, NULL, "coh901327_wdog"); + clk = clk_register_fixed_rate(NULL, "pll13", NULL, + CLK_IS_ROOT, 13000000); + + /* These derive from PLL208 */ + clk = clk_register_fixed_rate(NULL, "pll208", NULL, + CLK_IS_ROOT, 208000000); + clk = clk_register_fixed_factor(NULL, "app_208_clk", "pll208", + 0, 1, 1); + clk = clk_register_fixed_factor(NULL, "app_104_clk", "pll208", + 0, 1, 2); + clk = clk_register_fixed_factor(NULL, "app_52_clk", "pll208", + 0, 1, 4); + /* The 52 MHz is divided down to 26 MHz */ + clk = clk_register_fixed_factor(NULL, "app_26_clk", "app_52_clk", + 0, 1, 2); + + /* Directly on the AMBA interconnect */ + clk = syscon_clk_register(NULL, "cpu_clk", "app_208_clk", 0, true, + syscon_vbase + U300_SYSCON_RRR, 3, + syscon_vbase + U300_SYSCON_CERR, 3, + U300_SYSCON_SBCER_CPU_CLK_EN); + clk = syscon_clk_register(NULL, "dmac_clk", "app_52_clk", 0, true, + syscon_vbase + U300_SYSCON_RRR, 4, + syscon_vbase + U300_SYSCON_CERR, 4, + U300_SYSCON_SBCER_DMAC_CLK_EN); + clk_register_clkdev(clk, NULL, "dma"); + clk = syscon_clk_register(NULL, "fsmc_clk", "app_52_clk", 0, false, + syscon_vbase + U300_SYSCON_RRR, 6, + syscon_vbase + U300_SYSCON_CERR, 6, + U300_SYSCON_SBCER_NANDIF_CLK_EN); + clk_register_clkdev(clk, NULL, "fsmc-nand"); + clk = syscon_clk_register(NULL, "xgam_clk", "app_52_clk", 0, true, + syscon_vbase + U300_SYSCON_RRR, 8, + syscon_vbase + U300_SYSCON_CERR, 8, + U300_SYSCON_SBCER_XGAM_CLK_EN); + clk_register_clkdev(clk, NULL, "xgam"); + clk = syscon_clk_register(NULL, "semi_clk", "app_104_clk", 0, false, + syscon_vbase + U300_SYSCON_RRR, 9, + syscon_vbase + U300_SYSCON_CERR, 9, + U300_SYSCON_SBCER_SEMI_CLK_EN); + clk_register_clkdev(clk, NULL, "semi"); + + /* AHB bridge clocks */ + clk = syscon_clk_register(NULL, "ahb_subsys_clk", "app_52_clk", 0, true, + syscon_vbase + U300_SYSCON_RRR, 10, + syscon_vbase + U300_SYSCON_CERR, 10, + U300_SYSCON_SBCER_AHB_SUBSYS_BRIDGE_CLK_EN); + clk = syscon_clk_register(NULL, "intcon_clk", "ahb_subsys_clk", 0, false, + syscon_vbase + U300_SYSCON_RRR, 12, + syscon_vbase + U300_SYSCON_CERR, 12, + /* Cannot be enabled, just taken out of reset */ + 0xFFFFU); + clk_register_clkdev(clk, NULL, "intcon"); + clk = syscon_clk_register(NULL, "emif_clk", "ahb_subsys_clk", 0, false, + syscon_vbase + U300_SYSCON_RRR, 5, + syscon_vbase + U300_SYSCON_CERR, 5, + U300_SYSCON_SBCER_EMIF_CLK_EN); + clk_register_clkdev(clk, NULL, "pl172"); + + /* FAST bridge clocks */ + clk = syscon_clk_register(NULL, "fast_clk", "app_26_clk", 0, true, + syscon_vbase + U300_SYSCON_RFR, 0, + syscon_vbase + U300_SYSCON_CEFR, 0, + U300_SYSCON_SBCER_FAST_BRIDGE_CLK_EN); + clk = syscon_clk_register(NULL, "i2c0_p_clk", "fast_clk", 0, false, + syscon_vbase + U300_SYSCON_RFR, 1, + syscon_vbase + U300_SYSCON_CEFR, 1, + U300_SYSCON_SBCER_I2C0_CLK_EN); + clk_register_clkdev(clk, NULL, "stu300.0"); + clk = syscon_clk_register(NULL, "i2c1_p_clk", "fast_clk", 0, false, + syscon_vbase + U300_SYSCON_RFR, 2, + syscon_vbase + U300_SYSCON_CEFR, 2, + U300_SYSCON_SBCER_I2C1_CLK_EN); + clk_register_clkdev(clk, NULL, "stu300.1"); + clk = syscon_clk_register(NULL, "mmc_p_clk", "fast_clk", 0, false, + syscon_vbase + U300_SYSCON_RFR, 5, + syscon_vbase + U300_SYSCON_CEFR, 5, + U300_SYSCON_SBCER_MMC_CLK_EN); + clk_register_clkdev(clk, "apb_pclk", "mmci"); + clk = syscon_clk_register(NULL, "spi_p_clk", "fast_clk", 0, false, + syscon_vbase + U300_SYSCON_RFR, 6, + syscon_vbase + U300_SYSCON_CEFR, 6, + U300_SYSCON_SBCER_SPI_CLK_EN); + /* The SPI has no external clock for the outward bus, uses the pclk */ + clk_register_clkdev(clk, NULL, "pl022"); + clk_register_clkdev(clk, "apb_pclk", "pl022"); + + /* SLOW bridge clocks */ + clk = syscon_clk_register(NULL, "slow_clk", "pll13", 0, true, + syscon_vbase + U300_SYSCON_RSR, 0, + syscon_vbase + U300_SYSCON_CESR, 0, + U300_SYSCON_SBCER_SLOW_BRIDGE_CLK_EN); + clk = syscon_clk_register(NULL, "uart0_clk", "slow_clk", 0, false, + syscon_vbase + U300_SYSCON_RSR, 1, + syscon_vbase + U300_SYSCON_CESR, 1, + U300_SYSCON_SBCER_UART_CLK_EN); + /* Same clock is used for APB and outward bus */ + clk_register_clkdev(clk, NULL, "uart0"); + clk_register_clkdev(clk, "apb_pclk", "uart0"); + clk = syscon_clk_register(NULL, "gpio_clk", "slow_clk", 0, false, + syscon_vbase + U300_SYSCON_RSR, 4, + syscon_vbase + U300_SYSCON_CESR, 4, + U300_SYSCON_SBCER_GPIO_CLK_EN); + clk_register_clkdev(clk, NULL, "u300-gpio"); + clk = syscon_clk_register(NULL, "keypad_clk", "slow_clk", 0, false, + syscon_vbase + U300_SYSCON_RSR, 5, + syscon_vbase + U300_SYSCON_CESR, 6, + U300_SYSCON_SBCER_KEYPAD_CLK_EN); + clk_register_clkdev(clk, NULL, "coh901461-keypad"); + clk = syscon_clk_register(NULL, "rtc_clk", "slow_clk", 0, true, + syscon_vbase + U300_SYSCON_RSR, 6, + /* No clock enable register bit */ + NULL, 0, 0xFFFFU); + clk_register_clkdev(clk, NULL, "rtc-coh901331"); + clk = syscon_clk_register(NULL, "app_tmr_clk", "slow_clk", 0, false, + syscon_vbase + U300_SYSCON_RSR, 7, + syscon_vbase + U300_SYSCON_CESR, 7, + U300_SYSCON_SBCER_APP_TMR_CLK_EN); + clk_register_clkdev(clk, NULL, "apptimer"); + clk = syscon_clk_register(NULL, "acc_tmr_clk", "slow_clk", 0, false, + syscon_vbase + U300_SYSCON_RSR, 8, + syscon_vbase + U300_SYSCON_CESR, 8, + U300_SYSCON_SBCER_ACC_TMR_CLK_EN); + clk_register_clkdev(clk, NULL, "timer"); + + /* Then this special MMC/SD clock */ + clk = mclk_clk_register(NULL, "mmc_clk", "mmc_p_clk", false); + clk_register_clkdev(clk, NULL, "mmci"); +} diff --git a/include/linux/platform_data/clk-u300.h b/include/linux/platform_data/clk-u300.h new file mode 100644 index 000000000000..8429e73911a1 --- /dev/null +++ b/include/linux/platform_data/clk-u300.h @@ -0,0 +1 @@ +void __init u300_clk_init(void __iomem *base); -- cgit v1.2.3-59-g8ed1b From d8adde17e5f858427504725218c56aef90e90fc7 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 11 Jul 2012 14:01:52 -0700 Subject: memory hotplug: fix invalid memory access caused by stale kswapd pointer kswapd_stop() is called to destroy the kswapd work thread when all memory of a NUMA node has been offlined. But kswapd_stop() only terminates the work thread without resetting NODE_DATA(nid)->kswapd to NULL. The stale pointer will prevent kswapd_run() from creating a new work thread when adding memory to the memory-less NUMA node again. Eventually the stale pointer may cause invalid memory access. An example stack dump as below. It's reproduced with 2.6.32, but latest kernel has the same issue. BUG: unable to handle kernel NULL pointer dereference at (null) IP: [] exit_creds+0x12/0x78 PGD 0 Oops: 0000 [#1] SMP last sysfs file: /sys/devices/system/memory/memory391/state CPU 11 Modules linked in: cpufreq_conservative cpufreq_userspace cpufreq_powersave acpi_cpufreq microcode fuse loop dm_mod tpm_tis rtc_cmos i2c_i801 rtc_core tpm serio_raw pcspkr sg tpm_bios igb i2c_core iTCO_wdt rtc_lib mptctl iTCO_vendor_support button dca bnx2 usbhid hid uhci_hcd ehci_hcd usbcore sd_mod crc_t10dif edd ext3 mbcache jbd fan ide_pci_generic ide_core ata_generic ata_piix libata thermal processor thermal_sys hwmon mptsas mptscsih mptbase scsi_transport_sas scsi_mod Pid: 7949, comm: sh Not tainted 2.6.32.12-qiuxishi-5-default #92 Tecal RH2285 RIP: 0010:exit_creds+0x12/0x78 RSP: 0018:ffff8806044f1d78 EFLAGS: 00010202 RAX: 0000000000000000 RBX: ffff880604f22140 RCX: 0000000000019502 RDX: 0000000000000000 RSI: 0000000000000202 RDI: 0000000000000000 RBP: ffff880604f22150 R08: 0000000000000000 R09: ffffffff81a4dc10 R10: 00000000000032a0 R11: ffff880006202500 R12: 0000000000000000 R13: 0000000000c40000 R14: 0000000000008000 R15: 0000000000000001 FS: 00007fbc03d066f0(0000) GS:ffff8800282e0000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000000 CR3: 000000060f029000 CR4: 00000000000006e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process sh (pid: 7949, threadinfo ffff8806044f0000, task ffff880603d7c600) Stack: ffff880604f22140 ffffffff8103aac5 ffff880604f22140 ffffffff8104d21e ffff880006202500 0000000000008000 0000000000c38000 ffffffff810bd5b1 0000000000000000 ffff880603d7c600 00000000ffffdd29 0000000000000003 Call Trace: __put_task_struct+0x5d/0x97 kthread_stop+0x50/0x58 offline_pages+0x324/0x3da memory_block_change_state+0x179/0x1db store_mem_state+0x9e/0xbb sysfs_write_file+0xd0/0x107 vfs_write+0xad/0x169 sys_write+0x45/0x6e system_call_fastpath+0x16/0x1b Code: ff 4d 00 0f 94 c0 84 c0 74 08 48 89 ef e8 1f fd ff ff 5b 5d 31 c0 41 5c c3 53 48 8b 87 20 06 00 00 48 89 fb 48 8b bf 18 06 00 00 <8b> 00 48 c7 83 18 06 00 00 00 00 00 00 f0 ff 0f 0f 94 c0 84 c0 RIP exit_creds+0x12/0x78 RSP CR2: 0000000000000000 [akpm@linux-foundation.org: add pglist_data.kswapd locking comments] Signed-off-by: Xishi Qiu Signed-off-by: Jiang Liu Acked-by: KAMEZAWA Hiroyuki Acked-by: KOSAKI Motohiro Acked-by: Mel Gorman Acked-by: David Rientjes Reviewed-by: Minchan Kim Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 2 +- mm/vmscan.c | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 2427706f78b4..68c569fcbb66 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -694,7 +694,7 @@ typedef struct pglist_data { range, including holes */ int node_id; wait_queue_head_t kswapd_wait; - struct task_struct *kswapd; + struct task_struct *kswapd; /* Protected by lock_memory_hotplug() */ int kswapd_max_order; enum zone_type classzone_idx; } pg_data_t; diff --git a/mm/vmscan.c b/mm/vmscan.c index eeb3bc9d1d36..661576324c7f 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2955,14 +2955,17 @@ int kswapd_run(int nid) } /* - * Called by memory hotplug when all memory in a node is offlined. + * Called by memory hotplug when all memory in a node is offlined. Caller must + * hold lock_memory_hotplug(). */ void kswapd_stop(int nid) { struct task_struct *kswapd = NODE_DATA(nid)->kswapd; - if (kswapd) + if (kswapd) { kthread_stop(kswapd); + NODE_DATA(nid)->kswapd = NULL; + } } static int __init kswapd_init(void) -- cgit v1.2.3-59-g8ed1b From 99ab7b19440a72ebdf225f99b20f8ef40decee86 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Wed, 11 Jul 2012 14:02:53 -0700 Subject: mm: sparse: fix usemap allocation above node descriptor section After commit f5bf18fa22f8 ("bootmem/sparsemem: remove limit constraint in alloc_bootmem_section"), usemap allocations may easily be placed outside the optimal section that holds the node descriptor, even if there is space available in that section. This results in unnecessary hotplug dependencies that need to have the node unplugged before the section holding the usemap. The reason is that the bootmem allocator doesn't guarantee a linear search starting from the passed allocation goal but may start out at a much higher address absent an upper limit. Fix this by trying the allocation with the limit at the section end, then retry without if that fails. This keeps the fix from f5bf18fa22f8 of not panicking if the allocation does not fit in the section, but still makes sure to try to stay within the section at first. Signed-off-by: Yinghai Lu Signed-off-by: Johannes Weiner Cc: [3.3.x, 3.4.x] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bootmem.h | 5 +++++ mm/bootmem.c | 2 +- mm/nobootmem.c | 2 +- mm/sparse.c | 18 +++++++++++++----- 4 files changed, 20 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 324fe08ea3b1..6d6795d46a75 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -91,6 +91,11 @@ extern void *__alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal); +void *___alloc_bootmem_node_nopanic(pg_data_t *pgdat, + unsigned long size, + unsigned long align, + unsigned long goal, + unsigned long limit); extern void *__alloc_bootmem_low(unsigned long size, unsigned long align, unsigned long goal); diff --git a/mm/bootmem.c b/mm/bootmem.c index ec4fcb7a56c8..73096630cb35 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -698,7 +698,7 @@ void * __init __alloc_bootmem(unsigned long size, unsigned long align, return ___alloc_bootmem(size, align, goal, limit); } -static void * __init ___alloc_bootmem_node_nopanic(pg_data_t *pgdat, +void * __init ___alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal, unsigned long limit) { diff --git a/mm/nobootmem.c b/mm/nobootmem.c index d23415c001bc..0900b3910cda 100644 --- a/mm/nobootmem.c +++ b/mm/nobootmem.c @@ -274,7 +274,7 @@ void * __init __alloc_bootmem(unsigned long size, unsigned long align, return ___alloc_bootmem(size, align, goal, limit); } -static void * __init ___alloc_bootmem_node_nopanic(pg_data_t *pgdat, +void * __init ___alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal, diff --git a/mm/sparse.c b/mm/sparse.c index e861397016a9..c7bb952400c8 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -275,8 +275,9 @@ static unsigned long * __init sparse_early_usemaps_alloc_pgdat_section(struct pglist_data *pgdat, unsigned long size) { - pg_data_t *host_pgdat; - unsigned long goal; + unsigned long goal, limit; + unsigned long *p; + int nid; /* * A page may contain usemaps for other sections preventing the * page being freed and making a section unremovable while @@ -288,9 +289,16 @@ sparse_early_usemaps_alloc_pgdat_section(struct pglist_data *pgdat, * this problem. */ goal = __pa(pgdat) & (PAGE_SECTION_MASK << PAGE_SHIFT); - host_pgdat = NODE_DATA(early_pfn_to_nid(goal >> PAGE_SHIFT)); - return __alloc_bootmem_node_nopanic(host_pgdat, size, - SMP_CACHE_BYTES, goal); + limit = goal + (1UL << PA_SECTION_SHIFT); + nid = early_pfn_to_nid(goal >> PAGE_SHIFT); +again: + p = ___alloc_bootmem_node_nopanic(NODE_DATA(nid), size, + SMP_CACHE_BYTES, goal, limit); + if (!p && limit) { + limit = 0; + goto again; + } + return p; } static void __init check_usemap_section_nr(int nid, unsigned long *usemap) -- cgit v1.2.3-59-g8ed1b From 29f6738609e40227dabcc63bfb3b84b3726a75bd Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Wed, 11 Jul 2012 14:02:56 -0700 Subject: memblock: free allocated memblock_reserved_regions later memblock_free_reserved_regions() calls memblock_free(), but memblock_free() would double reserved.regions too, so we could free the old range for reserved.regions. Also tj said there is another bug which could be related to this. | I don't think we're saving any noticeable | amount by doing this "free - give it to page allocator - reserve | again" dancing. We should just allocate regions aligned to page | boundaries and free them later when memblock is no longer in use. in that case, when DEBUG_PAGEALLOC, will get panic: memblock_free: [0x0000102febc080-0x0000102febf080] memblock_free_reserved_regions+0x37/0x39 BUG: unable to handle kernel paging request at ffff88102febd948 IP: [] __next_free_mem_range+0x9b/0x155 PGD 4826063 PUD cf67a067 PMD cf7fa067 PTE 800000102febd160 Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC CPU 0 Pid: 0, comm: swapper Not tainted 3.5.0-rc2-next-20120614-sasha #447 RIP: 0010:[] [] __next_free_mem_range+0x9b/0x155 See the discussion at https://lkml.org/lkml/2012/6/13/469 So try to allocate with PAGE_SIZE alignment and free it later. Reported-by: Sasha Levin Acked-by: Tejun Heo Cc: Benjamin Herrenschmidt Signed-off-by: Yinghai Lu Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memblock.h | 4 +--- mm/memblock.c | 51 ++++++++++++++++++++++-------------------------- mm/nobootmem.c | 38 ++++++++++++++++++++++-------------- 3 files changed, 47 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/include/linux/memblock.h b/include/linux/memblock.h index a6bb10235148..19dc455b4f3d 100644 --- a/include/linux/memblock.h +++ b/include/linux/memblock.h @@ -50,9 +50,7 @@ phys_addr_t memblock_find_in_range_node(phys_addr_t start, phys_addr_t end, phys_addr_t size, phys_addr_t align, int nid); phys_addr_t memblock_find_in_range(phys_addr_t start, phys_addr_t end, phys_addr_t size, phys_addr_t align); -int memblock_free_reserved_regions(void); -int memblock_reserve_reserved_regions(void); - +phys_addr_t get_allocated_memblock_reserved_regions_info(phys_addr_t *addr); void memblock_allow_resize(void); int memblock_add_node(phys_addr_t base, phys_addr_t size, int nid); int memblock_add(phys_addr_t base, phys_addr_t size); diff --git a/mm/memblock.c b/mm/memblock.c index d4382095f8bd..5cc6731b00cc 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -143,30 +143,6 @@ phys_addr_t __init_memblock memblock_find_in_range(phys_addr_t start, MAX_NUMNODES); } -/* - * Free memblock.reserved.regions - */ -int __init_memblock memblock_free_reserved_regions(void) -{ - if (memblock.reserved.regions == memblock_reserved_init_regions) - return 0; - - return memblock_free(__pa(memblock.reserved.regions), - sizeof(struct memblock_region) * memblock.reserved.max); -} - -/* - * Reserve memblock.reserved.regions - */ -int __init_memblock memblock_reserve_reserved_regions(void) -{ - if (memblock.reserved.regions == memblock_reserved_init_regions) - return 0; - - return memblock_reserve(__pa(memblock.reserved.regions), - sizeof(struct memblock_region) * memblock.reserved.max); -} - static void __init_memblock memblock_remove_region(struct memblock_type *type, unsigned long r) { type->total_size -= type->regions[r].size; @@ -184,6 +160,18 @@ static void __init_memblock memblock_remove_region(struct memblock_type *type, u } } +phys_addr_t __init_memblock get_allocated_memblock_reserved_regions_info( + phys_addr_t *addr) +{ + if (memblock.reserved.regions == memblock_reserved_init_regions) + return 0; + + *addr = __pa(memblock.reserved.regions); + + return PAGE_ALIGN(sizeof(struct memblock_region) * + memblock.reserved.max); +} + /** * memblock_double_array - double the size of the memblock regions array * @type: memblock type of the regions array being doubled @@ -204,6 +192,7 @@ static int __init_memblock memblock_double_array(struct memblock_type *type, phys_addr_t new_area_size) { struct memblock_region *new_array, *old_array; + phys_addr_t old_alloc_size, new_alloc_size; phys_addr_t old_size, new_size, addr; int use_slab = slab_is_available(); int *in_slab; @@ -217,6 +206,12 @@ static int __init_memblock memblock_double_array(struct memblock_type *type, /* Calculate new doubled size */ old_size = type->max * sizeof(struct memblock_region); new_size = old_size << 1; + /* + * We need to allocated new one align to PAGE_SIZE, + * so we can free them completely later. + */ + old_alloc_size = PAGE_ALIGN(old_size); + new_alloc_size = PAGE_ALIGN(new_size); /* Retrieve the slab flag */ if (type == &memblock.memory) @@ -245,11 +240,11 @@ static int __init_memblock memblock_double_array(struct memblock_type *type, addr = memblock_find_in_range(new_area_start + new_area_size, memblock.current_limit, - new_size, sizeof(phys_addr_t)); + new_alloc_size, PAGE_SIZE); if (!addr && new_area_size) addr = memblock_find_in_range(0, min(new_area_start, memblock.current_limit), - new_size, sizeof(phys_addr_t)); + new_alloc_size, PAGE_SIZE); new_array = addr ? __va(addr) : 0; } @@ -279,13 +274,13 @@ static int __init_memblock memblock_double_array(struct memblock_type *type, kfree(old_array); else if (old_array != memblock_memory_init_regions && old_array != memblock_reserved_init_regions) - memblock_free(__pa(old_array), old_size); + memblock_free(__pa(old_array), old_alloc_size); /* Reserve the new array if that comes from the memblock. * Otherwise, we needn't do it */ if (!use_slab) - BUG_ON(memblock_reserve(addr, new_size)); + BUG_ON(memblock_reserve(addr, new_alloc_size)); /* Update slab flag */ *in_slab = use_slab; diff --git a/mm/nobootmem.c b/mm/nobootmem.c index 0900b3910cda..405573010f99 100644 --- a/mm/nobootmem.c +++ b/mm/nobootmem.c @@ -105,27 +105,35 @@ static void __init __free_pages_memory(unsigned long start, unsigned long end) __free_pages_bootmem(pfn_to_page(i), 0); } +static unsigned long __init __free_memory_core(phys_addr_t start, + phys_addr_t end) +{ + unsigned long start_pfn = PFN_UP(start); + unsigned long end_pfn = min_t(unsigned long, + PFN_DOWN(end), max_low_pfn); + + if (start_pfn > end_pfn) + return 0; + + __free_pages_memory(start_pfn, end_pfn); + + return end_pfn - start_pfn; +} + unsigned long __init free_low_memory_core_early(int nodeid) { unsigned long count = 0; - phys_addr_t start, end; + phys_addr_t start, end, size; u64 i; - /* free reserved array temporarily so that it's treated as free area */ - memblock_free_reserved_regions(); - - for_each_free_mem_range(i, MAX_NUMNODES, &start, &end, NULL) { - unsigned long start_pfn = PFN_UP(start); - unsigned long end_pfn = min_t(unsigned long, - PFN_DOWN(end), max_low_pfn); - if (start_pfn < end_pfn) { - __free_pages_memory(start_pfn, end_pfn); - count += end_pfn - start_pfn; - } - } + for_each_free_mem_range(i, MAX_NUMNODES, &start, &end, NULL) + count += __free_memory_core(start, end); + + /* free range that is used for reserved array if we allocate it */ + size = get_allocated_memblock_reserved_regions_info(&start); + if (size) + count += __free_memory_core(start, start + size); - /* put region array back? */ - memblock_reserve_reserved_regions(); return count; } -- cgit v1.2.3-59-g8ed1b From a613163dff04cbfcb7d66b06ef4a5f65498ee59b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 11 Jun 2012 17:33:12 +0200 Subject: ARM: integrator: convert to common clock This converts the Integrator platform to use common clock and the ICST driver. Since from this point not all ARM reference platforms use the clock, we define CONFIG_PLAT_VERSATILE_CLOCK and select it for all platforms except the Integrator. Open issue: I could not use the .init_early() field of the machine descriptor to initialize the clocks, but had to move them to .init_irq(), so presumably .init_early() is so early that common clock is not up, and .init_machine() is too late since it's needed for the clockevent/clocksource initialization. Any suggestions on how to solve this is very welcome. Cc: Russell King Signed-off-by: Linus Walleij [mturquette@linaro.org: use 'select' instead of versatile Kconfig] Signed-off-by: Mike Turquette --- arch/arm/Kconfig | 7 +- arch/arm/mach-integrator/core.c | 45 ---------- arch/arm/mach-integrator/include/mach/clkdev.h | 26 ------ arch/arm/mach-integrator/integrator_ap.c | 8 +- arch/arm/mach-integrator/integrator_cp.c | 63 +------------- arch/arm/plat-versatile/Kconfig | 3 + arch/arm/plat-versatile/Makefile | 2 +- drivers/clk/versatile/Makefile | 1 + drivers/clk/versatile/clk-integrator.c | 111 +++++++++++++++++++++++++ include/linux/platform_data/clk-integrator.h | 1 + 10 files changed, 131 insertions(+), 136 deletions(-) delete mode 100644 arch/arm/mach-integrator/include/mach/clkdev.h create mode 100644 drivers/clk/versatile/clk-integrator.c create mode 100644 include/linux/platform_data/clk-integrator.h (limited to 'include/linux') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index c59853738967..6f8cf405d3ec 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -254,8 +254,8 @@ config ARCH_INTEGRATOR bool "ARM Ltd. Integrator family" select ARM_AMBA select ARCH_HAS_CPUFREQ - select CLKDEV_LOOKUP - select HAVE_MACH_CLKDEV + select COMMON_CLK + select CLK_VERSATILE select HAVE_TCM select ICST select GENERIC_CLOCKEVENTS @@ -277,6 +277,7 @@ config ARCH_REALVIEW select GENERIC_CLOCKEVENTS select ARCH_WANT_OPTIONAL_GPIOLIB select PLAT_VERSATILE + select PLAT_VERSATILE_CLOCK select PLAT_VERSATILE_CLCD select ARM_TIMER_SP804 select GPIO_PL061 if GPIOLIB @@ -295,6 +296,7 @@ config ARCH_VERSATILE select ARCH_WANT_OPTIONAL_GPIOLIB select NEED_MACH_IO_H if PCI select PLAT_VERSATILE + select PLAT_VERSATILE_CLOCK select PLAT_VERSATILE_CLCD select PLAT_VERSATILE_FPGA_IRQ select ARM_TIMER_SP804 @@ -314,6 +316,7 @@ config ARCH_VEXPRESS select ICST select NO_IOPORT select PLAT_VERSATILE + select PLAT_VERSATILE_CLOCK select PLAT_VERSATILE_CLCD help This enables support for the ARM Ltd Versatile Express boards. diff --git a/arch/arm/mach-integrator/core.c b/arch/arm/mach-integrator/core.c index 2a20bba246fb..ebf680bebdf2 100644 --- a/arch/arm/mach-integrator/core.c +++ b/arch/arm/mach-integrator/core.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -61,50 +60,6 @@ static struct amba_device *amba_devs[] __initdata = { &kmi1_device, }; -/* - * These are fixed clocks. - */ -static struct clk clk24mhz = { - .rate = 24000000, -}; - -static struct clk uartclk = { - .rate = 14745600, -}; - -static struct clk dummy_apb_pclk; - -static struct clk_lookup lookups[] = { - { /* Bus clock */ - .con_id = "apb_pclk", - .clk = &dummy_apb_pclk, - }, { - /* Integrator/AP timer frequency */ - .dev_id = "ap_timer", - .clk = &clk24mhz, - }, { /* UART0 */ - .dev_id = "uart0", - .clk = &uartclk, - }, { /* UART1 */ - .dev_id = "uart1", - .clk = &uartclk, - }, { /* KMI0 */ - .dev_id = "kmi0", - .clk = &clk24mhz, - }, { /* KMI1 */ - .dev_id = "kmi1", - .clk = &clk24mhz, - }, { /* MMCI - IntegratorCP */ - .dev_id = "mmci", - .clk = &uartclk, - } -}; - -void __init integrator_init_early(void) -{ - clkdev_add_table(lookups, ARRAY_SIZE(lookups)); -} - static int __init integrator_init(void) { int i; diff --git a/arch/arm/mach-integrator/include/mach/clkdev.h b/arch/arm/mach-integrator/include/mach/clkdev.h deleted file mode 100644 index bfe07679faec..000000000000 --- a/arch/arm/mach-integrator/include/mach/clkdev.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef __ASM_MACH_CLKDEV_H -#define __ASM_MACH_CLKDEV_H - -#include -#include - -struct clk { - unsigned long rate; - const struct clk_ops *ops; - struct module *owner; - const struct icst_params *params; - void __iomem *vcoreg; - void *data; -}; - -static inline int __clk_get(struct clk *clk) -{ - return try_module_get(clk->owner); -} - -static inline void __clk_put(struct clk *clk) -{ - module_put(clk->owner); -} - -#endif diff --git a/arch/arm/mach-integrator/integrator_ap.c b/arch/arm/mach-integrator/integrator_ap.c index c857501c5783..7b1055c8e0b9 100644 --- a/arch/arm/mach-integrator/integrator_ap.c +++ b/arch/arm/mach-integrator/integrator_ap.c @@ -33,6 +33,7 @@ #include #include #include +#include #include