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') 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 From 0bc77f3f06fcf2ca7b7fad782d70926cd4d235f1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 May 2012 09:55:57 -0300 Subject: [media] mt9t001: Implement V4L2_CID_PIXEL_RATE control The pixel rate control is required by the OMAP3 ISP driver and should be implemented by all media controller-compatible sensor drivers. Signed-off-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mt9t001.c | 13 +++++++++++-- include/media/mt9t001.h | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/media/video/mt9t001.c b/drivers/media/video/mt9t001.c index 49ca3cbfc6f1..6d343adf891d 100644 --- a/drivers/media/video/mt9t001.c +++ b/drivers/media/video/mt9t001.c @@ -691,7 +691,7 @@ static int mt9t001_video_probe(struct i2c_client *client) return ret; /* Configure the pixel clock polarity */ - if (pdata && pdata->clk_pol) { + if (pdata->clk_pol) { ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK, MT9T001_PIXEL_CLOCK_INVERT); if (ret < 0) @@ -715,10 +715,16 @@ static int mt9t001_video_probe(struct i2c_client *client) static int mt9t001_probe(struct i2c_client *client, const struct i2c_device_id *did) { + struct mt9t001_platform_data *pdata = client->dev.platform_data; struct mt9t001 *mt9t001; unsigned int i; int ret; + if (pdata == NULL) { + dev_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) { dev_warn(&client->adapter->dev, @@ -735,7 +741,7 @@ static int mt9t001_probe(struct i2c_client *client, return -ENOMEM; v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) + - ARRAY_SIZE(mt9t001_gains) + 2); + ARRAY_SIZE(mt9t001_gains) + 3); v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN, @@ -743,6 +749,9 @@ static int mt9t001_probe(struct i2c_client *client, MT9T001_SHUTTER_WIDTH_DEF); v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1); + v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, + V4L2_CID_PIXEL_RATE, pdata->ext_clk, pdata->ext_clk, + 1, pdata->ext_clk); for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i) v4l2_ctrl_new_custom(&mt9t001->ctrls, &mt9t001_ctrls[i], NULL); diff --git a/include/media/mt9t001.h b/include/media/mt9t001.h index e839a78bb9c5..03fd63edd133 100644 --- a/include/media/mt9t001.h +++ b/include/media/mt9t001.h @@ -3,6 +3,7 @@ struct mt9t001_platform_data { unsigned int clk_pol:1; + unsigned int ext_clk; }; #endif -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 From 23ba4fd0a42a46551041e379712e808ad496ba45 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 24 May 2012 15:03:10 -0700 Subject: drm/i915: wait render timeout ioctl This helps implement GL_ARB_sync but stops short of allowing full blown sync objects. Finally we can use the new timed seqno waiting function to allow userspace to wait on a buffer object with a timeout. This implements that interface. The IOCTL will take as input a buffer object handle, and a timeout in nanoseconds (flags is currently optional but will likely be used for permutations of flush operations). Users may specify 0 nanoseconds to instantly check. The wait ioctl with a timeout of 0 reimplements the busy ioctl. With any non-zero timeout parameter the wait ioctl will wait for the given number of nanoseconds on an object becoming unbusy. Since the wait itself does so holding struct_mutex the object may become re-busied before this completes. A similar but shorter race condition exists in the busy ioctl. v2: ETIME/ERESTARTSYS instead of changing to EBUSY, and EGAIN (Chris) Flush the object from the gpu write domain (Chris + Daniel) Fix leaked refcount in good case (Chris) Naturally align ioctl struct (Chris) v3: Drop lock after getting seqno to avoid ugly dance (Chris) v4: check for 0 timeout after olr check to allow polling (Chris) v5: Updated the comment. (Chris) v6: Return -ETIME instead of -EBUSY when timeout_ns is 0 (Daniel) Fix the commit message comment to be less ugly (Ben) Add a warning to check the return timespec (Ben) v7: Use DRM_AUTH for the ioctl. (Eugeni) Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 2 + drivers/gpu/drm/i915/i915_gem.c | 86 +++++++++++++++++++++++++++++++++++++++++ include/drm/i915_drm.h | 10 +++++ 4 files changed, 99 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index f94792626b94..262a74d1f852 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1803,6 +1803,7 @@ struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_OVERLAY_ATTRS, intel_overlay_attrs, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I915_SET_SPRITE_COLORKEY, intel_sprite_set_colorkey, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I915_GET_SPRITE_COLORKEY, intel_sprite_get_colorkey, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(I915_GEM_WAIT, i915_gem_wait_ioctl, DRM_AUTH|DRM_UNLOCKED), }; int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 11c7a6a330c1..8ac10e8c0cdb 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1229,6 +1229,8 @@ int i915_gem_get_tiling(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int i915_gem_wait_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); void i915_gem_load(struct drm_device *dev); int i915_gem_init_object(struct drm_gem_object *obj); int __must_check i915_gem_flush_ring(struct intel_ring_buffer *ring, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c8a5b04bc8d5..a0d740fac240 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2000,6 +2000,92 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj) return 0; } +/** + * i915_gem_wait_ioctl - implements DRM_IOCTL_I915_GEM_WAIT + * @DRM_IOCTL_ARGS: standard ioctl arguments + * + * Returns 0 if successful, else an error is returned with the remaining time in + * the timeout parameter. + * -ETIME: object is still busy after timeout + * -ERESTARTSYS: signal interrupted the wait + * -ENONENT: object doesn't exist + * Also possible, but rare: + * -EAGAIN: GPU wedged + * -ENOMEM: damn + * -ENODEV: Internal IRQ fail + * -E?: The add request failed + * + * The wait ioctl with a timeout of 0 reimplements the busy ioctl. With any + * non-zero timeout parameter the wait ioctl will wait for the given number of + * nanoseconds on an object becoming unbusy. Since the wait itself does so + * without holding struct_mutex the object may become re-busied before this + * function completes. A similar but shorter * race condition exists in the busy + * ioctl + */ +int +i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) +{ + struct drm_i915_gem_wait *args = data; + struct drm_i915_gem_object *obj; + struct intel_ring_buffer *ring = NULL; + struct timespec timeout; + u32 seqno = 0; + int ret = 0; + + timeout = ns_to_timespec(args->timeout_ns); + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->bo_handle)); + if (&obj->base == NULL) { + mutex_unlock(&dev->struct_mutex); + return -ENOENT; + } + + /* Need to make sure the object is flushed first. This non-obvious + * flush is required to enforce that (active && !olr) == no wait + * necessary. + */ + ret = i915_gem_object_flush_gpu_write_domain(obj); + if (ret) + goto out; + + if (obj->active) { + seqno = obj->last_rendering_seqno; + ring = obj->ring; + } + + if (seqno == 0) + goto out; + + ret = i915_gem_check_olr(ring, seqno); + if (ret) + goto out; + + /* Do this after OLR check to make sure we make forward progress polling + * on this IOCTL with a 0 timeout (like busy ioctl) + */ + if (!args->timeout_ns) { + ret = -ETIME; + goto out; + } + + drm_gem_object_unreference(&obj->base); + mutex_unlock(&dev->struct_mutex); + + ret = __wait_seqno(ring, seqno, true, &timeout); + WARN_ON(!timespec_valid(&timeout)); + args->timeout_ns = timespec_to_ns(&timeout); + return ret; + +out: + drm_gem_object_unreference(&obj->base); + mutex_unlock(&dev->struct_mutex); + return ret; +} + /** * i915_gem_object_sync - sync an object to a ring. * diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index f3f82242bf1d..bab174334da5 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -200,6 +200,7 @@ typedef struct _drm_i915_sarea { #define DRM_I915_GEM_EXECBUFFER2 0x29 #define DRM_I915_GET_SPRITE_COLORKEY 0x2a #define DRM_I915_SET_SPRITE_COLORKEY 0x2b +#define DRM_I915_GEM_WAIT 0x2c #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -243,6 +244,7 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_OVERLAY_ATTRS DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs) #define DRM_IOCTL_I915_SET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) #define DRM_IOCTL_I915_GET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) +#define DRM_IOCTL_I915_GEM_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_WAIT, struct drm_i915_gem_wait) /* Allow drivers to submit batchbuffers directly to hardware, relying * on the security mechanisms provided by hardware. @@ -886,4 +888,12 @@ struct drm_intel_sprite_colorkey { __u32 flags; }; +struct drm_i915_gem_wait { + /** Handle of BO we shall wait on */ + __u32 bo_handle; + __u32 flags; + /** Number of nanoseconds to wait, Returns time remaining. */ + __u64 timeout_ns; +}; + #endif /* _I915_DRM_H_ */ -- cgit v1.2.3 From 188726ecb66f022e92ec110ca85c62a937184636 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 24 May 2012 19:28:17 +0200 Subject: firewire: core: make address handler length 64 bits The type of the length field of the fw_address_handler structure was size_t, which restricted it to 32 bits on 32-bit architectures. While making it u32 would match the userspace API, all calculations on this field use 64 bits anyway, and the ability to use 4 GB or larger address ranges is useful in the kernel. Signed-off-by: Clemens Ladisch Signed-off-by: Stefan Richter --- include/linux/firewire.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 584826ba2eb7..d77f60c6d1ed 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -307,7 +307,7 @@ struct fw_transaction { struct fw_address_handler { u64 offset; - size_t length; + u64 length; fw_address_callback_t address_callback; void *callback_data; struct list_head link; -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 From ec3ab083a7a004282ee374bdaeb0aa603521b8eb Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 9 May 2012 10:09:56 -0500 Subject: slub: Get rid of the node field The node field is always page_to_nid(c->page). So its rather easy to replace. Note that there maybe slightly more overhead in various hot paths due to the need to shift the bits from page->flags. However, that is mostly compensated for by a smaller footprint of the kmem_cache_cpu structure (this patch reduces that to 3 words per cache) which allows better caching. Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slub_def.h | 1 - mm/slub.c | 35 ++++++++++++++++------------------- 2 files changed, 16 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index c2f8c8bc56ed..ebdcf4ba42ee 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -48,7 +48,6 @@ struct kmem_cache_cpu { unsigned long tid; /* Globally unique transaction id */ struct page *page; /* The slab from which we are allocating */ struct page *partial; /* Partially allocated frozen slabs */ - int node; /* The node of the page (or -1 for debug) */ #ifdef CONFIG_SLUB_STATS unsigned stat[NR_SLUB_STAT_ITEMS]; #endif diff --git a/mm/slub.c b/mm/slub.c index b29246bc7392..aed879276410 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1561,7 +1561,6 @@ static void *get_partial_node(struct kmem_cache *s, if (!object) { c->page = page; - c->node = page_to_nid(page); stat(s, ALLOC_FROM_PARTIAL); object = t; available = page->objects - page->inuse; @@ -2057,7 +2056,7 @@ static void flush_all(struct kmem_cache *s) static inline int node_match(struct kmem_cache_cpu *c, int node) { #ifdef CONFIG_NUMA - if (node != NUMA_NO_NODE && c->node != node) + if (node != NUMA_NO_NODE && page_to_nid(c->page) != node) return 0; #endif return 1; @@ -2152,7 +2151,6 @@ static inline void *new_slab_objects(struct kmem_cache *s, gfp_t flags, page->freelist = NULL; stat(s, ALLOC_SLAB); - c->node = page_to_nid(page); c->page = page; *pc = c; } else @@ -2269,7 +2267,6 @@ new_slab: if (c->partial) { c->page = c->partial; c->partial = c->page->next; - c->node = page_to_nid(c->page); stat(s, CPU_PARTIAL_ALLOC); c->freelist = NULL; goto redo; @@ -2294,7 +2291,6 @@ new_slab: c->freelist = get_freepointer(s, freelist); deactivate_slab(s, c); - c->node = NUMA_NO_NODE; local_irq_restore(flags); return freelist; } @@ -4507,30 +4503,31 @@ static ssize_t show_slab_objects(struct kmem_cache *s, for_each_possible_cpu(cpu) { struct kmem_cache_cpu *c = per_cpu_ptr(s->cpu_slab, cpu); - int node = ACCESS_ONCE(c->node); + int node; struct page *page; - if (node < 0) - continue; page = ACCESS_ONCE(c->page); - if (page) { - if (flags & SO_TOTAL) - x = page->objects; - else if (flags & SO_OBJECTS) - x = page->inuse; - else - x = 1; + if (!page) + continue; - total += x; - nodes[node] += x; - } - page = c->partial; + node = page_to_nid(page); + if (flags & SO_TOTAL) + x = page->objects; + else if (flags & SO_OBJECTS) + x = page->inuse; + else + x = 1; + + total += x; + nodes[node] += x; + page = ACCESS_ONCE(c->partial); if (page) { x = page->pobjects; total += x; nodes[node] += x; } + per_cpu[node]++; } } -- cgit v1.2.3 From e5e372da9a469dfe3ece40277090a7056c566838 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 22 May 2012 11:41:43 -0500 Subject: libceph: eliminate connection state "DEAD" The ceph connection state "DEAD" is never set and is therefore not needed. Eliminate it. Signed-off-by: Alex Elder Reviewed-by: Yehuda Sadeh --- include/linux/ceph/messenger.h | 1 - net/ceph/messenger.c | 6 ------ 2 files changed, 7 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 2521a95fa6d9..aa506cadea67 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -119,7 +119,6 @@ struct ceph_msg_pos { #define CLOSED 10 /* we've closed the connection */ #define SOCK_CLOSED 11 /* socket state changed to closed */ #define OPENING 13 /* open connection w/ (possibly new) peer */ -#define DEAD 14 /* dead, about to kfree */ #define BACKOFF 15 /* diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 1a80907282cc..42ca8aab6dcf 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -2087,12 +2087,6 @@ bad_tag: */ static void queue_con(struct ceph_connection *con) { - if (test_bit(DEAD, &con->state)) { - dout("queue_con %p ignoring: DEAD\n", - con); - return; - } - if (!con->ops->get(con)) { dout("queue_con %p ref count 0\n", con); return; -- cgit v1.2.3 From 6384bb8b8e88a9c6bf2ae0d9517c2c0199177c34 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 29 May 2012 21:47:38 -0500 Subject: libceph: kill bad_proto ceph connection op No code sets a bad_proto method in its ceph connection operations vector, so just get rid of it. Signed-off-by: Alex Elder Reviewed-by: Yehuda Sadeh --- include/linux/ceph/messenger.h | 3 --- net/ceph/messenger.c | 5 ----- 2 files changed, 8 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index aa506cadea67..74f6c9bd8074 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -31,9 +31,6 @@ struct ceph_connection_operations { int (*verify_authorizer_reply) (struct ceph_connection *con, int len); int (*invalidate_authorizer)(struct ceph_connection *con); - /* protocol version mismatch */ - void (*bad_proto) (struct ceph_connection *con); - /* there was some error on the socket (disconnect, whatever) */ void (*fault) (struct ceph_connection *con); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 42ca8aab6dcf..07af9948e3f7 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -1356,11 +1356,6 @@ static void fail_protocol(struct ceph_connection *con) { reset_connection(con); set_bit(CLOSED, &con->state); /* in case there's queued work */ - - mutex_unlock(&con->mutex); - if (con->ops->bad_proto) - con->ops->bad_proto(con); - mutex_lock(&con->mutex); } static int process_connect(struct ceph_connection *con) -- cgit v1.2.3 From 15d9882c336db2db73ccf9871ae2398e452f694c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 26 May 2012 23:26:43 -0500 Subject: libceph: embed ceph messenger structure in ceph_client A ceph client has a pointer to a ceph messenger structure in it. There is always exactly one ceph messenger for a ceph client, so there is no need to allocate it separate from the ceph client structure. Switch the ceph_client structure to embed its ceph_messenger structure. Signed-off-by: Alex Elder Reviewed-by: Yehuda Sadeh Reviewed-by: Sage Weil --- fs/ceph/mds_client.c | 2 +- include/linux/ceph/libceph.h | 2 +- include/linux/ceph/messenger.h | 9 +++++---- net/ceph/ceph_common.c | 18 +++++------------- net/ceph/messenger.c | 30 +++++++++--------------------- net/ceph/mon_client.c | 6 +++--- net/ceph/osd_client.c | 4 ++-- 7 files changed, 26 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 200bc87eceb1..ad30261cd4c0 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -394,7 +394,7 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, s->s_seq = 0; mutex_init(&s->s_mutex); - ceph_con_init(mdsc->fsc->client->msgr, &s->s_con); + ceph_con_init(&mdsc->fsc->client->msgr, &s->s_con); s->s_con.private = s; s->s_con.ops = &mds_con_ops; s->s_con.peer_name.type = CEPH_ENTITY_TYPE_MDS; diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h index 92eef7c3d3c5..927361c4b0a8 100644 --- a/include/linux/ceph/libceph.h +++ b/include/linux/ceph/libceph.h @@ -131,7 +131,7 @@ struct ceph_client { u32 supported_features; u32 required_features; - struct ceph_messenger *msgr; /* messenger instance */ + struct ceph_messenger msgr; /* messenger instance */ struct ceph_mon_client monc; struct ceph_osd_client osdc; diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 74f6c9bd8074..3fbd4be804ed 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -211,10 +211,11 @@ extern int ceph_msgr_init(void); extern void ceph_msgr_exit(void); extern void ceph_msgr_flush(void); -extern struct ceph_messenger *ceph_messenger_create( - struct ceph_entity_addr *myaddr, - u32 features, u32 required); -extern void ceph_messenger_destroy(struct ceph_messenger *); +extern void ceph_messenger_init(struct ceph_messenger *msgr, + struct ceph_entity_addr *myaddr, + u32 supported_features, + u32 required_features, + bool nocrc); extern void ceph_con_init(struct ceph_messenger *msgr, struct ceph_connection *con); diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index cc913193d992..2de3ea1bbd64 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -468,19 +468,15 @@ struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private, /* msgr */ if (ceph_test_opt(client, MYIP)) myaddr = &client->options->my_addr; - client->msgr = ceph_messenger_create(myaddr, - client->supported_features, - client->required_features); - if (IS_ERR(client->msgr)) { - err = PTR_ERR(client->msgr); - goto fail; - } - client->msgr->nocrc = ceph_test_opt(client, NOCRC); + ceph_messenger_init(&client->msgr, myaddr, + client->supported_features, + client->required_features, + ceph_test_opt(client, NOCRC)); /* subsystems */ err = ceph_monc_init(&client->monc, client); if (err < 0) - goto fail_msgr; + goto fail; err = ceph_osdc_init(&client->osdc, client); if (err < 0) goto fail_monc; @@ -489,8 +485,6 @@ struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private, fail_monc: ceph_monc_stop(&client->monc); -fail_msgr: - ceph_messenger_destroy(client->msgr); fail: kfree(client); return ERR_PTR(err); @@ -515,8 +509,6 @@ void ceph_destroy_client(struct ceph_client *client) ceph_debugfs_client_cleanup(client); - ceph_messenger_destroy(client->msgr); - ceph_destroy_options(client->options); kfree(client); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 2ca491fc50e2..d8423a3f6698 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -2245,18 +2245,14 @@ out: /* - * create a new messenger instance + * initialize a new messenger instance */ -struct ceph_messenger *ceph_messenger_create(struct ceph_entity_addr *myaddr, - u32 supported_features, - u32 required_features) +void ceph_messenger_init(struct ceph_messenger *msgr, + struct ceph_entity_addr *myaddr, + u32 supported_features, + u32 required_features, + bool nocrc) { - struct ceph_messenger *msgr; - - msgr = kzalloc(sizeof(*msgr), GFP_KERNEL); - if (msgr == NULL) - return ERR_PTR(-ENOMEM); - msgr->supported_features = supported_features; msgr->required_features = required_features; @@ -2269,19 +2265,11 @@ struct ceph_messenger *ceph_messenger_create(struct ceph_entity_addr *myaddr, msgr->inst.addr.type = 0; get_random_bytes(&msgr->inst.addr.nonce, sizeof(msgr->inst.addr.nonce)); encode_my_addr(msgr); + msgr->nocrc = nocrc; - dout("messenger_create %p\n", msgr); - return msgr; -} -EXPORT_SYMBOL(ceph_messenger_create); - -void ceph_messenger_destroy(struct ceph_messenger *msgr) -{ - dout("destroy %p\n", msgr); - kfree(msgr); - dout("destroyed messenger %p\n", msgr); + dout("%s %p\n", __func__, msgr); } -EXPORT_SYMBOL(ceph_messenger_destroy); +EXPORT_SYMBOL(ceph_messenger_init); static void clear_standby(struct ceph_connection *con) { diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index 1845cde26227..704dc95dc620 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -763,7 +763,7 @@ int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl) monc->con = kmalloc(sizeof(*monc->con), GFP_KERNEL); if (!monc->con) goto out_monmap; - ceph_con_init(monc->client->msgr, monc->con); + ceph_con_init(&monc->client->msgr, monc->con); monc->con->private = monc; monc->con->ops = &mon_con_ops; @@ -880,8 +880,8 @@ static void handle_auth_reply(struct ceph_mon_client *monc, } else if (!was_auth && monc->auth->ops->is_authenticated(monc->auth)) { dout("authenticated, starting session\n"); - monc->client->msgr->inst.name.type = CEPH_ENTITY_TYPE_CLIENT; - monc->client->msgr->inst.name.num = + monc->client->msgr.inst.name.type = CEPH_ENTITY_TYPE_CLIENT; + monc->client->msgr.inst.name.num = cpu_to_le64(monc->auth->global_id); __send_subscribe(monc); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index b098e7b591f0..cca4c7f1c780 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -639,7 +639,7 @@ static struct ceph_osd *create_osd(struct ceph_osd_client *osdc) INIT_LIST_HEAD(&osd->o_osd_lru); osd->o_incarnation = 1; - ceph_con_init(osdc->client->msgr, &osd->o_con); + ceph_con_init(&osdc->client->msgr, &osd->o_con); osd->o_con.private = osd; osd->o_con.ops = &osd_con_ops; osd->o_con.peer_name.type = CEPH_ENTITY_TYPE_OSD; @@ -1391,7 +1391,7 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg) epoch, maplen); newmap = osdmap_apply_incremental(&p, next, osdc->osdmap, - osdc->client->msgr); + &osdc->client->msgr); if (IS_ERR(newmap)) { err = PTR_ERR(newmap); goto bad; -- cgit v1.2.3 From 928443cd9644e7cfd46f687dbeffda2d1a357ff9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 22 May 2012 11:41:43 -0500 Subject: libceph: start separating connection flags from state A ceph_connection holds a mixture of connection state (as in "state machine" state) and connection flags in a single "state" field. To make the distinction more clear, define a new "flags" field and use it rather than the "state" field to hold Boolean flag values. Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- include/linux/ceph/messenger.h | 18 ++++++++++----- net/ceph/messenger.c | 50 +++++++++++++++++++++--------------------- 2 files changed, 37 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 3fbd4be804ed..920235e114ad 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -103,20 +103,25 @@ struct ceph_msg_pos { #define MAX_DELAY_INTERVAL (5 * 60 * HZ) /* - * ceph_connection state bit flags + * ceph_connection flag bits */ + #define LOSSYTX 0 /* we can close channel or drop messages on errors */ -#define CONNECTING 1 -#define NEGOTIATING 2 #define KEEPALIVE_PENDING 3 #define WRITE_PENDING 4 /* we have data ready to send */ +#define SOCK_CLOSED 11 /* socket state changed to closed */ +#define BACKOFF 15 + +/* + * ceph_connection states + */ +#define CONNECTING 1 +#define NEGOTIATING 2 #define STANDBY 8 /* no outgoing messages, socket closed. we keep * the ceph_connection around to maintain shared * state with the peer. */ #define CLOSED 10 /* we've closed the connection */ -#define SOCK_CLOSED 11 /* socket state changed to closed */ #define OPENING 13 /* open connection w/ (possibly new) peer */ -#define BACKOFF 15 /* * A single connection with another host. @@ -133,7 +138,8 @@ struct ceph_connection { struct ceph_messenger *msgr; struct socket *sock; - unsigned long state; /* connection state (see flags above) */ + unsigned long flags; + unsigned long state; const char *error_msg; /* error message, if any */ struct ceph_entity_addr peer_addr; /* peer address */ diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index d8423a3f6698..e84e4fd86bb7 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -176,7 +176,7 @@ static void ceph_sock_write_space(struct sock *sk) * buffer. See net/ipv4/tcp_input.c:tcp_check_space() * and net/core/stream.c:sk_stream_write_space(). */ - if (test_bit(WRITE_PENDING, &con->state)) { + if (test_bit(WRITE_PENDING, &con->flags)) { if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) { dout("%s %p queueing write work\n", __func__, con); clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags); @@ -203,7 +203,7 @@ static void ceph_sock_state_change(struct sock *sk) dout("%s TCP_CLOSE\n", __func__); case TCP_CLOSE_WAIT: dout("%s TCP_CLOSE_WAIT\n", __func__); - if (test_and_set_bit(SOCK_CLOSED, &con->state) == 0) { + if (test_and_set_bit(SOCK_CLOSED, &con->flags) == 0) { if (test_bit(CONNECTING, &con->state)) con->error_msg = "connection failed"; else @@ -395,9 +395,9 @@ void ceph_con_close(struct ceph_connection *con) ceph_pr_addr(&con->peer_addr.in_addr)); set_bit(CLOSED, &con->state); /* in case there's queued work */ clear_bit(STANDBY, &con->state); /* avoid connect_seq bump */ - clear_bit(LOSSYTX, &con->state); /* so we retry next connect */ - clear_bit(KEEPALIVE_PENDING, &con->state); - clear_bit(WRITE_PENDING, &con->state); + clear_bit(LOSSYTX, &con->flags); /* so we retry next connect */ + clear_bit(KEEPALIVE_PENDING, &con->flags); + clear_bit(WRITE_PENDING, &con->flags); mutex_lock(&con->mutex); reset_connection(con); con->peer_global_seq = 0; @@ -614,7 +614,7 @@ static void prepare_write_message(struct ceph_connection *con) prepare_write_message_footer(con); } - set_bit(WRITE_PENDING, &con->state); + set_bit(WRITE_PENDING, &con->flags); } /* @@ -635,7 +635,7 @@ static void prepare_write_ack(struct ceph_connection *con) &con->out_temp_ack); con->out_more = 1; /* more will follow.. eventually.. */ - set_bit(WRITE_PENDING, &con->state); + set_bit(WRITE_PENDING, &con->flags); } /* @@ -646,7 +646,7 @@ static void prepare_write_keepalive(struct ceph_connection *con) dout("prepare_write_keepalive %p\n", con); con_out_kvec_reset(con); con_out_kvec_add(con, sizeof (tag_keepalive), &tag_keepalive); - set_bit(WRITE_PENDING, &con->state); + set_bit(WRITE_PENDING, &con->flags); } /* @@ -675,7 +675,7 @@ static struct ceph_auth_handshake *get_connect_authorizer(struct ceph_connection if (IS_ERR(auth)) return auth; - if (test_bit(CLOSED, &con->state) || test_bit(OPENING, &con->state)) + if (test_bit(CLOSED, &con->state) || test_bit(OPENING, &con->flags)) return ERR_PTR(-EAGAIN); con->auth_reply_buf = auth->authorizer_reply_buf; @@ -695,7 +695,7 @@ static void prepare_write_banner(struct ceph_connection *con) &con->msgr->my_enc_addr); con->out_more = 0; - set_bit(WRITE_PENDING, &con->state); + set_bit(WRITE_PENDING, &con->flags); } static int prepare_write_connect(struct ceph_connection *con) @@ -745,7 +745,7 @@ static int prepare_write_connect(struct ceph_connection *con) auth->authorizer_buf); con->out_more = 0; - set_bit(WRITE_PENDING, &con->state); + set_bit(WRITE_PENDING, &con->flags); return 0; } @@ -1492,7 +1492,7 @@ static int process_connect(struct ceph_connection *con) le32_to_cpu(con->in_reply.connect_seq)); if (con->in_reply.flags & CEPH_MSG_CONNECT_LOSSY) - set_bit(LOSSYTX, &con->state); + set_bit(LOSSYTX, &con->flags); prepare_read_tag(con); break; @@ -1933,14 +1933,14 @@ do_next: prepare_write_ack(con); goto more; } - if (test_and_clear_bit(KEEPALIVE_PENDING, &con->state)) { + if (test_and_clear_bit(KEEPALIVE_PENDING, &con->flags)) { prepare_write_keepalive(con); goto more; } } /* Nothing to do! */ - clear_bit(WRITE_PENDING, &con->state); + clear_bit(WRITE_PENDING, &con->flags); dout("try_write nothing else to write.\n"); ret = 0; out: @@ -2106,7 +2106,7 @@ static void con_work(struct work_struct *work) mutex_lock(&con->mutex); restart: - if (test_and_clear_bit(BACKOFF, &con->state)) { + if (test_and_clear_bit(BACKOFF, &con->flags)) { dout("con_work %p backing off\n", con); if (queue_delayed_work(ceph_msgr_wq, &con->work, round_jiffies_relative(con->delay))) { @@ -2135,7 +2135,7 @@ restart: con_close_socket(con); } - if (test_and_clear_bit(SOCK_CLOSED, &con->state)) + if (test_and_clear_bit(SOCK_CLOSED, &con->flags)) goto fault; ret = try_read(con); @@ -2174,7 +2174,7 @@ static void ceph_fault(struct ceph_connection *con) dout("fault %p state %lu to peer %s\n", con, con->state, ceph_pr_addr(&con->peer_addr.in_addr)); - if (test_bit(LOSSYTX, &con->state)) { + if (test_bit(LOSSYTX, &con->flags)) { dout("fault on LOSSYTX channel\n"); goto out; } @@ -2196,9 +2196,9 @@ static void ceph_fault(struct ceph_connection *con) /* If there are no messages queued or keepalive pending, place * the connection in a STANDBY state */ if (list_empty(&con->out_queue) && - !test_bit(KEEPALIVE_PENDING, &con->state)) { + !test_bit(KEEPALIVE_PENDING, &con->flags)) { dout("fault %p setting STANDBY clearing WRITE_PENDING\n", con); - clear_bit(WRITE_PENDING, &con->state); + clear_bit(WRITE_PENDING, &con->flags); set_bit(STANDBY, &con->state); } else { /* retry after a delay. */ @@ -2222,7 +2222,7 @@ static void ceph_fault(struct ceph_connection *con) * that when con_work restarts we schedule the * delay then. */ - set_bit(BACKOFF, &con->state); + set_bit(BACKOFF, &con->flags); } } @@ -2278,8 +2278,8 @@ static void clear_standby(struct ceph_connection *con) mutex_lock(&con->mutex); dout("clear_standby %p and ++connect_seq\n", con); con->connect_seq++; - WARN_ON(test_bit(WRITE_PENDING, &con->state)); - WARN_ON(test_bit(KEEPALIVE_PENDING, &con->state)); + WARN_ON(test_bit(WRITE_PENDING, &con->flags)); + WARN_ON(test_bit(KEEPALIVE_PENDING, &con->flags)); mutex_unlock(&con->mutex); } } @@ -2317,7 +2317,7 @@ void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg) /* if there wasn't anything waiting to send before, queue * new work */ clear_standby(con); - if (test_and_set_bit(WRITE_PENDING, &con->state) == 0) + if (test_and_set_bit(WRITE_PENDING, &con->flags) == 0) queue_con(con); } EXPORT_SYMBOL(ceph_con_send); @@ -2384,8 +2384,8 @@ void ceph_con_keepalive(struct ceph_connection *con) { dout("con_keepalive %p\n", con); clear_standby(con); - if (test_and_set_bit(KEEPALIVE_PENDING, &con->state) == 0 && - test_and_set_bit(WRITE_PENDING, &con->state) == 0) + if (test_and_set_bit(KEEPALIVE_PENDING, &con->flags) == 0 && + test_and_set_bit(WRITE_PENDING, &con->flags) == 0) queue_con(con); } EXPORT_SYMBOL(ceph_con_keepalive); -- cgit v1.2.3 From ce2c8903e76e690846a00a0284e4bd9ee954d680 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 22 May 2012 22:15:49 -0500 Subject: libceph: start tracking connection socket state Start explicitly keeping track of the state of a ceph connection's socket, separate from the state of the connection itself. Create placeholder functions to encapsulate the state transitions. -------- | NEW* | transient initial state -------- | con_sock_state_init() v ---------- | CLOSED | initialized, but no socket (and no ---------- TCP connection) ^ \ | \ con_sock_state_connecting() | ---------------------- | \ + con_sock_state_closed() \ |\ \ | \ \ | ----------- \ | | CLOSING | socket event; \ | ----------- await close \ | ^ | | | | | + con_sock_state_closing() | | / \ | | / --------------- | | / \ v | / -------------- | / -----------------| CONNECTING | socket created, TCP | | / -------------- connect initiated | | | con_sock_state_connected() | | v ------------- | CONNECTED | TCP connection established ------------- Make the socket state an atomic variable, reinforcing that it's a distinct transtion with no possible "intermediate/both" states. This is almost certainly overkill at this point, though the transitions into CONNECTED and CLOSING state do get called via socket callback (the rest of the transitions occur with the connection mutex held). We can back out the atomicity later. Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- include/linux/ceph/messenger.h | 8 ++++-- net/ceph/messenger.c | 64 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 920235e114ad..5e852f444f68 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -137,14 +137,18 @@ struct ceph_connection { const struct ceph_connection_operations *ops; struct ceph_messenger *msgr; + + atomic_t sock_state; struct socket *sock; + struct ceph_entity_addr peer_addr; /* peer address */ + struct ceph_entity_addr peer_addr_for_me; + unsigned long flags; unsigned long state; const char *error_msg; /* error message, if any */ - struct ceph_entity_addr peer_addr; /* peer address */ struct ceph_entity_name peer_name; /* peer name */ - struct ceph_entity_addr peer_addr_for_me; + unsigned peer_features; u32 connect_seq; /* identify the most recent connection attempt for this connection, client */ diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index e84e4fd86bb7..a4ac3deec161 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -29,6 +29,14 @@ * the sender. */ +/* State values for ceph_connection->sock_state; NEW is assumed to be 0 */ + +#define CON_SOCK_STATE_NEW 0 /* -> CLOSED */ +#define CON_SOCK_STATE_CLOSED 1 /* -> CONNECTING */ +#define CON_SOCK_STATE_CONNECTING 2 /* -> CONNECTED or -> CLOSING */ +#define CON_SOCK_STATE_CONNECTED 3 /* -> CLOSING or -> CLOSED */ +#define CON_SOCK_STATE_CLOSING 4 /* -> CLOSED */ + /* static tag bytes (protocol control messages) */ static char tag_msg = CEPH_MSGR_TAG_MSG; static char tag_ack = CEPH_MSGR_TAG_ACK; @@ -147,6 +155,55 @@ void ceph_msgr_flush(void) } EXPORT_SYMBOL(ceph_msgr_flush); +/* Connection socket state transition functions */ + +static void con_sock_state_init(struct ceph_connection *con) +{ + int old_state; + + old_state = atomic_xchg(&con->sock_state, CON_SOCK_STATE_CLOSED); + if (WARN_ON(old_state != CON_SOCK_STATE_NEW)) + printk("%s: unexpected old state %d\n", __func__, old_state); +} + +static void con_sock_state_connecting(struct ceph_connection *con) +{ + int old_state; + + old_state = atomic_xchg(&con->sock_state, CON_SOCK_STATE_CONNECTING); + if (WARN_ON(old_state != CON_SOCK_STATE_CLOSED)) + printk("%s: unexpected old state %d\n", __func__, old_state); +} + +static void con_sock_state_connected(struct ceph_connection *con) +{ + int old_state; + + old_state = atomic_xchg(&con->sock_state, CON_SOCK_STATE_CONNECTED); + if (WARN_ON(old_state != CON_SOCK_STATE_CONNECTING)) + printk("%s: unexpected old state %d\n", __func__, old_state); +} + +static void con_sock_state_closing(struct ceph_connection *con) +{ + int old_state; + + old_state = atomic_xchg(&con->sock_state, CON_SOCK_STATE_CLOSING); + if (WARN_ON(old_state != CON_SOCK_STATE_CONNECTING && + old_state != CON_SOCK_STATE_CONNECTED && + old_state != CON_SOCK_STATE_CLOSING)) + printk("%s: unexpected old state %d\n", __func__, old_state); +} + +static void con_sock_state_closed(struct ceph_connection *con) +{ + int old_state; + + old_state = atomic_xchg(&con->sock_state, CON_SOCK_STATE_CLOSED); + if (WARN_ON(old_state != CON_SOCK_STATE_CONNECTED && + old_state != CON_SOCK_STATE_CLOSING)) + printk("%s: unexpected old state %d\n", __func__, old_state); +} /* * socket callback functions @@ -203,6 +260,7 @@ static void ceph_sock_state_change(struct sock *sk) dout("%s TCP_CLOSE\n", __func__); case TCP_CLOSE_WAIT: dout("%s TCP_CLOSE_WAIT\n", __func__); + con_sock_state_closing(con); if (test_and_set_bit(SOCK_CLOSED, &con->flags) == 0) { if (test_bit(CONNECTING, &con->state)) con->error_msg = "connection failed"; @@ -213,6 +271,7 @@ static void ceph_sock_state_change(struct sock *sk) break; case TCP_ESTABLISHED: dout("%s TCP_ESTABLISHED\n", __func__); + con_sock_state_connected(con); queue_con(con); break; default: /* Everything else is uninteresting */ @@ -277,6 +336,7 @@ static int ceph_tcp_connect(struct ceph_connection *con) return ret; } con->sock = sock; + con_sock_state_connecting(con); return 0; } @@ -343,6 +403,7 @@ static int con_close_socket(struct ceph_connection *con) sock_release(con->sock); con->sock = NULL; clear_bit(SOCK_CLOSED, &con->state); + con_sock_state_closed(con); return rc; } @@ -462,6 +523,9 @@ void ceph_con_init(struct ceph_messenger *msgr, struct ceph_connection *con) memset(con, 0, sizeof(*con)); atomic_set(&con->nref, 1); con->msgr = msgr; + + con_sock_state_init(con); + mutex_init(&con->mutex); INIT_LIST_HEAD(&con->out_queue); INIT_LIST_HEAD(&con->out_sent); -- cgit v1.2.3 From 86ed4bc83abf530cf2019044b74f89a39dfd6425 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 3 May 2012 11:08:19 +0800 Subject: ACPICA: Add support for multiple notify handlers This change adds support to allow multiple notify handlers on Device, ThermalZone, and Processor objects. Also re-worked and restructured the entire notify support code for handler installation, handler removal, notify event queuing, and notify dispatch to handler. Extends and updates original commit 3f0be67("ACPI / ACPICA: Multiple system notify handlers per device") by Rafael Wysocki. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/acglobal.h | 3 +- drivers/acpi/acpica/aclocal.h | 13 +- drivers/acpi/acpica/acobject.h | 9 +- drivers/acpi/acpica/evmisc.c | 185 +++++++---------- drivers/acpi/acpica/evxface.c | 440 ++++++++++++++--------------------------- drivers/acpi/acpica/exdump.c | 25 ++- drivers/acpi/acpica/utdelete.c | 24 ++- drivers/acpi/acpica/utglobal.c | 4 +- include/acpi/actypes.h | 4 + 9 files changed, 266 insertions(+), 441 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 4f7d3f57d05c..dec7994d405c 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -278,8 +278,7 @@ ACPI_EXTERN acpi_cache_t *acpi_gbl_operand_cache; /* Global handlers */ -ACPI_EXTERN struct acpi_object_notify_handler acpi_gbl_device_notify; -ACPI_EXTERN struct acpi_object_notify_handler acpi_gbl_system_notify; +ACPI_EXTERN struct acpi_global_notify_handler acpi_gbl_global_notify[2]; ACPI_EXTERN acpi_exception_handler acpi_gbl_exception_handler; ACPI_EXTERN acpi_init_handler acpi_gbl_init_handler; ACPI_EXTERN acpi_tbl_handler acpi_gbl_table_handler; diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index e3922ca20e7f..28f677834bfd 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -600,13 +600,22 @@ acpi_status(*acpi_parse_downwards) (struct acpi_walk_state * walk_state, typedef acpi_status(*acpi_parse_upwards) (struct acpi_walk_state * walk_state); +/* Global handlers for AML Notifies */ + +struct acpi_global_notify_handler { + acpi_notify_handler handler; + void *context; +}; + /* * Notify info - used to pass info to the deferred notify * handler/dispatcher. */ struct acpi_notify_info { - ACPI_STATE_COMMON struct acpi_namespace_node *node; - union acpi_operand_object *handler_obj; + ACPI_STATE_COMMON u8 handler_list_id; + struct acpi_namespace_node *node; + union acpi_operand_object *handler_list_head; + struct acpi_global_notify_handler *global; }; /* Generic state is union of structs above */ diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index c065078ca83b..39a2b84120be 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -206,8 +206,7 @@ struct acpi_object_method { * Common fields for objects that support ASL notifications */ #define ACPI_COMMON_NOTIFY_INFO \ - union acpi_operand_object *system_notify; /* Handler for system notifies */\ - union acpi_operand_object *device_notify; /* Handler for driver notifies */\ + union acpi_operand_object *notify_list[2]; /* Handlers for system/device notifies */\ union acpi_operand_object *handler; /* Handler for Address space */ struct acpi_object_notify_common { /* COMMON NOTIFY for POWER, PROCESSOR, DEVICE, and THERMAL */ @@ -296,10 +295,10 @@ struct acpi_object_buffer_field { struct acpi_object_notify_handler { ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *node; /* Parent device */ - u32 handler_type; - acpi_notify_handler handler; + u32 handler_type; /* Type: Device/System/Both */ + acpi_notify_handler handler; /* Handler address */ void *context; - struct acpi_object_notify_handler *next; + union acpi_operand_object *next[2]; /* Device and System handler lists */ }; struct acpi_object_addr_handler { diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c index 51ef9f5e002d..381fce93a561 100644 --- a/drivers/acpi/acpica/evmisc.c +++ b/drivers/acpi/acpica/evmisc.c @@ -101,102 +101,77 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node * node, u32 notify_value) { union acpi_operand_object *obj_desc; - union acpi_operand_object *handler_obj = NULL; - union acpi_generic_state *notify_info; + union acpi_operand_object *handler_list_head = NULL; + union acpi_generic_state *info; + u8 handler_list_id = 0; acpi_status status = AE_OK; ACPI_FUNCTION_NAME(ev_queue_notify_request); - /* - * For value 0x03 (Ejection Request), may need to run a device method. - * For value 0x02 (Device Wake), if _PRW exists, may need to run - * the _PS0 method. - * For value 0x80 (Status Change) on the power button or sleep button, - * initiate soft-off or sleep operation. - * - * For all cases, simply dispatch the notify to the handler. - */ - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Dispatching Notify on [%4.4s] (%s) Value 0x%2.2X (%s) Node %p\n", - acpi_ut_get_node_name(node), - acpi_ut_get_type_name(node->type), notify_value, - acpi_ut_get_notify_name(notify_value), node)); + /* Are Notifies allowed on this object? */ - /* Get the notify object attached to the NS Node */ - - obj_desc = acpi_ns_get_attached_object(node); - if (obj_desc) { - - /* We have the notify object, Get the correct handler */ - - switch (node->type) { + if (!acpi_ev_is_notify_object(node)) { + return (AE_TYPE); + } - /* Notify is allowed only on these types */ + /* Get the correct notify list type (System or Device) */ - case ACPI_TYPE_DEVICE: - case ACPI_TYPE_THERMAL: - case ACPI_TYPE_PROCESSOR: + if (notify_value <= ACPI_MAX_SYS_NOTIFY) { + handler_list_id = ACPI_SYSTEM_HANDLER_LIST; + } else { + handler_list_id = ACPI_DEVICE_HANDLER_LIST; + } - if (notify_value <= ACPI_MAX_SYS_NOTIFY) { - handler_obj = - obj_desc->common_notify.system_notify; - } else { - handler_obj = - obj_desc->common_notify.device_notify; - } - break; + /* Get the notify object attached to the namespace Node */ - default: + obj_desc = acpi_ns_get_attached_object(node); + if (obj_desc) { - /* All other types are not supported */ + /* We have an attached object, Get the correct handler list */ - return (AE_TYPE); - } + handler_list_head = + obj_desc->common_notify.notify_list[handler_list_id]; } /* - * If there is a handler to run, schedule the dispatcher. - * Check for: - * 1) Global system notify handler - * 2) Global device notify handler - * 3) Per-device notify handler + * If there is no notify handler (Global or Local) + * for this object, just ignore the notify */ - if ((acpi_gbl_system_notify.handler && - (notify_value <= ACPI_MAX_SYS_NOTIFY)) || - (acpi_gbl_device_notify.handler && - (notify_value > ACPI_MAX_SYS_NOTIFY)) || handler_obj) { - notify_info = acpi_ut_create_generic_state(); - if (!notify_info) { - return (AE_NO_MEMORY); - } + if (!acpi_gbl_global_notify[handler_list_id].handler + && !handler_list_head) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No notify handler for Notify, ignoring (%4.4s, %X) node %p\n", + acpi_ut_get_node_name(node), notify_value, + node)); - if (!handler_obj) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Executing system notify handler for Notify (%4.4s, %X) " - "node %p\n", - acpi_ut_get_node_name(node), - notify_value, node)); - } + return (AE_OK); + } - notify_info->common.descriptor_type = - ACPI_DESC_TYPE_STATE_NOTIFY; - notify_info->notify.node = node; - notify_info->notify.value = (u16) notify_value; - notify_info->notify.handler_obj = handler_obj; + /* Setup notify info and schedule the notify dispatcher */ - status = - acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_ev_notify_dispatch, - notify_info); - if (ACPI_FAILURE(status)) { - acpi_ut_delete_generic_state(notify_info); - } - } else { - /* There is no notify handler (per-device or system) for this device */ + info = acpi_ut_create_generic_state(); + if (!info) { + return (AE_NO_MEMORY); + } - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "No notify handler for Notify (%4.4s, %X) node %p\n", - acpi_ut_get_node_name(node), notify_value, - node)); + info->common.descriptor_type = ACPI_DESC_TYPE_STATE_NOTIFY; + + info->notify.node = node; + info->notify.value = (u16)notify_value; + info->notify.handler_list_id = handler_list_id; + info->notify.handler_list_head = handler_list_head; + info->notify.global = &acpi_gbl_global_notify[handler_list_id]; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Dispatching Notify on [%4.4s] (%s) Value 0x%2.2X (%s) Node %p\n", + acpi_ut_get_node_name(node), + acpi_ut_get_type_name(node->type), notify_value, + acpi_ut_get_notify_name(notify_value), node)); + + status = acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_ev_notify_dispatch, + info); + if (ACPI_FAILURE(status)) { + acpi_ut_delete_generic_state(info); } return (status); @@ -217,60 +192,34 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node * node, static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context) { - union acpi_generic_state *notify_info = - (union acpi_generic_state *)context; - acpi_notify_handler global_handler = NULL; - void *global_context = NULL; + union acpi_generic_state *info = (union acpi_generic_state *)context; union acpi_operand_object *handler_obj; ACPI_FUNCTION_ENTRY(); - /* - * We will invoke a global notify handler if installed. This is done - * _before_ we invoke the per-device handler attached to the device. - */ - if (notify_info->notify.value <= ACPI_MAX_SYS_NOTIFY) { - - /* Global system notification handler */ - - if (acpi_gbl_system_notify.handler) { - global_handler = acpi_gbl_system_notify.handler; - global_context = acpi_gbl_system_notify.context; - } - } else { - /* Global driver notification handler */ - - if (acpi_gbl_device_notify.handler) { - global_handler = acpi_gbl_device_notify.handler; - global_context = acpi_gbl_device_notify.context; - } - } - - /* Invoke the system handler first, if present */ + /* Invoke a global notify handler if installed */ - if (global_handler) { - global_handler(notify_info->notify.node, - notify_info->notify.value, global_context); + if (info->notify.global->handler) { + info->notify.global->handler(info->notify.node, + info->notify.value, + info->notify.global->context); } - /* Now invoke the per-device handler, if present */ + /* Now invoke the local notify handler(s) if any are installed */ - handler_obj = notify_info->notify.handler_obj; - if (handler_obj) { - struct acpi_object_notify_handler *notifier; + handler_obj = info->notify.handler_list_head; + while (handler_obj) { + handler_obj->notify.handler(info->notify.node, + info->notify.value, + handler_obj->notify.context); - notifier = &handler_obj->notify; - while (notifier) { - notifier->handler(notify_info->notify.node, - notify_info->notify.value, - notifier->context); - notifier = notifier->next; - } + handler_obj = + handler_obj->notify.next[info->notify.handler_list_id]; } /* All done with the info object */ - acpi_ut_delete_generic_state(notify_info); + acpi_ut_delete_generic_state(info); } #if (!ACPI_REDUCED_HARDWARE) diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 44bef5744ebb..90ae6d193645 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -52,88 +52,27 @@ ACPI_MODULE_NAME("evxface") -/******************************************************************************* - * - * FUNCTION: acpi_populate_handler_object - * - * PARAMETERS: handler_obj - Handler object to populate - * handler_type - The type of handler: - * ACPI_SYSTEM_NOTIFY: system_handler (00-7f) - * ACPI_DEVICE_NOTIFY: driver_handler (80-ff) - * ACPI_ALL_NOTIFY: both system and device - * handler - Address of the handler - * context - Value passed to the handler on each GPE - * next - Address of a handler object to link to - * - * RETURN: None - * - * DESCRIPTION: Populate a handler object. - * - ******************************************************************************/ -static void -acpi_populate_handler_object(struct acpi_object_notify_handler *handler_obj, - u32 handler_type, - acpi_notify_handler handler, void *context, - struct acpi_object_notify_handler *next) -{ - handler_obj->handler_type = handler_type; - handler_obj->handler = handler; - handler_obj->context = context; - handler_obj->next = next; -} - -/******************************************************************************* - * - * FUNCTION: acpi_add_handler_object - * - * PARAMETERS: parent_obj - Parent of the new object - * handler - Address of the handler - * context - Value passed to the handler on each GPE - * - * RETURN: Status - * - * DESCRIPTION: Create a new handler object and populate it. - * - ******************************************************************************/ -static acpi_status -acpi_add_handler_object(struct acpi_object_notify_handler *parent_obj, - acpi_notify_handler handler, void *context) -{ - struct acpi_object_notify_handler *handler_obj; - - /* The parent must not be a defice notify handler object. */ - if (parent_obj->handler_type & ACPI_DEVICE_NOTIFY) - return AE_BAD_PARAMETER; - - handler_obj = ACPI_ALLOCATE_ZEROED(sizeof(*handler_obj)); - if (!handler_obj) - return AE_NO_MEMORY; - - acpi_populate_handler_object(handler_obj, - ACPI_SYSTEM_NOTIFY, - handler, context, - parent_obj->next); - parent_obj->next = handler_obj; - - return AE_OK; -} - - /******************************************************************************* * * FUNCTION: acpi_install_notify_handler * * PARAMETERS: Device - The device for which notifies will be handled * handler_type - The type of handler: - * ACPI_SYSTEM_NOTIFY: system_handler (00-7f) - * ACPI_DEVICE_NOTIFY: driver_handler (80-ff) - * ACPI_ALL_NOTIFY: both system and device + * ACPI_SYSTEM_NOTIFY: System Handler (00-7F) + * ACPI_DEVICE_NOTIFY: Device Handler (80-FF) + * ACPI_ALL_NOTIFY: Both System and Device * Handler - Address of the handler * Context - Value passed to the handler on each GPE * * RETURN: Status * - * DESCRIPTION: Install a handler for notifies on an ACPI device + * DESCRIPTION: Install a handler for notifications on an ACPI Device, + * thermal_zone, or Processor object. + * + * NOTES: The Root namespace object may have only one handler for each + * type of notify (System/Device). Device/Thermal/Processor objects + * may have one device notify handler, and multiple system notify + * handlers. * ******************************************************************************/ acpi_status @@ -141,17 +80,19 @@ acpi_install_notify_handler(acpi_handle device, u32 handler_type, acpi_notify_handler handler, void *context) { + struct acpi_namespace_node *node = + ACPI_CAST_PTR(struct acpi_namespace_node, device); union acpi_operand_object *obj_desc; - union acpi_operand_object *notify_obj; - struct acpi_namespace_node *node; + union acpi_operand_object *handler_obj; acpi_status status; + u32 i; ACPI_FUNCTION_TRACE(acpi_install_notify_handler); /* Parameter validation */ - if ((!device) || - (!handler) || (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) { + if ((!device) || (!handler) || (!handler_type) || + (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) { return_ACPI_STATUS(AE_BAD_PARAMETER); } @@ -160,144 +101,112 @@ acpi_install_notify_handler(acpi_handle device, return_ACPI_STATUS(status); } - /* Convert and validate the device handle */ - - node = acpi_ns_validate_handle(device); - if (!node) { - status = AE_BAD_PARAMETER; - goto unlock_and_exit; - } - /* * Root Object: * Registering a notify handler on the root object indicates that the * caller wishes to receive notifications for all objects. Note that - * only one global handler can be regsitered (per notify type). + * only one global handler can be registered per notify type. + * Ensure that a handler is not already installed. */ if (device == ACPI_ROOT_OBJECT) { + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + if (handler_type & (i + 1)) { + if (acpi_gbl_global_notify[i].handler) { + status = AE_ALREADY_EXISTS; + goto unlock_and_exit; + } - /* Make sure the handler is not already installed */ - - if (((handler_type & ACPI_SYSTEM_NOTIFY) && - acpi_gbl_system_notify.handler) || - ((handler_type & ACPI_DEVICE_NOTIFY) && - acpi_gbl_device_notify.handler)) { - status = AE_ALREADY_EXISTS; - goto unlock_and_exit; - } - - if (handler_type & ACPI_SYSTEM_NOTIFY) { - acpi_gbl_system_notify.node = node; - acpi_gbl_system_notify.handler = handler; - acpi_gbl_system_notify.context = context; - } - - if (handler_type & ACPI_DEVICE_NOTIFY) { - acpi_gbl_device_notify.node = node; - acpi_gbl_device_notify.handler = handler; - acpi_gbl_device_notify.context = context; + acpi_gbl_global_notify[i].handler = handler; + acpi_gbl_global_notify[i].context = context; + } } - /* Global notify handler installed */ + goto unlock_and_exit; /* Global notify handler installed, all done */ } /* * All Other Objects: - * Caller will only receive notifications specific to the target object. - * Note that only certain object types can receive notifications. + * Caller will only receive notifications specific to the target + * object. Note that only certain object types are allowed to + * receive notifications. */ - else { - /* Notifies allowed on this object? */ - if (!acpi_ev_is_notify_object(node)) { - status = AE_TYPE; - goto unlock_and_exit; - } + /* Are Notifies allowed on this object? */ - /* Check for an existing internal object */ + if (!acpi_ev_is_notify_object(node)) { + status = AE_TYPE; + goto unlock_and_exit; + } - obj_desc = acpi_ns_get_attached_object(node); - if (obj_desc) { + /* Check for an existing internal object, might not exist */ - /* Object exists. */ + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { - /* For a device notify, make sure there's no handler. */ - if ((handler_type & ACPI_DEVICE_NOTIFY) && - obj_desc->common_notify.device_notify) { - status = AE_ALREADY_EXISTS; - goto unlock_and_exit; - } + /* Create a new object */ - /* System notifies may have more handlers installed. */ - notify_obj = obj_desc->common_notify.system_notify; + obj_desc = acpi_ut_create_internal_object(node->type); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } - if ((handler_type & ACPI_SYSTEM_NOTIFY) && notify_obj) { - struct acpi_object_notify_handler *parent_obj; + /* Attach new object to the Node, remove local reference */ - if (handler_type & ACPI_DEVICE_NOTIFY) { + status = acpi_ns_attach_object(device, obj_desc, node->type); + acpi_ut_remove_reference(obj_desc); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + } + + /* Ensure that the handler is not already installed in the lists */ + + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + if (handler_type & (i + 1)) { + handler_obj = obj_desc->common_notify.notify_list[i]; + while (handler_obj) { + if (handler_obj->notify.handler == handler) { status = AE_ALREADY_EXISTS; goto unlock_and_exit; } - parent_obj = ¬ify_obj->notify; - status = acpi_add_handler_object(parent_obj, - handler, - context); - goto unlock_and_exit; - } - } else { - /* Create a new object */ - - obj_desc = acpi_ut_create_internal_object(node->type); - if (!obj_desc) { - status = AE_NO_MEMORY; - goto unlock_and_exit; - } - - /* Attach new object to the Node */ - - status = - acpi_ns_attach_object(device, obj_desc, node->type); - - /* Remove local reference to the object */ - - acpi_ut_remove_reference(obj_desc); - if (ACPI_FAILURE(status)) { - goto unlock_and_exit; + handler_obj = handler_obj->notify.next[i]; } } + } - /* Install the handler */ + /* Create and populate a new notify handler object */ - notify_obj = - acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_NOTIFY); - if (!notify_obj) { - status = AE_NO_MEMORY; - goto unlock_and_exit; - } + handler_obj = acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_NOTIFY); + if (!handler_obj) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } - acpi_populate_handler_object(¬ify_obj->notify, - handler_type, - handler, context, - NULL); + handler_obj->notify.node = node; + handler_obj->notify.handler_type = handler_type; + handler_obj->notify.handler = handler; + handler_obj->notify.context = context; - if (handler_type & ACPI_SYSTEM_NOTIFY) { - obj_desc->common_notify.system_notify = notify_obj; - } + /* Install the handler at the list head(s) */ - if (handler_type & ACPI_DEVICE_NOTIFY) { - obj_desc->common_notify.device_notify = notify_obj; - } + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + if (handler_type & (i + 1)) { + handler_obj->notify.next[i] = + obj_desc->common_notify.notify_list[i]; - if (handler_type == ACPI_ALL_NOTIFY) { + obj_desc->common_notify.notify_list[i] = handler_obj; + } + } - /* Extra ref if installed in both */ + /* Add an extra reference if handler was installed in both lists */ - acpi_ut_add_reference(notify_obj); - } + if (handler_type == ACPI_ALL_NOTIFY) { + acpi_ut_add_reference(handler_obj); } - unlock_and_exit: +unlock_and_exit: (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); return_ACPI_STATUS(status); } @@ -308,11 +217,11 @@ ACPI_EXPORT_SYMBOL(acpi_install_notify_handler) * * FUNCTION: acpi_remove_notify_handler * - * PARAMETERS: Device - The device for which notifies will be handled + * PARAMETERS: Device - The device for which the handler is installed * handler_type - The type of handler: - * ACPI_SYSTEM_NOTIFY: system_handler (00-7f) - * ACPI_DEVICE_NOTIFY: driver_handler (80-ff) - * ACPI_ALL_NOTIFY: both system and device + * ACPI_SYSTEM_NOTIFY: System Handler (00-7F) + * ACPI_DEVICE_NOTIFY: Device Handler (80-FF) + * ACPI_ALL_NOTIFY: Both System and Device * Handler - Address of the handler * * RETURN: Status @@ -324,165 +233,106 @@ acpi_status acpi_remove_notify_handler(acpi_handle device, u32 handler_type, acpi_notify_handler handler) { - union acpi_operand_object *notify_obj; + struct acpi_namespace_node *node = + ACPI_CAST_PTR(struct acpi_namespace_node, device); union acpi_operand_object *obj_desc; - struct acpi_namespace_node *node; + union acpi_operand_object *handler_obj; + union acpi_operand_object *previous_handler_obj; acpi_status status; + u32 i; ACPI_FUNCTION_TRACE(acpi_remove_notify_handler); /* Parameter validation */ - if ((!device) || - (!handler) || (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) { - status = AE_BAD_PARAMETER; - goto exit; + if ((!device) || (!handler) || (!handler_type) || + (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) { + return_ACPI_STATUS(AE_BAD_PARAMETER); } - - /* Make sure all deferred tasks are completed */ + acpi_os_wait_events_complete(NULL); status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); if (ACPI_FAILURE(status)) { - goto exit; - } - - /* Convert and validate the device handle */ - - node = acpi_ns_validate_handle(device); - if (!node) { - status = AE_BAD_PARAMETER; - goto unlock_and_exit; + return_ACPI_STATUS(status); } - /* Root Object */ + /* Root Object. Global handlers are removed here */ if (device == ACPI_ROOT_OBJECT) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Removing notify handler for namespace root object\n")); + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + if (handler_type & (i + 1)) { + if (!acpi_gbl_global_notify[i].handler || + (acpi_gbl_global_notify[i].handler != + handler)) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } - if (((handler_type & ACPI_SYSTEM_NOTIFY) && - !acpi_gbl_system_notify.handler) || - ((handler_type & ACPI_DEVICE_NOTIFY) && - !acpi_gbl_device_notify.handler)) { - status = AE_NOT_EXIST; - goto unlock_and_exit; - } + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Removing global notify handler\n")); - if (handler_type & ACPI_SYSTEM_NOTIFY) { - acpi_gbl_system_notify.node = NULL; - acpi_gbl_system_notify.handler = NULL; - acpi_gbl_system_notify.context = NULL; + acpi_gbl_global_notify[i].handler = NULL; + acpi_gbl_global_notify[i].context = NULL; + } } - if (handler_type & ACPI_DEVICE_NOTIFY) { - acpi_gbl_device_notify.node = NULL; - acpi_gbl_device_notify.handler = NULL; - acpi_gbl_device_notify.context = NULL; - } + goto unlock_and_exit; } - /* All Other Objects */ - - else { - /* Notifies allowed on this object? */ + /* All other objects: Are Notifies allowed on this object? */ - if (!acpi_ev_is_notify_object(node)) { - status = AE_TYPE; - goto unlock_and_exit; - } + if (!acpi_ev_is_notify_object(node)) { + status = AE_TYPE; + goto unlock_and_exit; + } - /* Check for an existing internal object */ + /* Must have an existing internal object */ - obj_desc = acpi_ns_get_attached_object(node); - if (!obj_desc) { - status = AE_NOT_EXIST; - goto unlock_and_exit; - } + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } - /* Object exists - make sure there's an existing handler */ + /* Internal object exists. Find the handler and remove it */ - if (handler_type & ACPI_SYSTEM_NOTIFY) { - struct acpi_object_notify_handler *handler_obj; - struct acpi_object_notify_handler *parent_obj; + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + if (handler_type & (i + 1)) { + handler_obj = obj_desc->common_notify.notify_list[i]; + previous_handler_obj = NULL; - notify_obj = obj_desc->common_notify.system_notify; - if (!notify_obj) { - status = AE_NOT_EXIST; - goto unlock_and_exit; - } + /* Attempt to find the handler in the handler list */ - handler_obj = ¬ify_obj->notify; - parent_obj = NULL; - while (handler_obj->handler != handler) { - if (handler_obj->next) { - parent_obj = handler_obj; - handler_obj = handler_obj->next; - } else { - break; - } + while (handler_obj && + (handler_obj->notify.handler != handler)) { + previous_handler_obj = handler_obj; + handler_obj = handler_obj->notify.next[i]; } - if (handler_obj->handler != handler) { - status = AE_BAD_PARAMETER; + if (!handler_obj) { + status = AE_NOT_EXIST; goto unlock_and_exit; } - /* - * Remove the handler. There are three possible cases. - * First, we may need to remove a non-embedded object. - * Second, we may need to remove the embedded object's - * handler data, while non-embedded objects exist. - * Finally, we may need to remove the embedded object - * entirely along with its container. - */ - if (parent_obj) { - /* Non-embedded object is being removed. */ - parent_obj->next = handler_obj->next; - ACPI_FREE(handler_obj); - } else if (notify_obj->notify.next) { - /* - * The handler matches the embedded object, but - * there are more handler objects in the list. - * Replace the embedded object's data with the - * first next object's data and remove that - * object. - */ - parent_obj = ¬ify_obj->notify; - handler_obj = notify_obj->notify.next; - *parent_obj = *handler_obj; - ACPI_FREE(handler_obj); - } else { - /* No more handler objects in the list. */ - obj_desc->common_notify.system_notify = NULL; - acpi_ut_remove_reference(notify_obj); - } - } + /* Remove the handler object from the list */ - if (handler_type & ACPI_DEVICE_NOTIFY) { - notify_obj = obj_desc->common_notify.device_notify; - if (!notify_obj) { - status = AE_NOT_EXIST; - goto unlock_and_exit; - } + if (previous_handler_obj) { /* Handler is not at the list head */ + previous_handler_obj->notify.next[i] = + handler_obj->notify.next[i]; + } else { /* Handler is at the list head */ - if (notify_obj->notify.handler != handler) { - status = AE_BAD_PARAMETER; - goto unlock_and_exit; + obj_desc->common_notify.notify_list[i] = + handler_obj->notify.next[i]; } - /* Remove the handler */ - obj_desc->common_notify.device_notify = NULL; - acpi_ut_remove_reference(notify_obj); + acpi_ut_remove_reference(handler_obj); } } - unlock_and_exit: +unlock_and_exit: (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - exit: - if (ACPI_FAILURE(status)) - ACPI_EXCEPTION((AE_INFO, status, "Removing notify handler")); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c index 836fe76e65d0..26c56545804d 100644 --- a/drivers/acpi/acpica/exdump.c +++ b/drivers/acpi/acpica/exdump.c @@ -109,9 +109,9 @@ static struct acpi_exdump_info acpi_ex_dump_package[5] = { static struct acpi_exdump_info acpi_ex_dump_device[4] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_device), NULL}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.handler), "Handler"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.system_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.notify_list[0]), "System Notify"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.device_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.notify_list[1]), "Device Notify"} }; @@ -158,9 +158,9 @@ static struct acpi_exdump_info acpi_ex_dump_power[5] = { "System Level"}, {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(power_resource.resource_order), "Resource Order"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.system_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.notify_list[0]), "System Notify"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.device_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.notify_list[1]), "Device Notify"} }; @@ -169,18 +169,18 @@ static struct acpi_exdump_info acpi_ex_dump_processor[7] = { {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(processor.proc_id), "Processor ID"}, {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(processor.length), "Length"}, {ACPI_EXD_ADDRESS, ACPI_EXD_OFFSET(processor.address), "Address"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.system_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.notify_list[0]), "System Notify"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.device_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.notify_list[1]), "Device Notify"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.handler), "Handler"} }; static struct acpi_exdump_info acpi_ex_dump_thermal[4] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_thermal), NULL}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.system_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.notify_list[0]), "System Notify"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.device_notify), + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.notify_list[1]), "Device Notify"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.handler), "Handler"} }; @@ -241,10 +241,15 @@ static struct acpi_exdump_info acpi_ex_dump_address_handler[6] = { {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(address_space.context), "Context"} }; -static struct acpi_exdump_info acpi_ex_dump_notify[3] = { +static struct acpi_exdump_info acpi_ex_dump_notify[7] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_notify), NULL}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.node), "Node"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.context), "Context"} + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(notify.handler_type), "Handler Type"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.handler), "Handler"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.context), "Context"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.next[0]), + "Next System Notify"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.next[1]), "Next Device Notify"} }; /* Miscellaneous tables */ diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c index 2a6c3e183697..0d50f2c6bac2 100644 --- a/drivers/acpi/acpica/utdelete.c +++ b/drivers/acpi/acpica/utdelete.c @@ -152,7 +152,7 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) case ACPI_TYPE_PROCESSOR: case ACPI_TYPE_THERMAL: - /* Walk the notify handler list for this object */ + /* Walk the address handler list for this object */ handler_desc = object->common_notify.handler; while (handler_desc) { @@ -480,6 +480,7 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) acpi_status status = AE_OK; union acpi_generic_state *state_list = NULL; union acpi_operand_object *next_object = NULL; + union acpi_operand_object *prev_object; union acpi_generic_state *state; u32 i; @@ -505,12 +506,21 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) case ACPI_TYPE_POWER: case ACPI_TYPE_THERMAL: - /* Update the notify objects for these types (if present) */ - - acpi_ut_update_ref_count(object->common_notify. - system_notify, action); - acpi_ut_update_ref_count(object->common_notify. - device_notify, action); + /* + * Update the notify objects for these types (if present) + * Two lists, system and device notify handlers. + */ + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + prev_object = + object->common_notify.notify_list[i]; + while (prev_object) { + next_object = + prev_object->notify.next[i]; + acpi_ut_update_ref_count(prev_object, + action); + prev_object = next_object; + } + } break; case ACPI_TYPE_PACKAGE: diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c index 90f53b42eca9..78cf1fe390b3 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -304,8 +304,8 @@ acpi_status acpi_ut_init_globals(void) /* Global handlers */ - acpi_gbl_system_notify.handler = NULL; - acpi_gbl_device_notify.handler = NULL; + acpi_gbl_global_notify[0].handler = NULL; + acpi_gbl_global_notify[1].handler = NULL; acpi_gbl_exception_handler = NULL; acpi_gbl_init_handler = NULL; acpi_gbl_table_handler = NULL; diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index e8bcc4742e0e..0339a2d93f1d 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -706,10 +706,14 @@ typedef u32 acpi_event_status; #define ACPI_DEVICE_NOTIFY 0x2 #define ACPI_ALL_NOTIFY (ACPI_SYSTEM_NOTIFY | ACPI_DEVICE_NOTIFY) #define ACPI_MAX_NOTIFY_HANDLER_TYPE 0x3 +#define ACPI_NUM_NOTIFY_TYPES 2 #define ACPI_MAX_SYS_NOTIFY 0x7F #define ACPI_MAX_DEVICE_SPECIFIC_NOTIFY 0xBF +#define ACPI_SYSTEM_HANDLER_LIST 0 /* Used as index, must be SYSTEM_NOTIFY -1 */ +#define ACPI_DEVICE_HANDLER_LIST 1 /* Used as index, must be DEVICE_NOTIFY -1 */ + /* Address Space (Operation Region) Types */ typedef u8 acpi_adr_space_type; -- cgit v1.2.3 From 9fd7522570449bd10f6a6377cf5cb542ef527dd5 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 3 May 2012 10:50:54 +0800 Subject: ACPICA: Update to version 20120420 Version 20120420. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 982110134672..a323a7cc1224 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -47,7 +47,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20120320 +#define ACPI_CA_VERSION 0x20120420 #include "acconfig.h" #include "actypes.h" -- cgit v1.2.3 From 43e1c6892c88ae0730cb0eb7bd5a2812b960c32a Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Tue, 22 May 2012 16:23:21 +0800 Subject: ACPICA: ACPI 5/iASL: Add support for PCC keyword Adds support for the PCC keyword for the Register() resource descriptor macro. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- include/acpi/actypes.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 0339a2d93f1d..376c9ed739d4 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -728,8 +728,9 @@ typedef u8 acpi_adr_space_type; #define ACPI_ADR_SPACE_IPMI (acpi_adr_space_type) 7 #define ACPI_ADR_SPACE_GPIO (acpi_adr_space_type) 8 #define ACPI_ADR_SPACE_GSBUS (acpi_adr_space_type) 9 +#define ACPI_ADR_SPACE_PLATFORM_COMM (acpi_adr_space_type) 10 -#define ACPI_NUM_PREDEFINED_REGIONS 10 +#define ACPI_NUM_PREDEFINED_REGIONS 11 /* * Special Address Spaces -- cgit v1.2.3 From bd6f10a5f984e48cb56a39f2698cd58e7a33d56b Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Tue, 22 May 2012 16:43:49 +0800 Subject: ACPICA: Remove argument of acpi_os_wait_events_complete Remove the unused argument of acpi_os_wait_events_complete. Signed-off-by: Lin Ming Signed-off-by: Bob Moore Signed-off-by: Len Brown --- drivers/acpi/acpica/evxface.c | 4 ++-- drivers/acpi/osl.c | 4 ++-- drivers/acpi/sleep.c | 2 +- include/acpi/acpiosxf.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 90ae6d193645..6a8b53789be4 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -251,7 +251,7 @@ acpi_remove_notify_handler(acpi_handle device, } /* Make sure all deferred tasks are completed */ - acpi_os_wait_events_complete(NULL); + acpi_os_wait_events_complete(); status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); if (ACPI_FAILURE(status)) { @@ -699,7 +699,7 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, /* Make sure all deferred tasks are completed */ - acpi_os_wait_events_complete(NULL); + acpi_os_wait_events_complete(); status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); if (ACPI_FAILURE(status)) { diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index c3881b2eb8b2..9eaf708f5885 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -891,7 +891,7 @@ static void acpi_os_execute_deferred(struct work_struct *work) struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work); if (dpc->wait) - acpi_os_wait_events_complete(NULL); + acpi_os_wait_events_complete(); dpc->function(dpc->context); kfree(dpc); @@ -987,7 +987,7 @@ acpi_status acpi_os_hotplug_execute(acpi_osd_exec_callback function, return __acpi_os_execute(0, function, context, 1); } -void acpi_os_wait_events_complete(void *context) +void acpi_os_wait_events_complete(void) { flush_workqueue(kacpid_wq); flush_workqueue(kacpi_notify_wq); diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index eb6fd233764b..9a14f90d98ec 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -144,7 +144,7 @@ void __init acpi_old_suspend_ordering(void) static int acpi_pm_freeze(void) { acpi_disable_all_gpes(); - acpi_os_wait_events_complete(NULL); + acpi_os_wait_events_complete(); acpi_ec_block_transactions(); return 0; } diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index 21a5548c6686..f3746f7e0f40 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h @@ -205,7 +205,7 @@ acpi_os_execute(acpi_execute_type type, acpi_status acpi_os_hotplug_execute(acpi_osd_exec_callback function, void *context); -void acpi_os_wait_events_complete(void *context); +void acpi_os_wait_events_complete(void); void acpi_os_sleep(u64 milliseconds); -- cgit v1.2.3 From 9748f313101c95dcc65f5dddd1c2ea99b7ce3e9b Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Tue, 22 May 2012 16:44:42 +0800 Subject: ACPICA: Update to version 20120518 Version string 20120518. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index a323a7cc1224..381c94008536 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -47,7 +47,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20120420 +#define ACPI_CA_VERSION 0x20120518 #include "acconfig.h" #include "actypes.h" -- cgit v1.2.3 From 4126c0197bc8c58a0bb7fcda07b01b596b6fb4c5 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 7 May 2012 17:57:41 -0700 Subject: cpuidle: add support for states that affect multiple cpus On some ARM SMP SoCs (OMAP4460, Tegra 2, and probably more), the cpus cannot be independently powered down, either due to sequencing restrictions (on Tegra 2, cpu 0 must be the last to power down), or due to HW bugs (on OMAP4460, a cpu powering up will corrupt the gic state unless the other cpu runs a work around). Each cpu has a power state that it can enter without coordinating with the other cpu (usually Wait For Interrupt, or WFI), and one or more "coupled" power states that affect blocks shared between the cpus (L2 cache, interrupt controller, and sometimes the whole SoC). Entering a coupled power state must be tightly controlled on both cpus. The easiest solution to implementing coupled cpu power states is to hotplug all but one cpu whenever possible, usually using a cpufreq governor that looks at cpu load to determine when to enable the secondary cpus. This causes problems, as hotplug is an expensive operation, so the number of hotplug transitions must be minimized, leading to very slow response to loads, often on the order of seconds. This file implements an alternative solution, where each cpu will wait in the WFI state until all cpus are ready to enter a coupled state, at which point the coupled state function will be called on all cpus at approximately the same time. Once all cpus are ready to enter idle, they are woken by an smp cross call. At this point, there is a chance that one of the cpus will find work to do, and choose not to enter idle. A final pass is needed to guarantee that all cpus will call the power state enter function at the same time. During this pass, each cpu will increment the ready counter, and continue once the ready counter matches the number of online coupled cpus. If any cpu exits idle, the other cpus will decrement their counter and retry. To use coupled cpuidle states, a cpuidle driver must: Set struct cpuidle_device.coupled_cpus to the mask of all coupled cpus, usually the same as cpu_possible_mask if all cpus are part of the same cluster. The coupled_cpus mask must be set in the struct cpuidle_device for each cpu. Set struct cpuidle_device.safe_state to a state that is not a coupled state. This is usually WFI. Set CPUIDLE_FLAG_COUPLED in struct cpuidle_state.flags for each state that affects multiple cpus. Provide a struct cpuidle_state.enter function for each state that affects multiple cpus. This function is guaranteed to be called on all cpus at approximately the same time. The driver should ensure that the cpus all abort together if any cpu tries to abort once the function is called. update1: cpuidle: coupled: fix count of online cpus online_count was never incremented on boot, and was also counting cpus that were not part of the coupled set. Fix both issues by introducting a new function that counts online coupled cpus, and call it from register as well as the hotplug notifier. update2: cpuidle: coupled: fix decrementing ready count cpuidle_coupled_set_not_ready sometimes refuses to decrement the ready count in order to prevent a race condition. This makes it unsuitable for use when finished with idle. Add a new function cpuidle_coupled_set_done that decrements both the ready count and waiting count, and call it after idle is complete. Cc: Amit Kucheria Cc: Arjan van de Ven Cc: Trinabh Gupta Cc: Deepthi Dharwar Reviewed-by: Santosh Shilimkar Tested-by: Santosh Shilimkar Reviewed-by: Kevin Hilman Tested-by: Kevin Hilman Signed-off-by: Colin Cross Acked-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/cpuidle/Kconfig | 3 + drivers/cpuidle/Makefile | 1 + drivers/cpuidle/coupled.c | 678 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/cpuidle/cpuidle.c | 15 +- drivers/cpuidle/cpuidle.h | 30 ++ include/linux/cpuidle.h | 7 + 6 files changed, 733 insertions(+), 1 deletion(-) create mode 100644 drivers/cpuidle/coupled.c (limited to 'include') diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index 78a666d1e5f5..a76b689e553b 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -18,3 +18,6 @@ config CPU_IDLE_GOV_MENU bool depends on CPU_IDLE && NO_HZ default y + +config ARCH_NEEDS_CPU_IDLE_COUPLED + def_bool n diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 5634f88379df..38c8f69f30cf 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -3,3 +3,4 @@ # obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ +obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c new file mode 100644 index 000000000000..aab6bba8daec --- /dev/null +++ b/drivers/cpuidle/coupled.c @@ -0,0 +1,678 @@ +/* + * coupled.c - helper functions to enter the same idle state on multiple cpus + * + * Copyright (c) 2011 Google, Inc. + * + * Author: Colin Cross + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "cpuidle.h" + +/** + * DOC: Coupled cpuidle states + * + * On some ARM SMP SoCs (OMAP4460, Tegra 2, and probably more), the + * cpus cannot be independently powered down, either due to + * sequencing restrictions (on Tegra 2, cpu 0 must be the last to + * power down), or due to HW bugs (on OMAP4460, a cpu powering up + * will corrupt the gic state unless the other cpu runs a work + * around). Each cpu has a power state that it can enter without + * coordinating with the other cpu (usually Wait For Interrupt, or + * WFI), and one or more "coupled" power states that affect blocks + * shared between the cpus (L2 cache, interrupt controller, and + * sometimes the whole SoC). Entering a coupled power state must + * be tightly controlled on both cpus. + * + * This file implements a solution, where each cpu will wait in the + * WFI state until all cpus are ready to enter a coupled state, at + * which point the coupled state function will be called on all + * cpus at approximately the same time. + * + * Once all cpus are ready to enter idle, they are woken by an smp + * cross call. At this point, there is a chance that one of the + * cpus will find work to do, and choose not to enter idle. A + * final pass is needed to guarantee that all cpus will call the + * power state enter function at the same time. During this pass, + * each cpu will increment the ready counter, and continue once the + * ready counter matches the number of online coupled cpus. If any + * cpu exits idle, the other cpus will decrement their counter and + * retry. + * + * requested_state stores the deepest coupled idle state each cpu + * is ready for. It is assumed that the states are indexed from + * shallowest (highest power, lowest exit latency) to deepest + * (lowest power, highest exit latency). The requested_state + * variable is not locked. It is only written from the cpu that + * it stores (or by the on/offlining cpu if that cpu is offline), + * and only read after all the cpus are ready for the coupled idle + * state are are no longer updating it. + * + * Three atomic counters are used. alive_count tracks the number + * of cpus in the coupled set that are currently or soon will be + * online. waiting_count tracks the number of cpus that are in + * the waiting loop, in the ready loop, or in the coupled idle state. + * ready_count tracks the number of cpus that are in the ready loop + * or in the coupled idle state. + * + * To use coupled cpuidle states, a cpuidle driver must: + * + * Set struct cpuidle_device.coupled_cpus to the mask of all + * coupled cpus, usually the same as cpu_possible_mask if all cpus + * are part of the same cluster. The coupled_cpus mask must be + * set in the struct cpuidle_device for each cpu. + * + * Set struct cpuidle_device.safe_state to a state that is not a + * coupled state. This is usually WFI. + * + * Set CPUIDLE_FLAG_COUPLED in struct cpuidle_state.flags for each + * state that affects multiple cpus. + * + * Provide a struct cpuidle_state.enter function for each state + * that affects multiple cpus. This function is guaranteed to be + * called on all cpus at approximately the same time. The driver + * should ensure that the cpus all abort together if any cpu tries + * to abort once the function is called. The function should return + * with interrupts still disabled. + */ + +/** + * struct cpuidle_coupled - data for set of cpus that share a coupled idle state + * @coupled_cpus: mask of cpus that are part of the coupled set + * @requested_state: array of requested states for cpus in the coupled set + * @ready_waiting_counts: combined count of cpus in ready or waiting loops + * @online_count: count of cpus that are online + * @refcnt: reference count of cpuidle devices that are using this struct + * @prevent: flag to prevent coupled idle while a cpu is hotplugging + */ +struct cpuidle_coupled { + cpumask_t coupled_cpus; + int requested_state[NR_CPUS]; + atomic_t ready_waiting_counts; + int online_count; + int refcnt; + int prevent; +}; + +#define WAITING_BITS 16 +#define MAX_WAITING_CPUS (1 << WAITING_BITS) +#define WAITING_MASK (MAX_WAITING_CPUS - 1) +#define READY_MASK (~WAITING_MASK) + +#define CPUIDLE_COUPLED_NOT_IDLE (-1) + +static DEFINE_MUTEX(cpuidle_coupled_lock); +static DEFINE_PER_CPU(struct call_single_data, cpuidle_coupled_poke_cb); + +/* + * The cpuidle_coupled_poked_mask mask is used to avoid calling + * __smp_call_function_single with the per cpu call_single_data struct already + * in use. This prevents a deadlock where two cpus are waiting for each others + * call_single_data struct to be available + */ +static cpumask_t cpuidle_coupled_poked_mask; + +/** + * cpuidle_state_is_coupled - check if a state is part of a coupled set + * @dev: struct cpuidle_device for the current cpu + * @drv: struct cpuidle_driver for the platform + * @state: index of the target state in drv->states + * + * Returns true if the target state is coupled with cpus besides this one + */ +bool cpuidle_state_is_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int state) +{ + return drv->states[state].flags & CPUIDLE_FLAG_COUPLED; +} + +/** + * cpuidle_coupled_set_ready - mark a cpu as ready + * @coupled: the struct coupled that contains the current cpu + */ +static inline void cpuidle_coupled_set_ready(struct cpuidle_coupled *coupled) +{ + atomic_add(MAX_WAITING_CPUS, &coupled->ready_waiting_counts); +} + +/** + * cpuidle_coupled_set_not_ready - mark a cpu as not ready + * @coupled: the struct coupled that contains the current cpu + * + * Decrements the ready counter, unless the ready (and thus the waiting) counter + * is equal to the number of online cpus. Prevents a race where one cpu + * decrements the waiting counter and then re-increments it just before another + * cpu has decremented its ready counter, leading to the ready counter going + * down from the number of online cpus without going through the coupled idle + * state. + * + * Returns 0 if the counter was decremented successfully, -EINVAL if the ready + * counter was equal to the number of online cpus. + */ +static +inline int cpuidle_coupled_set_not_ready(struct cpuidle_coupled *coupled) +{ + int all; + int ret; + + all = coupled->online_count || (coupled->online_count << WAITING_BITS); + ret = atomic_add_unless(&coupled->ready_waiting_counts, + -MAX_WAITING_CPUS, all); + + return ret ? 0 : -EINVAL; +} + +/** + * cpuidle_coupled_no_cpus_ready - check if no cpus in a coupled set are ready + * @coupled: the struct coupled that contains the current cpu + * + * Returns true if all of the cpus in a coupled set are out of the ready loop. + */ +static inline int cpuidle_coupled_no_cpus_ready(struct cpuidle_coupled *coupled) +{ + int r = atomic_read(&coupled->ready_waiting_counts) >> WAITING_BITS; + return r == 0; +} + +/** + * cpuidle_coupled_cpus_ready - check if all cpus in a coupled set are ready + * @coupled: the struct coupled that contains the current cpu + * + * Returns true if all cpus coupled to this target state are in the ready loop + */ +static inline bool cpuidle_coupled_cpus_ready(struct cpuidle_coupled *coupled) +{ + int r = atomic_read(&coupled->ready_waiting_counts) >> WAITING_BITS; + return r == coupled->online_count; +} + +/** + * cpuidle_coupled_cpus_waiting - check if all cpus in a coupled set are waiting + * @coupled: the struct coupled that contains the current cpu + * + * Returns true if all cpus coupled to this target state are in the wait loop + */ +static inline bool cpuidle_coupled_cpus_waiting(struct cpuidle_coupled *coupled) +{ + int w = atomic_read(&coupled->ready_waiting_counts) & WAITING_MASK; + return w == coupled->online_count; +} + +/** + * cpuidle_coupled_no_cpus_waiting - check if no cpus in coupled set are waiting + * @coupled: the struct coupled that contains the current cpu + * + * Returns true if all of the cpus in a coupled set are out of the waiting loop. + */ +static inline int cpuidle_coupled_no_cpus_waiting(struct cpuidle_coupled *coupled) +{ + int w = atomic_read(&coupled->ready_waiting_counts) & WAITING_MASK; + return w == 0; +} + +/** + * cpuidle_coupled_get_state - determine the deepest idle state + * @dev: struct cpuidle_device for this cpu + * @coupled: the struct coupled that contains the current cpu + * + * Returns the deepest idle state that all coupled cpus can enter + */ +static inline int cpuidle_coupled_get_state(struct cpuidle_device *dev, + struct cpuidle_coupled *coupled) +{ + int i; + int state = INT_MAX; + + /* + * Read barrier ensures that read of requested_state is ordered after + * reads of ready_count. Matches the write barriers + * cpuidle_set_state_waiting. + */ + smp_rmb(); + + for_each_cpu_mask(i, coupled->coupled_cpus) + if (cpu_online(i) && coupled->requested_state[i] < state) + state = coupled->requested_state[i]; + + return state; +} + +static void cpuidle_coupled_poked(void *info) +{ + int cpu = (unsigned long)info; + cpumask_clear_cpu(cpu, &cpuidle_coupled_poked_mask); +} + +/** + * cpuidle_coupled_poke - wake up a cpu that may be waiting + * @cpu: target cpu + * + * Ensures that the target cpu exits it's waiting idle state (if it is in it) + * and will see updates to waiting_count before it re-enters it's waiting idle + * state. + * + * If cpuidle_coupled_poked_mask is already set for the target cpu, that cpu + * either has or will soon have a pending IPI that will wake it out of idle, + * or it is currently processing the IPI and is not in idle. + */ +static void cpuidle_coupled_poke(int cpu) +{ + struct call_single_data *csd = &per_cpu(cpuidle_coupled_poke_cb, cpu); + + if (!cpumask_test_and_set_cpu(cpu, &cpuidle_coupled_poked_mask)) + __smp_call_function_single(cpu, csd, 0); +} + +/** + * cpuidle_coupled_poke_others - wake up all other cpus that may be waiting + * @dev: struct cpuidle_device for this cpu + * @coupled: the struct coupled that contains the current cpu + * + * Calls cpuidle_coupled_poke on all other online cpus. + */ +static void cpuidle_coupled_poke_others(int this_cpu, + struct cpuidle_coupled *coupled) +{ + int cpu; + + for_each_cpu_mask(cpu, coupled->coupled_cpus) + if (cpu != this_cpu && cpu_online(cpu)) + cpuidle_coupled_poke(cpu); +} + +/** + * cpuidle_coupled_set_waiting - mark this cpu as in the wait loop + * @dev: struct cpuidle_device for this cpu + * @coupled: the struct coupled that contains the current cpu + * @next_state: the index in drv->states of the requested state for this cpu + * + * Updates the requested idle state for the specified cpuidle device, + * poking all coupled cpus out of idle if necessary to let them see the new + * state. + */ +static void cpuidle_coupled_set_waiting(int cpu, + struct cpuidle_coupled *coupled, int next_state) +{ + int w; + + coupled->requested_state[cpu] = next_state; + + /* + * If this is the last cpu to enter the waiting state, poke + * all the other cpus out of their waiting state so they can + * enter a deeper state. This can race with one of the cpus + * exiting the waiting state due to an interrupt and + * decrementing waiting_count, see comment below. + * + * The atomic_inc_return provides a write barrier to order the write + * to requested_state with the later write that increments ready_count. + */ + w = atomic_inc_return(&coupled->ready_waiting_counts) & WAITING_MASK; + if (w == coupled->online_count) + cpuidle_coupled_poke_others(cpu, coupled); +} + +/** + * cpuidle_coupled_set_not_waiting - mark this cpu as leaving the wait loop + * @dev: struct cpuidle_device for this cpu + * @coupled: the struct coupled that contains the current cpu + * + * Removes the requested idle state for the specified cpuidle device. + */ +static void cpuidle_coupled_set_not_waiting(int cpu, + struct cpuidle_coupled *coupled) +{ + /* + * Decrementing waiting count can race with incrementing it in + * cpuidle_coupled_set_waiting, but that's OK. Worst case, some + * cpus will increment ready_count and then spin until they + * notice that this cpu has cleared it's requested_state. + */ + atomic_dec(&coupled->ready_waiting_counts); + + coupled->requested_state[cpu] = CPUIDLE_COUPLED_NOT_IDLE; +} + +/** + * cpuidle_coupled_set_done - mark this cpu as leaving the ready loop + * @cpu: the current cpu + * @coupled: the struct coupled that contains the current cpu + * + * Marks this cpu as no longer in the ready and waiting loops. Decrements + * the waiting count first to prevent another cpu looping back in and seeing + * this cpu as waiting just before it exits idle. + */ +static void cpuidle_coupled_set_done(int cpu, struct cpuidle_coupled *coupled) +{ + cpuidle_coupled_set_not_waiting(cpu, coupled); + atomic_sub(MAX_WAITING_CPUS, &coupled->ready_waiting_counts); +} + +/** + * cpuidle_coupled_clear_pokes - spin until the poke interrupt is processed + * @cpu - this cpu + * + * Turns on interrupts and spins until any outstanding poke interrupts have + * been processed and the poke bit has been cleared. + * + * Other interrupts may also be processed while interrupts are enabled, so + * need_resched() must be tested after turning interrupts off again to make sure + * the interrupt didn't schedule work that should take the cpu out of idle. + * + * Returns 0 if need_resched was false, -EINTR if need_resched was true. + */ +static int cpuidle_coupled_clear_pokes(int cpu) +{ + local_irq_enable(); + while (cpumask_test_cpu(cpu, &cpuidle_coupled_poked_mask)) + cpu_relax(); + local_irq_disable(); + + return need_resched() ? -EINTR : 0; +} + +/** + * cpuidle_enter_state_coupled - attempt to enter a state with coupled cpus + * @dev: struct cpuidle_device for the current cpu + * @drv: struct cpuidle_driver for the platform + * @next_state: index of the requested state in drv->states + * + * Coordinate with coupled cpus to enter the target state. This is a two + * stage process. In the first stage, the cpus are operating independently, + * and may call into cpuidle_enter_state_coupled at completely different times. + * To save as much power as possible, the first cpus to call this function will + * go to an intermediate state (the cpuidle_device's safe state), and wait for + * all the other cpus to call this function. Once all coupled cpus are idle, + * the second stage will start. Each coupled cpu will spin until all cpus have + * guaranteed that they will call the target_state. + * + * This function must be called with interrupts disabled. It may enable + * interrupts while preparing for idle, and it will always return with + * interrupts enabled. + */ +int cpuidle_enter_state_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int next_state) +{ + int entered_state = -1; + struct cpuidle_coupled *coupled = dev->coupled; + + if (!coupled) + return -EINVAL; + + while (coupled->prevent) { + if (cpuidle_coupled_clear_pokes(dev->cpu)) { + local_irq_enable(); + return entered_state; + } + entered_state = cpuidle_enter_state(dev, drv, + dev->safe_state_index); + } + + /* Read barrier ensures online_count is read after prevent is cleared */ + smp_rmb(); + + cpuidle_coupled_set_waiting(dev->cpu, coupled, next_state); + +retry: + /* + * Wait for all coupled cpus to be idle, using the deepest state + * allowed for a single cpu. + */ + while (!cpuidle_coupled_cpus_waiting(coupled)) { + if (cpuidle_coupled_clear_pokes(dev->cpu)) { + cpuidle_coupled_set_not_waiting(dev->cpu, coupled); + goto out; + } + + if (coupled->prevent) { + cpuidle_coupled_set_not_waiting(dev->cpu, coupled); + goto out; + } + + entered_state = cpuidle_enter_state(dev, drv, + dev->safe_state_index); + } + + if (cpuidle_coupled_clear_pokes(dev->cpu)) { + cpuidle_coupled_set_not_waiting(dev->cpu, coupled); + goto out; + } + + /* + * All coupled cpus are probably idle. There is a small chance that + * one of the other cpus just became active. Increment the ready count, + * and spin until all coupled cpus have incremented the counter. Once a + * cpu has incremented the ready counter, it cannot abort idle and must + * spin until either all cpus have incremented the ready counter, or + * another cpu leaves idle and decrements the waiting counter. + */ + + cpuidle_coupled_set_ready(coupled); + while (!cpuidle_coupled_cpus_ready(coupled)) { + /* Check if any other cpus bailed out of idle. */ + if (!cpuidle_coupled_cpus_waiting(coupled)) + if (!cpuidle_coupled_set_not_ready(coupled)) + goto retry; + + cpu_relax(); + } + + /* all cpus have acked the coupled state */ + next_state = cpuidle_coupled_get_state(dev, coupled); + + entered_state = cpuidle_enter_state(dev, drv, next_state); + + cpuidle_coupled_set_done(dev->cpu, coupled); + +out: + /* + * Normal cpuidle states are expected to return with irqs enabled. + * That leads to an inefficiency where a cpu receiving an interrupt + * that brings it out of idle will process that interrupt before + * exiting the idle enter function and decrementing ready_count. All + * other cpus will need to spin waiting for the cpu that is processing + * the interrupt. If the driver returns with interrupts disabled, + * all other cpus will loop back into the safe idle state instead of + * spinning, saving power. + * + * Calling local_irq_enable here allows coupled states to return with + * interrupts disabled, but won't cause problems for drivers that + * exit with interrupts enabled. + */ + local_irq_enable(); + + /* + * Wait until all coupled cpus have exited idle. There is no risk that + * a cpu exits and re-enters the ready state because this cpu has + * already decremented its waiting_count. + */ + while (!cpuidle_coupled_no_cpus_ready(coupled)) + cpu_relax(); + + return entered_state; +} + +static void cpuidle_coupled_update_online_cpus(struct cpuidle_coupled *coupled) +{ + cpumask_t cpus; + cpumask_and(&cpus, cpu_online_mask, &coupled->coupled_cpus); + coupled->online_count = cpumask_weight(&cpus); +} + +/** + * cpuidle_coupled_register_device - register a coupled cpuidle device + * @dev: struct cpuidle_device for the current cpu + * + * Called from cpuidle_register_device to handle coupled idle init. Finds the + * cpuidle_coupled struct for this set of coupled cpus, or creates one if none + * exists yet. + */ +int cpuidle_coupled_register_device(struct cpuidle_device *dev) +{ + int cpu; + struct cpuidle_device *other_dev; + struct call_single_data *csd; + struct cpuidle_coupled *coupled; + + if (cpumask_empty(&dev->coupled_cpus)) + return 0; + + for_each_cpu_mask(cpu, dev->coupled_cpus) { + other_dev = per_cpu(cpuidle_devices, cpu); + if (other_dev && other_dev->coupled) { + coupled = other_dev->coupled; + goto have_coupled; + } + } + + /* No existing coupled info found, create a new one */ + coupled = kzalloc(sizeof(struct cpuidle_coupled), GFP_KERNEL); + if (!coupled) + return -ENOMEM; + + coupled->coupled_cpus = dev->coupled_cpus; + +have_coupled: + dev->coupled = coupled; + if (WARN_ON(!cpumask_equal(&dev->coupled_cpus, &coupled->coupled_cpus))) + coupled->prevent++; + + cpuidle_coupled_update_online_cpus(coupled); + + coupled->refcnt++; + + csd = &per_cpu(cpuidle_coupled_poke_cb, dev->cpu); + csd->func = cpuidle_coupled_poked; + csd->info = (void *)(unsigned long)dev->cpu; + + return 0; +} + +/** + * cpuidle_coupled_unregister_device - unregister a coupled cpuidle device + * @dev: struct cpuidle_device for the current cpu + * + * Called from cpuidle_unregister_device to tear down coupled idle. Removes the + * cpu from the coupled idle set, and frees the cpuidle_coupled_info struct if + * this was the last cpu in the set. + */ +void cpuidle_coupled_unregister_device(struct cpuidle_device *dev) +{ + struct cpuidle_coupled *coupled = dev->coupled; + + if (cpumask_empty(&dev->coupled_cpus)) + return; + + if (--coupled->refcnt) + kfree(coupled); + dev->coupled = NULL; +} + +/** + * cpuidle_coupled_prevent_idle - prevent cpus from entering a coupled state + * @coupled: the struct coupled that contains the cpu that is changing state + * + * Disables coupled cpuidle on a coupled set of cpus. Used to ensure that + * cpu_online_mask doesn't change while cpus are coordinating coupled idle. + */ +static void cpuidle_coupled_prevent_idle(struct cpuidle_coupled *coupled) +{ + int cpu = get_cpu(); + + /* Force all cpus out of the waiting loop. */ + coupled->prevent++; + cpuidle_coupled_poke_others(cpu, coupled); + put_cpu(); + while (!cpuidle_coupled_no_cpus_waiting(coupled)) + cpu_relax(); +} + +/** + * cpuidle_coupled_allow_idle - allows cpus to enter a coupled state + * @coupled: the struct coupled that contains the cpu that is changing state + * + * Enables coupled cpuidle on a coupled set of cpus. Used to ensure that + * cpu_online_mask doesn't change while cpus are coordinating coupled idle. + */ +static void cpuidle_coupled_allow_idle(struct cpuidle_coupled *coupled) +{ + int cpu = get_cpu(); + + /* + * Write barrier ensures readers see the new online_count when they + * see prevent == 0. + */ + smp_wmb(); + coupled->prevent--; + /* Force cpus out of the prevent loop. */ + cpuidle_coupled_poke_others(cpu, coupled); + put_cpu(); +} + +/** + * cpuidle_coupled_cpu_notify - notifier called during hotplug transitions + * @nb: notifier block + * @action: hotplug transition + * @hcpu: target cpu number + * + * Called when a cpu is brought on or offline using hotplug. Updates the + * coupled cpu set appropriately + */ +static int cpuidle_coupled_cpu_notify(struct notifier_block *nb, + unsigned long action, void *hcpu) +{ + int cpu = (unsigned long)hcpu; + struct cpuidle_device *dev; + + mutex_lock(&cpuidle_lock); + + dev = per_cpu(cpuidle_devices, cpu); + if (!dev->coupled) + goto out; + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_UP_PREPARE: + case CPU_DOWN_PREPARE: + cpuidle_coupled_prevent_idle(dev->coupled); + break; + case CPU_ONLINE: + case CPU_DEAD: + cpuidle_coupled_update_online_cpus(dev->coupled); + /* Fall through */ + case CPU_UP_CANCELED: + case CPU_DOWN_FAILED: + cpuidle_coupled_allow_idle(dev->coupled); + break; + } + +out: + mutex_unlock(&cpuidle_lock); + return NOTIFY_OK; +} + +static struct notifier_block cpuidle_coupled_cpu_notifier = { + .notifier_call = cpuidle_coupled_cpu_notify, +}; + +static int __init cpuidle_coupled_init(void) +{ + return register_cpu_notifier(&cpuidle_coupled_cpu_notifier); +} +core_initcall(cpuidle_coupled_init); diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 4540672a2e1c..e81cfda295a5 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -171,7 +171,11 @@ int cpuidle_idle_call(void) trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu); trace_cpu_idle_rcuidle(next_state, dev->cpu); - entered_state = cpuidle_enter_state(dev, drv, next_state); + if (cpuidle_state_is_coupled(dev, drv, next_state)) + entered_state = cpuidle_enter_state_coupled(dev, drv, + next_state); + else + entered_state = cpuidle_enter_state(dev, drv, next_state); trace_power_end_rcuidle(dev->cpu); trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); @@ -407,9 +411,16 @@ static int __cpuidle_register_device(struct cpuidle_device *dev) if (ret) goto err_sysfs; + ret = cpuidle_coupled_register_device(dev); + if (ret) + goto err_coupled; + dev->registered = 1; return 0; +err_coupled: + cpuidle_remove_sysfs(cpu_dev); + wait_for_completion(&dev->kobj_unregister); err_sysfs: list_del(&dev->device_list); per_cpu(cpuidle_devices, dev->cpu) = NULL; @@ -464,6 +475,8 @@ void cpuidle_unregister_device(struct cpuidle_device *dev) wait_for_completion(&dev->kobj_unregister); per_cpu(cpuidle_devices, dev->cpu) = NULL; + cpuidle_coupled_unregister_device(dev); + cpuidle_resume_and_unlock(); module_put(cpuidle_driver->owner); diff --git a/drivers/cpuidle/cpuidle.h b/drivers/cpuidle/cpuidle.h index d8a3ccce8281..76e7f696ad8c 100644 --- a/drivers/cpuidle/cpuidle.h +++ b/drivers/cpuidle/cpuidle.h @@ -32,4 +32,34 @@ extern void cpuidle_remove_state_sysfs(struct cpuidle_device *device); extern int cpuidle_add_sysfs(struct device *dev); extern void cpuidle_remove_sysfs(struct device *dev); +#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED +bool cpuidle_state_is_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int state); +int cpuidle_enter_state_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int next_state); +int cpuidle_coupled_register_device(struct cpuidle_device *dev); +void cpuidle_coupled_unregister_device(struct cpuidle_device *dev); +#else +static inline bool cpuidle_state_is_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int state) +{ + return false; +} + +static inline int cpuidle_enter_state_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int next_state) +{ + return -1; +} + +static inline int cpuidle_coupled_register_device(struct cpuidle_device *dev) +{ + return 0; +} + +static inline void cpuidle_coupled_unregister_device(struct cpuidle_device *dev) +{ +} +#endif + #endif /* __DRIVER_CPUIDLE_H */ diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 6c26a3da0e03..603844835bc7 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -57,6 +57,7 @@ struct cpuidle_state { /* Idle State Flags */ #define CPUIDLE_FLAG_TIME_VALID (0x01) /* is residency time measurable? */ +#define CPUIDLE_FLAG_COUPLED (0x02) /* state applies to multiple cpus */ #define CPUIDLE_DRIVER_FLAGS_MASK (0xFFFF0000) @@ -100,6 +101,12 @@ struct cpuidle_device { struct list_head device_list; struct kobject kobj; struct completion kobj_unregister; + +#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED + int safe_state_index; + cpumask_t coupled_cpus; + struct cpuidle_coupled *coupled; +#endif }; DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices); -- cgit v1.2.3 From 20ff51a36b2cd25ee7eb3216b6d02b68935435ba Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 7 May 2012 17:57:42 -0700 Subject: cpuidle: coupled: add parallel barrier function Adds cpuidle_coupled_parallel_barrier, which can be used by coupled cpuidle state enter functions to handle resynchronization after determining if any cpu needs to abort. The normal use case will be: static bool abort_flag; static atomic_t abort_barrier; int arch_cpuidle_enter(struct cpuidle_device *dev, ...) { if (arch_turn_off_irq_controller()) { /* returns an error if an irq is pending and would be lost if idle continued and turned off power */ abort_flag = true; } cpuidle_coupled_parallel_barrier(dev, &abort_barrier); if (abort_flag) { /* One of the cpus didn't turn off it's irq controller */ arch_turn_on_irq_controller(); return -EINTR; } /* continue with idle */ ... } This will cause all cpus to abort idle together if one of them needs to abort. Reviewed-by: Santosh Shilimkar Tested-by: Santosh Shilimkar Reviewed-by: Kevin Hilman Tested-by: Kevin Hilman Signed-off-by: Colin Cross Signed-off-by: Len Brown --- drivers/cpuidle/coupled.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/cpuidle.h | 4 ++++ 2 files changed, 41 insertions(+) (limited to 'include') diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c index aab6bba8daec..2c9bf2692232 100644 --- a/drivers/cpuidle/coupled.c +++ b/drivers/cpuidle/coupled.c @@ -129,6 +129,43 @@ static DEFINE_PER_CPU(struct call_single_data, cpuidle_coupled_poke_cb); */ static cpumask_t cpuidle_coupled_poked_mask; +/** + * cpuidle_coupled_parallel_barrier - synchronize all online coupled cpus + * @dev: cpuidle_device of the calling cpu + * @a: atomic variable to hold the barrier + * + * No caller to this function will return from this function until all online + * cpus in the same coupled group have called this function. Once any caller + * has returned from this function, the barrier is immediately available for + * reuse. + * + * The atomic variable a must be initialized to 0 before any cpu calls + * this function, will be reset to 0 before any cpu returns from this function. + * + * Must only be called from within a coupled idle state handler + * (state.enter when state.flags has CPUIDLE_FLAG_COUPLED set). + * + * Provides full smp barrier semantics before and after calling. + */ +void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a) +{ + int n = dev->coupled->online_count; + + smp_mb__before_atomic_inc(); + atomic_inc(a); + + while (atomic_read(a) < n) + cpu_relax(); + + if (atomic_inc_return(a) == n * 2) { + atomic_set(a, 0); + return; + } + + while (atomic_read(a) > n) + cpu_relax(); +} + /** * cpuidle_state_is_coupled - check if a state is part of a coupled set * @dev: struct cpuidle_device for the current cpu diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 603844835bc7..5ab7183313ce 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -183,6 +183,10 @@ static inline int cpuidle_play_dead(void) {return -ENODEV; } #endif +#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED +void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a); +#endif + /****************************** * CPUIDLE GOVERNOR INTERFACE * ******************************/ -- cgit v1.2.3 From b9c7aff481f19dd655ae3ce6513817d625e2d47c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 29 May 2012 11:18:51 -0700 Subject: drivers/thermal/spear_thermal.c: add Device Tree probing capability SPEAr platforms now support DT and so must convert all drivers to support DT. This patch adds DT probing support for SPEAr thermal sensor driver and updates its documentation too. Also, as SPEAr is the only user of this driver and is only available with DT, make this an only DT driver. So, platform_data is completely removed and passed via DT now. Signed-off-by: Viresh Kumar Cc: Dan Carpenter Reviewed-by: Vincenzo Frascino Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- .../devicetree/bindings/thermal/spear-thermal.txt | 14 ++++++++++++ drivers/thermal/Kconfig | 1 + drivers/thermal/spear_thermal.c | 26 +++++++++++++--------- include/linux/platform_data/spear_thermal.h | 26 ---------------------- 4 files changed, 31 insertions(+), 36 deletions(-) create mode 100644 Documentation/devicetree/bindings/thermal/spear-thermal.txt delete mode 100644 include/linux/platform_data/spear_thermal.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/thermal/spear-thermal.txt b/Documentation/devicetree/bindings/thermal/spear-thermal.txt new file mode 100644 index 000000000000..93e3b67c102d --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/spear-thermal.txt @@ -0,0 +1,14 @@ +* SPEAr Thermal + +Required properties: +- compatible : "st,thermal-spear1340" +- reg : Address range of the thermal registers +- st,thermal-flags: flags used to enable thermal sensor + +Example: + + thermal@fc000000 { + compatible = "st,thermal-spear1340"; + reg = <0xfc000000 0x1000>; + st,thermal-flags = <0x7000>; + }; diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 514a691abea0..3ab2bd540b54 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -23,6 +23,7 @@ config SPEAR_THERMAL bool "SPEAr thermal sensor driver" depends on THERMAL depends on PLAT_SPEAR + depends on OF help Enable this to plug the SPEAr thermal sensor driver into the Linux thermal framework diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c index c2e32df3b164..ca40d36d0ba8 100644 --- a/drivers/thermal/spear_thermal.c +++ b/drivers/thermal/spear_thermal.c @@ -20,9 +20,9 @@ #include #include #include +#include #include #include -#include #include #define MD_FACTOR 1000 @@ -103,21 +103,20 @@ static int spear_thermal_probe(struct platform_device *pdev) { struct thermal_zone_device *spear_thermal = NULL; struct spear_thermal_dev *stdev; - struct spear_thermal_pdata *pdata; - int ret = 0; + struct device_node *np = pdev->dev.of_node; struct resource *stres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int ret = 0, val; + + if (!np || !of_property_read_u32(np, "st,thermal-flags", &val)) { + dev_err(&pdev->dev, "Failed: DT Pdata not passed\n"); + return -EINVAL; + } if (!stres) { dev_err(&pdev->dev, "memory resource missing\n"); return -ENODEV; } - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) { - dev_err(&pdev->dev, "platform data is NULL\n"); - return -EINVAL; - } - stdev = devm_kzalloc(&pdev->dev, sizeof(*stdev), GFP_KERNEL); if (!stdev) { dev_err(&pdev->dev, "kzalloc fail\n"); @@ -144,7 +143,7 @@ static int spear_thermal_probe(struct platform_device *pdev) goto put_clk; } - stdev->flags = pdata->thermal_flags; + stdev->flags = val; writel_relaxed(stdev->flags, stdev->thermal_base); spear_thermal = thermal_zone_device_register("spear_thermal", 0, @@ -189,6 +188,12 @@ static int spear_thermal_exit(struct platform_device *pdev) return 0; } +static const struct of_device_id spear_thermal_id_table[] = { + { .compatible = "st,thermal-spear1340" }, + {} +}; +MODULE_DEVICE_TABLE(of, spear_thermal_id_table); + static struct platform_driver spear_thermal_driver = { .probe = spear_thermal_probe, .remove = spear_thermal_exit, @@ -196,6 +201,7 @@ static struct platform_driver spear_thermal_driver = { .name = "spear_thermal", .owner = THIS_MODULE, .pm = &spear_thermal_pm_ops, + .of_match_table = of_match_ptr(spear_thermal_id_table), }, }; diff --git a/include/linux/platform_data/spear_thermal.h b/include/linux/platform_data/spear_thermal.h deleted file mode 100644 index 724f2e1cbbcb..000000000000 --- a/include/linux/platform_data/spear_thermal.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * SPEAr thermal driver platform data. - * - * Copyright (C) 2011-2012 ST Microelectronics - * Author: Vincenzo Frascino - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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. - * - */ -#ifndef SPEAR_THERMAL_H -#define SPEAR_THERMAL_H - -/* SPEAr Thermal Sensor Platform Data */ -struct spear_thermal_pdata { - /* flags used to enable thermal sensor */ - unsigned int thermal_flags; -}; - -#endif /* SPEAR_THERMAL_H */ -- cgit v1.2.3 From 1aad779fccdbb4d79af7b9de93dfd2bfe807e052 Mon Sep 17 00:00:00 2001 From: Ola Lilja Date: Thu, 24 May 2012 15:26:03 +0200 Subject: ALSA: pcm: Add debug-print helper function Adds a function getting the stream-name as a string for a specific stream. Signed-off-by: Ola Lilja Reviewed-by: Takashi Iwai Signed-off-by: Mark Brown --- include/sound/pcm.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 0d1112815be3..a55d5db7eb5a 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1073,4 +1073,15 @@ static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) const char *snd_pcm_format_name(snd_pcm_format_t format); +/** + * Get a string naming the direction of a stream + */ +static inline const char *snd_pcm_stream_str(struct snd_pcm_substream *substream) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return "Playback"; + else + return "Capture"; +} + #endif /* __SOUND_PCM_H */ -- cgit v1.2.3 From d7e7eb91551ad99244b989d71d092cb0375648fa Mon Sep 17 00:00:00 2001 From: Ola Lilja Date: Thu, 24 May 2012 15:26:25 +0200 Subject: ASoC: core: Add widget SND_SOC_DAPM_CLOCK_SUPPLY Adds a supply-widget variant for connection to the clock-framework. This widget-type corresponds to the variant for regulators. Signed-off-by: Ola Lilja Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 10 ++++++++++ sound/soc/soc-dapm.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index e3833d9f1914..05559e571d44 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -229,6 +229,10 @@ struct device; { .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \ .shift = wshift, .invert = winvert, \ .event = wevent, .event_flags = wflags} +#define SND_SOC_DAPM_CLOCK_SUPPLY(wname) \ +{ .id = snd_soc_dapm_clock_supply, .name = wname, \ + .reg = SND_SOC_NOPM, .event = dapm_clock_event, \ + .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD } /* generic widgets */ #define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \ @@ -245,6 +249,7 @@ struct device; .reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD } + /* dapm kcontrol types */ #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -327,6 +332,8 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); int dapm_regulator_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +int dapm_clock_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); /* dapm controls */ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, @@ -432,6 +439,7 @@ enum snd_soc_dapm_type { snd_soc_dapm_post, /* machine specific post widget - exec last */ snd_soc_dapm_supply, /* power/clock supply */ snd_soc_dapm_regulator_supply, /* external regulator */ + snd_soc_dapm_clock_supply, /* external clock */ snd_soc_dapm_aif_in, /* audio interface input */ snd_soc_dapm_aif_out, /* audio interface output */ snd_soc_dapm_siggen, /* signal generator */ @@ -537,6 +545,8 @@ struct snd_soc_dapm_widget { struct list_head dirty; int inputs; int outputs; + + struct clk *clk; }; struct snd_soc_dapm_update { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 90ee77d2409d..3bb7a6f058d0 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,7 @@ static int dapm_up_seq[] = { [snd_soc_dapm_pre] = 0, [snd_soc_dapm_supply] = 1, [snd_soc_dapm_regulator_supply] = 1, + [snd_soc_dapm_clock_supply] = 1, [snd_soc_dapm_micbias] = 2, [snd_soc_dapm_dai_link] = 2, [snd_soc_dapm_dai] = 3, @@ -92,6 +94,7 @@ static int dapm_down_seq[] = { [snd_soc_dapm_aif_out] = 10, [snd_soc_dapm_dai] = 10, [snd_soc_dapm_dai_link] = 11, + [snd_soc_dapm_clock_supply] = 12, [snd_soc_dapm_regulator_supply] = 12, [snd_soc_dapm_supply] = 12, [snd_soc_dapm_post] = 13, @@ -391,6 +394,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, case snd_soc_dapm_vmid: case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: case snd_soc_dapm_aif_in: case snd_soc_dapm_aif_out: case snd_soc_dapm_dai: @@ -764,6 +768,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, switch (widget->id) { case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: return 0; default: break; @@ -850,6 +855,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, switch (widget->id) { case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: return 0; default: break; @@ -996,6 +1002,24 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, } EXPORT_SYMBOL_GPL(dapm_regulator_event); +/* + * Handler for clock supply widget. + */ +int dapm_clock_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (!w->clk) + return -EIO; + + if (SND_SOC_DAPM_EVENT_ON(event)) { + return clk_enable(w->clk); + } else { + clk_disable(w->clk); + return 0; + } +} +EXPORT_SYMBOL_GPL(dapm_clock_event); + static int dapm_widget_power_check(struct snd_soc_dapm_widget *w) { if (w->power_checked) @@ -1487,6 +1511,7 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, switch (w->id) { case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: /* Supplies can't affect their outputs, only their inputs */ break; default: @@ -1587,6 +1612,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) break; case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: case snd_soc_dapm_micbias: if (d->target_bias_level < SND_SOC_BIAS_STANDBY) d->target_bias_level = SND_SOC_BIAS_STANDBY; @@ -1941,6 +1967,7 @@ static ssize_t dapm_widget_show(struct device *dev, case snd_soc_dapm_mixer_named_ctl: case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: if (w->name) count += sprintf(buf + count, "%s: %s\n", w->name, w->power ? "On":"Off"); @@ -2187,6 +2214,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_post: case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: case snd_soc_dapm_aif_in: case snd_soc_dapm_aif_out: case snd_soc_dapm_dai: @@ -2873,6 +2901,15 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, return NULL; } break; + case snd_soc_dapm_clock_supply: + w->clk = clk_get(dapm->dev, w->name); + if (IS_ERR(w->clk)) { + ret = PTR_ERR(w->clk); + dev_err(dapm->dev, "Failed to request %s: %d\n", + w->name, ret); + return NULL; + } + break; default: break; } @@ -2924,6 +2961,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, break; case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: + case snd_soc_dapm_clock_supply: w->power_check = dapm_supply_check_power; break; case snd_soc_dapm_dai: -- cgit v1.2.3 From bc92657a11c0982783979bbb84ceaf58ba222124 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 25 May 2012 18:22:11 -0600 Subject: ASoC: make snd_soc_dai_link more symmetrical Prior to this patch, the CPU side of a DAI link was specified using a single name. Often, this was the result of calling dev_name() on the device providing the DAI, but in the case of a CPU DAI driver that provided multiple DAIs, it needed to mix together both the device name and some device-relative name, in order to form a single globally unique name. However, the CODEC side of the DAI link was specified using separate fields for device (name or OF node) and device-relative DAI name. This patch allows the CPU side of a DAI link to be specified in the same way as the CODEC side, separating concepts of device and device-relative DAI name. I believe this will be important in multi-codec and/or dynamic PCM scenarios, where a single CPU driver provides multiple DAIs, while also booting using device tree, with accompanying desire not to hard-code the CPU side device's name into the original .cpu_dai_name field. Ideally, both the CPU DAI and CODEC DAI loops in soc_bind_dai_link() would now be identical. However, two things prevent that at present: 1) The need to save rtd->codec for the CODEC side, which means we have to search for the CODEC explicitly, and not just the CODEC side DAI. 2) Since we know the CODEC side DAI is part of a codec, and not just a standalone DAI, it's slightly more efficient to convert .codec_name/ .codec_of_node into a codec first, and then compare each DAI's .codec field, since this avoids strcmp() on each DAI's CODEC's name within the loop. However, the two loops are essentially semantically equivalent. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- include/sound/soc.h | 33 +++++++++++++++++++++++++++----- sound/soc/mxs/mxs-sgtl5000.c | 2 +- sound/soc/soc-core.c | 42 ++++++++++++++++++++++++++++++----------- sound/soc/tegra/tegra_alc5632.c | 6 +++--- sound/soc/tegra/tegra_wm8753.c | 6 +++--- sound/soc/tegra/tegra_wm8903.c | 6 +++--- sound/soc/tegra/trimslice.c | 6 +++--- 7 files changed, 72 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index c703871f5f65..23c4efbe13a6 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -785,13 +785,36 @@ struct snd_soc_dai_link { /* config - must be set by machine driver */ const char *name; /* Codec name */ const char *stream_name; /* Stream name */ - const char *codec_name; /* for multi-codec */ - const struct device_node *codec_of_node; - const char *platform_name; /* for multi-platform */ - const struct device_node *platform_of_node; + /* + * You MAY specify the link's CPU-side device, either by device name, + * or by DT/OF node, but not both. If this information is omitted, + * the CPU-side DAI is matched using .cpu_dai_name only, which hence + * must be globally unique. These fields are currently typically used + * only for codec to codec links, or systems using device tree. + */ + const char *cpu_name; + const struct device_node *cpu_of_node; + /* + * You MAY specify the DAI name of the CPU DAI. If this information is + * omitted, the CPU-side DAI is matched using .cpu_name/.cpu_of_node + * only, which only works well when that device exposes a single DAI. + */ const char *cpu_dai_name; - const struct device_node *cpu_dai_of_node; + /* + * You MUST specify the link's codec, either by device name, or by + * DT/OF node, but not both. + */ + const char *codec_name; + const struct device_node *codec_of_node; + /* You MUST specify the DAI name within the codec */ const char *codec_dai_name; + /* + * You MAY specify the link's platform/PCM/DMA driver, either by + * device name, or by DT/OF node, but not both. Some forms of link + * do not need a platform. + */ + const char *platform_name; + const struct device_node *platform_of_node; int be_id; /* optional ID for machine driver BE identification */ const struct snd_soc_pcm_stream *params; diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 3e6e8764b2e6..215113b05f7d 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -133,7 +133,7 @@ static int __devinit mxs_sgtl5000_probe_dt(struct platform_device *pdev) mxs_sgtl5000_dai[i].codec_name = NULL; mxs_sgtl5000_dai[i].codec_of_node = codec_np; mxs_sgtl5000_dai[i].cpu_dai_name = NULL; - mxs_sgtl5000_dai[i].cpu_dai_of_node = saif_np[i]; + mxs_sgtl5000_dai[i].cpu_of_node = saif_np[i]; mxs_sgtl5000_dai[i].platform_name = NULL; mxs_sgtl5000_dai[i].platform_of_node = saif_np[i]; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b37ee8077ed1..ec8350570346 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -812,13 +812,15 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) /* Find CPU DAI from registered DAIs*/ list_for_each_entry(cpu_dai, &dai_list, list) { - if (dai_link->cpu_dai_of_node) { - if (cpu_dai->dev->of_node != dai_link->cpu_dai_of_node) - continue; - } else { - if (strcmp(cpu_dai->name, dai_link->cpu_dai_name)) - continue; - } + if (dai_link->cpu_of_node && + (cpu_dai->dev->of_node != dai_link->cpu_of_node)) + continue; + if (dai_link->cpu_name && + strcmp(dev_name(cpu_dai->dev), dai_link->cpu_name)) + continue; + if (dai_link->cpu_dai_name && + strcmp(cpu_dai->name, dai_link->cpu_dai_name)) + continue; rtd->cpu_dai = cpu_dai; } @@ -3346,6 +3348,12 @@ int snd_soc_register_card(struct snd_soc_card *card) link->name); return -EINVAL; } + /* Codec DAI name must be specified */ + if (!link->codec_dai_name) { + dev_err(card->dev, "codec_dai_name not set for %s\n", + link->name); + return -EINVAL; + } /* * Platform may be specified by either name or OF node, but @@ -3358,12 +3366,24 @@ int snd_soc_register_card(struct snd_soc_card *card) } /* - * CPU DAI must be specified by 1 of name or OF node, - * not both or neither. + * CPU device may be specified by either name or OF node, but + * can be left unspecified, and will be matched based on DAI + * name alone.. + */ + if (link->cpu_name && link->cpu_of_node) { + dev_err(card->dev, + "Neither/both cpu name/of_node are set for %s\n", + link->name); + return -EINVAL; + } + /* + * At least one of CPU DAI name or CPU device name/node must be + * specified */ - if (!!link->cpu_dai_name == !!link->cpu_dai_of_node) { + if (!link->cpu_dai_name && + !(link->cpu_name || link->cpu_of_node)) { dev_err(card->dev, - "Neither/both cpu_dai name/of_node are set for %s\n", + "Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", link->name); return -EINVAL; } diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c index 15669570ae31..417b09b83fdf 100644 --- a/sound/soc/tegra/tegra_alc5632.c +++ b/sound/soc/tegra/tegra_alc5632.c @@ -197,16 +197,16 @@ static __devinit int tegra_alc5632_probe(struct platform_device *pdev) goto err; } - tegra_alc5632_dai.cpu_dai_of_node = of_parse_phandle( + tegra_alc5632_dai.cpu_of_node = of_parse_phandle( pdev->dev.of_node, "nvidia,i2s-controller", 0); - if (!tegra_alc5632_dai.cpu_dai_of_node) { + if (!tegra_alc5632_dai.cpu_of_node) { dev_err(&pdev->dev, "Property 'nvidia,i2s-controller' missing or invalid\n"); ret = -EINVAL; goto err; } - tegra_alc5632_dai.platform_of_node = tegra_alc5632_dai.cpu_dai_of_node; + tegra_alc5632_dai.platform_of_node = tegra_alc5632_dai.cpu_of_node; ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev); if (ret) diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c index 4e77026807a2..02bd5a8e8544 100644 --- a/sound/soc/tegra/tegra_wm8753.c +++ b/sound/soc/tegra/tegra_wm8753.c @@ -157,9 +157,9 @@ static __devinit int tegra_wm8753_driver_probe(struct platform_device *pdev) goto err; } - tegra_wm8753_dai.cpu_dai_of_node = of_parse_phandle( + tegra_wm8753_dai.cpu_of_node = of_parse_phandle( pdev->dev.of_node, "nvidia,i2s-controller", 0); - if (!tegra_wm8753_dai.cpu_dai_of_node) { + if (!tegra_wm8753_dai.cpu_of_node) { dev_err(&pdev->dev, "Property 'nvidia,i2s-controller' missing or invalid\n"); ret = -EINVAL; @@ -167,7 +167,7 @@ static __devinit int tegra_wm8753_driver_probe(struct platform_device *pdev) } tegra_wm8753_dai.platform_of_node = - tegra_wm8753_dai.cpu_dai_of_node; + tegra_wm8753_dai.cpu_of_node; ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); if (ret) diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index b75e0e8db1d0..1fd71e5a9eb9 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -331,9 +331,9 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) } tegra_wm8903_dai.cpu_dai_name = NULL; - tegra_wm8903_dai.cpu_dai_of_node = of_parse_phandle(np, + tegra_wm8903_dai.cpu_of_node = of_parse_phandle(np, "nvidia,i2s-controller", 0); - if (!tegra_wm8903_dai.cpu_dai_of_node) { + if (!tegra_wm8903_dai.cpu_of_node) { dev_err(&pdev->dev, "Property 'nvidia,i2s-controller' missing or invalid\n"); ret = -EINVAL; @@ -342,7 +342,7 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) tegra_wm8903_dai.platform_name = NULL; tegra_wm8903_dai.platform_of_node = - tegra_wm8903_dai.cpu_dai_of_node; + tegra_wm8903_dai.cpu_of_node; } else { card->dapm_routes = harmony_audio_map; card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map); diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c index 4a8d5b672c9f..5815430e8521 100644 --- a/sound/soc/tegra/trimslice.c +++ b/sound/soc/tegra/trimslice.c @@ -162,9 +162,9 @@ static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev) } trimslice_tlv320aic23_dai.cpu_dai_name = NULL; - trimslice_tlv320aic23_dai.cpu_dai_of_node = of_parse_phandle( + trimslice_tlv320aic23_dai.cpu_of_node = of_parse_phandle( pdev->dev.of_node, "nvidia,i2s-controller", 0); - if (!trimslice_tlv320aic23_dai.cpu_dai_of_node) { + if (!trimslice_tlv320aic23_dai.cpu_of_node) { dev_err(&pdev->dev, "Property 'nvidia,i2s-controller' missing or invalid\n"); ret = -EINVAL; @@ -173,7 +173,7 @@ static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev) trimslice_tlv320aic23_dai.platform_name = NULL; trimslice_tlv320aic23_dai.platform_of_node = - trimslice_tlv320aic23_dai.cpu_dai_of_node; + trimslice_tlv320aic23_dai.cpu_of_node; } ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev); -- cgit v1.2.3 From 6c9d8cf6372ed2995a3d982f5c1f966e842101cc Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Thu, 31 May 2012 15:18:01 +0100 Subject: ASoC: core: Add single controls with specified range of values Control type added for cases where a specific range of values within a register are required for control. Added convenience macros: SOC_SINGLE_RANGE SOC_SINGLE_RANGE_TLV Added accessor implementations: snd_soc_info_volsw_range snd_soc_put_volsw_range snd_soc_get_volsw_range Signed-off-by: Michal Hajduk Signed-off-by: Adam Thomson Signed-off-by: Mark Brown --- include/sound/soc.h | 23 ++++++++++++ sound/soc/soc-core.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 23c4efbe13a6..e4348d25fca3 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -47,6 +47,13 @@ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ .put = snd_soc_put_volsw, \ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } +#define SOC_SINGLE_RANGE(xname, xreg, xshift, xmin, xmax, xinvert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .info = snd_soc_info_volsw_range, .get = snd_soc_get_volsw_range, \ + .put = snd_soc_put_volsw_range, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .shift = xshift, .min = xmin,\ + .max = xmax, .platform_max = xmax, .invert = xinvert} } #define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ @@ -67,6 +74,16 @@ {.reg = xreg, .rreg = xreg, \ .shift = xshift, .rshift = xshift, \ .max = xmax, .min = xmin} } +#define SOC_SINGLE_RANGE_TLV(xname, xreg, xshift, xmin, xmax, xinvert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw_range, \ + .get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .shift = xshift, .min = xmin,\ + .max = xmax, .platform_max = xmax, .invert = xinvert} } #define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ @@ -460,6 +477,12 @@ int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); int snd_soc_limit_volume(struct snd_soc_codec *codec, const char *name, int max); int snd_soc_bytes_info(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ec8350570346..3d803f3cd272 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2791,6 +2791,104 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8); +/** + * snd_soc_info_volsw_range - single mixer info callback with range. + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information, within a range, about a single + * mixer control. + * + * returns 0 for success. + */ +int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int platform_max; + int min = mc->min; + + if (!mc->platform_max) + mc->platform_max = mc->max; + platform_max = mc->platform_max; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = platform_max - min; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range); + +/** + * snd_soc_put_volsw_range - single mixer put value callback with range. + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to set the value, within a range, for a single mixer control. + * + * Returns 0 for success. + */ +int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + int min = mc->min; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val, val_mask; + + val = ((ucontrol->value.integer.value[0] + min) & mask); + if (invert) + val = max - val; + val_mask = mask << shift; + val = val << shift; + + return snd_soc_update_bits_locked(codec, reg, val_mask, val); +} +EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range); + +/** + * snd_soc_get_volsw_range - single mixer get callback with range + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value, within a range, of a single mixer control. + * + * Returns 0 for success. + */ +int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + int min = mc->min; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, reg) >> shift) & mask; + if (invert) + ucontrol->value.integer.value[0] = + max - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[0] = + ucontrol->value.integer.value[0] - min; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); + /** * snd_soc_limit_volume - Set new limit to an existing volume control. * -- cgit v1.2.3 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') 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 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') 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 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') 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 From 275c58d77062bbb85dbeb3843ba04f34aa50cf8e Mon Sep 17 00:00:00 2001 From: Toshi Kani Date: Wed, 23 May 2012 20:25:19 -0600 Subject: ACPI: Add an interface to evaluate _OST Added acpi_evaluate_hotplug_opt(). All ACPI hotplug handlers must call this function when evaluating _OST for hotplug operations. If the platform does not support _OST, this function returns AE_NOT_FOUND and has no effect on the platform. ACPI_HOTPLUG_OST is defined when all relevant ACPI hotplug operations, such as CPU, memory and container hotplug, are enabled. This assures consistent behavior among the hotplug operations with regarding the _OST support. When ACPI_HOTPLUG_OST is not defined, this function is a no-op. ACPI PCI hotplug is not enhanced to support _OST at this time since it is a legacy method being replaced by PCIe native hotplug. _OST support for ACPI PCI hotplug may be added in future if necessary. Some platforms may require the OS to support _OST in order to support ACPI hotplug operations. For example, if a platform has the management console where user can request a hotplug operation from, this _OST support would be required for the management console to show the result of the hotplug request to user. Added macro definitions of _OST source events and status codes. Also renamed OSC_SB_CPUHP_OST_SUPPORT to OSC_SB_HOTPLUG_OST_SUPPORT since this _OSC bit is not specific to CPU hotplug. This bit is defined in Table 6-147 of ACPI 5.0 as follows. Bits: 3 Field Name: Insertion / Ejection _OST Processing Support Definition: This bit is set if OSPM will evaluate the _OST object defined under a device when processing insertion and ejection source event codes. Signed-off-by: Toshi Kani Signed-off-by: Len Brown --- drivers/acpi/utils.c | 42 ++++++++++++++++++++++++++++++++++++++++++ include/acpi/acpi_bus.h | 3 +++ include/linux/acpi.h | 40 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 84 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index adbbc1c80a26..3e87c9c538aa 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -412,3 +412,45 @@ out: return status; } EXPORT_SYMBOL(acpi_get_physical_device_location); + +/** + * acpi_evaluate_hotplug_ost: Evaluate _OST for hotplug operations + * @handle: ACPI device handle + * @source_event: source event code + * @status_code: status code + * @status_buf: optional detailed information (NULL if none) + * + * Evaluate _OST for hotplug operations. All ACPI hotplug handlers + * must call this function when evaluating _OST for hotplug operations. + * When the platform does not support _OST, this function has no effect. + */ +acpi_status +acpi_evaluate_hotplug_ost(acpi_handle handle, u32 source_event, + u32 status_code, struct acpi_buffer *status_buf) +{ +#ifdef ACPI_HOTPLUG_OST + union acpi_object params[3] = { + {.type = ACPI_TYPE_INTEGER,}, + {.type = ACPI_TYPE_INTEGER,}, + {.type = ACPI_TYPE_BUFFER,} + }; + struct acpi_object_list arg_list = {3, params}; + acpi_status status; + + params[0].integer.value = source_event; + params[1].integer.value = status_code; + if (status_buf != NULL) { + params[2].buffer.pointer = status_buf->pointer; + params[2].buffer.length = status_buf->length; + } else { + params[2].buffer.pointer = NULL; + params[2].buffer.length = 0; + } + + status = acpi_evaluate_object(handle, "_OST", &arg_list, NULL); + return status; +#else + return AE_OK; +#endif +} +EXPORT_SYMBOL(acpi_evaluate_hotplug_ost); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index b0d62820ada1..1139f3a01209 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -50,6 +50,9 @@ acpi_evaluate_reference(acpi_handle handle, acpi_string pathname, struct acpi_object_list *arguments, struct acpi_handle_list *list); +acpi_status +acpi_evaluate_hotplug_ost(acpi_handle handle, u32 source_event, + u32 status_code, struct acpi_buffer *status_buf); struct acpi_pld { unsigned int revision:7; /* 0 */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index f421dd84f29d..b2b4d2ad7103 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -277,7 +277,7 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); #define OSC_SB_PAD_SUPPORT 1 #define OSC_SB_PPC_OST_SUPPORT 2 #define OSC_SB_PR3_SUPPORT 4 -#define OSC_SB_CPUHP_OST_SUPPORT 8 +#define OSC_SB_HOTPLUG_OST_SUPPORT 8 #define OSC_SB_APEI_SUPPORT 16 extern bool osc_sb_apei_support_acked; @@ -309,6 +309,44 @@ extern bool osc_sb_apei_support_acked; extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req); + +/* Enable _OST when all relevant hotplug operations are enabled */ +#if defined(CONFIG_ACPI_HOTPLUG_CPU) && \ + (defined(CONFIG_ACPI_HOTPLUG_MEMORY) || \ + defined(CONFIG_ACPI_HOTPLUG_MEMORY_MODULE)) && \ + (defined(CONFIG_ACPI_CONTAINER) || \ + defined(CONFIG_ACPI_CONTAINER_MODULE)) +#define ACPI_HOTPLUG_OST +#endif + +/* _OST Source Event Code (OSPM Action) */ +#define ACPI_OST_EC_OSPM_SHUTDOWN 0x100 +#define ACPI_OST_EC_OSPM_EJECT 0x103 +#define ACPI_OST_EC_OSPM_INSERTION 0x200 + +/* _OST General Processing Status Code */ +#define ACPI_OST_SC_SUCCESS 0x0 +#define ACPI_OST_SC_NON_SPECIFIC_FAILURE 0x1 +#define ACPI_OST_SC_UNRECOGNIZED_NOTIFY 0x2 + +/* _OST OS Shutdown Processing (0x100) Status Code */ +#define ACPI_OST_SC_OS_SHUTDOWN_DENIED 0x80 +#define ACPI_OST_SC_OS_SHUTDOWN_IN_PROGRESS 0x81 +#define ACPI_OST_SC_OS_SHUTDOWN_COMPLETED 0x82 +#define ACPI_OST_SC_OS_SHUTDOWN_NOT_SUPPORTED 0x83 + +/* _OST Ejection Request (0x3, 0x103) Status Code */ +#define ACPI_OST_SC_EJECT_NOT_SUPPORTED 0x80 +#define ACPI_OST_SC_DEVICE_IN_USE 0x81 +#define ACPI_OST_SC_DEVICE_BUSY 0x82 +#define ACPI_OST_SC_EJECT_DEPENDENCY_BUSY 0x83 +#define ACPI_OST_SC_EJECT_IN_PROGRESS 0x84 + +/* _OST Insertion Request (0x200) Status Code */ +#define ACPI_OST_SC_INSERT_IN_PROGRESS 0x80 +#define ACPI_OST_SC_DRIVER_LOAD_FAILURE 0x81 +#define ACPI_OST_SC_INSERT_NOT_SUPPORTED 0x82 + extern void acpi_early_init(void); extern int acpi_nvs_register(__u64 start, __u64 size); -- cgit v1.2.3 From c4753e57b78b213f2384fa0dbafa348b087114fa Mon Sep 17 00:00:00 2001 From: Toshi Kani Date: Wed, 23 May 2012 20:25:20 -0600 Subject: ACPI: Add _OST support for sysfs eject Changed acpi_bus_hot_remove_device() to support _OST. This function is also changed to global so that it can be called from hotplug notify handlers to perform hot-remove operation. Changed acpi_eject_store(), which is the sysfs eject handler. It checks eject_pending to see if the request was originated from ACPI eject notification. If not, it calls _OST(0x103,84,) per Figure 6-37 in ACPI 5.0 spec. Added eject_pending bit to acpi_device_flags. This bit is set when the kernel has received an ACPI eject notification, but does not initiate its hot-remove operation by itself. Added struct acpi_eject_event. This structure is used to pass extended information to acpi_bus_hot_remove_device(), which has a single argument to support asynchronous call Signed-off-by: Toshi Kani Signed-off-by: Len Brown --- drivers/acpi/scan.c | 58 +++++++++++++++++++++++++++++++++++++++++-------- include/acpi/acpi_bus.h | 9 +++++++- 2 files changed, 57 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 85cbfdccc97c..bea3ab6b524f 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -83,19 +83,29 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha } static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); -static void acpi_bus_hot_remove_device(void *context) +/** + * acpi_bus_hot_remove_device: hot-remove a device and its children + * @context: struct acpi_eject_event pointer (freed in this func) + * + * Hot-remove a device and its children. This function frees up the + * memory space passed by arg context, so that the caller may call + * this function asynchronously through acpi_os_hotplug_execute(). + */ +void acpi_bus_hot_remove_device(void *context) { + struct acpi_eject_event *ej_event = (struct acpi_eject_event *) context; struct acpi_device *device; - acpi_handle handle = context; + acpi_handle handle = ej_event->handle; struct acpi_object_list arg_list; union acpi_object arg; acpi_status status = AE_OK; + u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ if (acpi_bus_get_device(handle, &device)) - return; + goto err_out; if (!device) - return; + goto err_out; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Hot-removing device %s...\n", dev_name(&device->dev))); @@ -103,7 +113,7 @@ static void acpi_bus_hot_remove_device(void *context) if (acpi_bus_trim(device, 1)) { printk(KERN_ERR PREFIX "Removing device failed\n"); - return; + goto err_out; } /* power off device */ @@ -129,10 +139,21 @@ static void acpi_bus_hot_remove_device(void *context) * TBD: _EJD support. */ status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); - if (ACPI_FAILURE(status)) - printk(KERN_WARNING PREFIX - "Eject device failed\n"); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) + printk(KERN_WARNING PREFIX + "Eject device failed\n"); + goto err_out; + } + + kfree(context); + return; +err_out: + /* Inform firmware the hot-remove operation has completed w/ error */ + (void) acpi_evaluate_hotplug_ost(handle, + ej_event->event, ost_code, NULL); + kfree(context); return; } @@ -144,6 +165,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, acpi_status status; acpi_object_type type = 0; struct acpi_device *acpi_device = to_acpi_device(d); + struct acpi_eject_event *ej_event; if ((!count) || (buf[0] != '1')) { return -EINVAL; @@ -160,7 +182,25 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, goto err; } - acpi_os_hotplug_execute(acpi_bus_hot_remove_device, acpi_device->handle); + ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); + if (!ej_event) { + ret = -ENOMEM; + goto err; + } + + ej_event->handle = acpi_device->handle; + if (acpi_device->flags.eject_pending) { + /* event originated from ACPI eject notification */ + ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; + acpi_device->flags.eject_pending = 0; + } else { + /* event originated from user */ + ej_event->event = ACPI_OST_EC_OSPM_EJECT; + (void) acpi_evaluate_hotplug_ost(ej_event->handle, + ej_event->event, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + } + + acpi_os_hotplug_execute(acpi_bus_hot_remove_device, (void *)ej_event); err: return ret; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 1139f3a01209..62eb514f8e3a 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -182,7 +182,8 @@ struct acpi_device_flags { u32 suprise_removal_ok:1; u32 power_manageable:1; u32 performance_manageable:1; - u32 reserved:24; + u32 eject_pending:1; + u32 reserved:23; }; /* File System */ @@ -334,6 +335,11 @@ struct acpi_bus_event { u32 data; }; +struct acpi_eject_event { + acpi_handle handle; + u32 event; +}; + extern struct kobject *acpi_kobj; extern int acpi_bus_generate_netlink_event(const char*, const char*, u8, int); void acpi_bus_private_data_handler(acpi_handle, void *); @@ -371,6 +377,7 @@ int acpi_bus_register_driver(struct acpi_driver *driver); void acpi_bus_unregister_driver(struct acpi_driver *driver); int acpi_bus_add(struct acpi_device **child, struct acpi_device *parent, acpi_handle handle, int type); +void acpi_bus_hot_remove_device(void *context); int acpi_bus_trim(struct acpi_device *start, int rmdevice); int acpi_bus_start(struct acpi_device *device); acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle * ejd); -- cgit v1.2.3 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') 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 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') 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 From ab73b751303bc60d7d9fba875c958dedfe14754c Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 10 Apr 2012 12:51:52 +0200 Subject: NFC: Export LLCP general bytes getter Signed-off-by: Samuel Ortiz --- include/net/nfc/nfc.h | 1 + net/nfc/core.c | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index b7ca4a2a1d72..3116f923f607 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -188,6 +188,7 @@ struct sk_buff *nfc_alloc_recv_skb(unsigned int size, gfp_t gfp); int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gt, u8 gt_len); +u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len); int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets, int ntargets); diff --git a/net/nfc/core.c b/net/nfc/core.c index 9f6ce011d35d..f5a43f701a9e 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -447,6 +447,14 @@ int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gb, u8 gb_len) } EXPORT_SYMBOL(nfc_set_remote_general_bytes); +u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len) +{ + pr_debug("dev_name=%s\n", dev_name(&dev->dev)); + + return nfc_llcp_general_bytes(dev, gb_len); +} +EXPORT_SYMBOL(nfc_get_local_general_bytes); + /** * nfc_alloc_send_skb - allocate a skb for data exchange responses * -- cgit v1.2.3 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') 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 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') 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 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') 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 From be9ae4ce4ee66e211815122ab4f41913efed4fec Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 16 May 2012 15:55:48 +0200 Subject: NFC: Introduce target mode tx ops And rename the initiator mode data exchange ops for consistency sake. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 8 ++++---- include/net/nfc/nfc.h | 3 ++- net/nfc/core.c | 37 ++++++++++++++++++++----------------- net/nfc/hci/core.c | 8 ++++---- net/nfc/nci/core.c | 8 ++++---- 5 files changed, 34 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index c6b9bc5ac6e6..fd94c6f5d6a8 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1691,9 +1691,9 @@ error: return 0; } -static int pn533_data_exchange(struct nfc_dev *nfc_dev, - struct nfc_target *target, struct sk_buff *skb, - data_exchange_cb_t cb, void *cb_context) +static int pn533_transceive(struct nfc_dev *nfc_dev, + struct nfc_target *target, struct sk_buff *skb, + data_exchange_cb_t cb, void *cb_context) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); struct pn533_frame *out_frame, *in_frame; @@ -1853,7 +1853,7 @@ struct nfc_ops pn533_nfc_ops = { .stop_poll = pn533_stop_poll, .activate_target = pn533_activate_target, .deactivate_target = pn533_deactivate_target, - .data_exchange = pn533_data_exchange, + .im_transceive = pn533_transceive, }; static int pn533_probe(struct usb_interface *interface, diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index a6a7b49a3e2d..45c4c970575c 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -63,9 +63,10 @@ struct nfc_ops { u32 protocol); void (*deactivate_target)(struct nfc_dev *dev, struct nfc_target *target); - int (*data_exchange)(struct nfc_dev *dev, struct nfc_target *target, + int (*im_transceive)(struct nfc_dev *dev, struct nfc_target *target, struct sk_buff *skb, data_exchange_cb_t cb, void *cb_context); + int (*tm_send)(struct nfc_dev *dev, struct sk_buff *skb); int (*check_presence)(struct nfc_dev *dev, struct nfc_target *target); }; diff --git a/net/nfc/core.c b/net/nfc/core.c index 722a0c76c669..76c1e207d297 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -413,27 +413,30 @@ int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb, goto error; } - if (dev->active_target == NULL) { - rc = -ENOTCONN; - kfree_skb(skb); - goto error; - } + if (dev->rf_mode == NFC_RF_INITIATOR && dev->active_target != NULL) { + if (dev->active_target->idx != target_idx) { + rc = -EADDRNOTAVAIL; + kfree_skb(skb); + goto error; + } - if (dev->active_target->idx != target_idx) { - rc = -EADDRNOTAVAIL; + if (dev->ops->check_presence) + del_timer_sync(&dev->check_pres_timer); + + rc = dev->ops->im_transceive(dev, dev->active_target, skb, cb, + cb_context); + + if (!rc && dev->ops->check_presence) + mod_timer(&dev->check_pres_timer, jiffies + + msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); + } else if (dev->rf_mode == NFC_RF_TARGET && dev->ops->tm_send != NULL) { + rc = dev->ops->tm_send(dev, skb); + } else { + rc = -ENOTCONN; kfree_skb(skb); goto error; } - if (dev->ops->check_presence) - del_timer_sync(&dev->check_pres_timer); - - rc = dev->ops->data_exchange(dev, dev->active_target, skb, cb, - cb_context); - - if (!rc && dev->ops->check_presence) - mod_timer(&dev->check_pres_timer, jiffies + - msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS)); error: device_unlock(&dev->dev); @@ -727,7 +730,7 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops, struct nfc_dev *dev; if (!ops->start_poll || !ops->stop_poll || !ops->activate_target || - !ops->deactivate_target || !ops->data_exchange) + !ops->deactivate_target || !ops->im_transceive) return NULL; if (!supported_protocols) diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 281f3a3bec00..a8b0b71e8f86 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -512,9 +512,9 @@ static void hci_deactivate_target(struct nfc_dev *nfc_dev, { } -static int hci_data_exchange(struct nfc_dev *nfc_dev, struct nfc_target *target, - struct sk_buff *skb, data_exchange_cb_t cb, - void *cb_context) +static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, + struct sk_buff *skb, data_exchange_cb_t cb, + void *cb_context) { struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); int r; @@ -580,7 +580,7 @@ static struct nfc_ops hci_nfc_ops = { .stop_poll = hci_stop_poll, .activate_target = hci_activate_target, .deactivate_target = hci_deactivate_target, - .data_exchange = hci_data_exchange, + .im_transceive = hci_transceive, .check_presence = hci_check_presence, }; diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 0f718982f808..766a02b1dfa1 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -522,9 +522,9 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev, } } -static int nci_data_exchange(struct nfc_dev *nfc_dev, struct nfc_target *target, - struct sk_buff *skb, - data_exchange_cb_t cb, void *cb_context) +static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, + struct sk_buff *skb, + data_exchange_cb_t cb, void *cb_context) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); int rc; @@ -557,7 +557,7 @@ static struct nfc_ops nci_nfc_ops = { .stop_poll = nci_stop_poll, .activate_target = nci_activate_target, .deactivate_target = nci_deactivate_target, - .data_exchange = nci_data_exchange, + .im_transceive = nci_transceive, }; /* ---- Interface to NCI drivers ---- */ -- cgit v1.2.3 From 73167ced31d15c04e57b9e0885ac05675e9195a4 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 31 May 2012 00:05:50 +0200 Subject: NFC: Introduce target mode rx data callback This routine will be called by drivers whenever they receive data in target mode. This should be unexpected events and as such should be handled by a standalone API (i.e. not as a callback pointer from an existing API). Signed-off-by: Samuel Ortiz --- include/net/nfc/nfc.h | 1 + net/nfc/core.c | 12 ++++++++++++ net/nfc/llcp/llcp.c | 15 +++++++++++++++ net/nfc/nfc.h | 7 +++++++ 4 files changed, 35 insertions(+) (limited to 'include') diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 45c4c970575c..180964b954ab 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -202,5 +202,6 @@ int nfc_dep_link_is_up(struct nfc_dev *dev, u32 target_idx, 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); +int nfc_tm_data_received(struct nfc_dev *dev, struct sk_buff *skb); #endif /* __NET_NFC_H */ diff --git a/net/nfc/core.c b/net/nfc/core.c index 76c1e207d297..6a3799eebc30 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -462,6 +462,18 @@ u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len) } EXPORT_SYMBOL(nfc_get_local_general_bytes); +int nfc_tm_data_received(struct nfc_dev *dev, struct sk_buff *skb) +{ + /* Only LLCP target mode for now */ + if (dev->dep_link_up == false) { + kfree_skb(skb); + return -ENOLINK; + } + + return nfc_llcp_data_received(dev, skb); +} +EXPORT_SYMBOL(nfc_tm_data_received); + int nfc_tm_activated(struct nfc_dev *dev, u32 protocol, u8 comm_mode, u8 *gb, size_t gb_len) { diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index 5f7aa3f632fb..5705e6dffb32 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -937,6 +937,21 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err) return; } +int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb) +{ + struct nfc_llcp_local *local; + + local = nfc_llcp_find_local(dev); + if (local == NULL) + return -ENODEV; + + local->rx_pending = skb_get(skb); + del_timer(&local->link_timer); + queue_work(local->rx_wq, &local->rx_work); + + return 0; +} + void nfc_llcp_mac_is_down(struct nfc_dev *dev) { struct nfc_llcp_local *local; diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index cd9fcbe57464..c5e42b79a418 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -55,6 +55,7 @@ int nfc_llcp_register_device(struct nfc_dev *dev); void nfc_llcp_unregister_device(struct nfc_dev *dev); int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len); u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len); +int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb); int __init nfc_llcp_init(void); void nfc_llcp_exit(void); @@ -90,6 +91,12 @@ static inline u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *gb_len) return NULL; } +static inline int nfc_llcp_data_received(struct nfc_dev *dev, + struct sk_buff *skb) +{ + return 0; +} + static inline int nfc_llcp_init(void) { return 0; -- cgit v1.2.3 From 2827011f666e157f3307d55070a75e1d1110b194 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Thu, 17 May 2012 21:14:09 -0700 Subject: Bluetooth: Fix early return from l2cap_chan_del This fixes a regression from commit 2ead70b8390d199ca04cd35311b51f5f3676079e that is present in all kernels starting at v3.0. When L2CAP information was moved to struct l2cap_chan, a check was added to l2cap_chan_del to avoid certain cleanup operations when ERTM or streaming mode had not yet been initialized. The logic in the check did not take in to account that chan->conf_state is set to 0 in l2cap_chan_ready, so l2cap_chan_del failed to cancel timers and leaked memory any time the ERTM queues or lists were not empty. This change makes sure that l2cap_chan_del only returns early if ERTM initialization was not performed. Signed-off-by: Mat Martineau Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 1c7d1cd5e679..452fcc4c0fff 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -597,6 +597,7 @@ enum { CONF_EWS_RECV, CONF_LOC_CONF_PEND, CONF_REM_CONF_PEND, + CONF_NOT_COMPLETE, }; #define L2CAP_CONF_MAX_CONF_REQ 2 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 078bf805cd97..d9f215f3f8e9 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -392,6 +392,9 @@ struct l2cap_chan *l2cap_chan_create(void) atomic_set(&chan->refcnt, 1); + /* This flag is cleared in l2cap_chan_ready() */ + set_bit(CONF_NOT_COMPLETE, &chan->conf_state); + BT_DBG("chan %p", chan); return chan; @@ -509,8 +512,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) release_sock(sk); - if (!(test_bit(CONF_OUTPUT_DONE, &chan->conf_state) && - test_bit(CONF_INPUT_DONE, &chan->conf_state))) + if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state)) return; skb_queue_purge(&chan->tx_q); @@ -923,6 +925,7 @@ static void l2cap_chan_ready(struct l2cap_chan *chan) BT_DBG("sk %p, parent %p", sk, parent); + /* This clears all conf flags, including CONF_NOT_COMPLETE */ chan->conf_state = 0; __clear_chan_timer(chan); -- cgit v1.2.3 From f5dbb0772df3feb2bb5eda8a9f0e0acdeb25653f Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Thu, 17 May 2012 20:53:38 -0700 Subject: Bluetooth: Remove receive code that has been superceded This deletes the receive code that had handlers for each frame type at the top level, and then had logic to determine the receive state within each handler. Signed-off-by: Mat Martineau Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 8 - net/bluetooth/l2cap_core.c | 492 ------------------------------------------ 2 files changed, 500 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 452fcc4c0fff..7d1da5a7d11e 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -419,11 +419,6 @@ struct l2cap_seq_list { #define L2CAP_SEQ_LIST_CLEAR 0xFFFF #define L2CAP_SEQ_LIST_TAIL 0x8000 -struct srej_list { - __u16 tx_seq; - struct list_head list; -}; - struct l2cap_chan { struct sock *sk; @@ -475,14 +470,12 @@ struct l2cap_chan { __u16 expected_ack_seq; __u16 expected_tx_seq; __u16 buffer_seq; - __u16 buffer_seq_srej; __u16 srej_save_reqseq; __u16 last_acked_seq; __u16 frames_sent; __u16 unacked_frames; __u8 retry_count; __u16 srej_queue_next; - __u8 num_acked; __u16 sdu_len; struct sk_buff *sdu; struct sk_buff *sdu_last_frag; @@ -515,7 +508,6 @@ struct l2cap_chan { struct sk_buff_head srej_q; struct l2cap_seq_list srej_list; struct l2cap_seq_list retrans_list; - struct list_head srej_l; struct list_head list; struct list_head global_l; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 0a195ab4a385..d795d15cadf9 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -534,8 +534,6 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) skb_queue_purge(&chan->tx_q); if (chan->mode == L2CAP_MODE_ERTM) { - struct srej_list *l, *tmp; - __clear_retrans_timer(chan); __clear_monitor_timer(chan); __clear_ack_timer(chan); @@ -544,10 +542,6 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) l2cap_seq_list_free(&chan->srej_list); l2cap_seq_list_free(&chan->retrans_list); - list_for_each_entry_safe(l, tmp, &chan->srej_l, list) { - list_del(&l->list); - kfree(l); - } } } @@ -1658,25 +1652,6 @@ static void l2cap_retrans_timeout(struct work_struct *work) l2cap_chan_put(chan); } -static void l2cap_drop_acked_frames(struct l2cap_chan *chan) -{ - struct sk_buff *skb; - - while ((skb = skb_peek(&chan->tx_q)) && - chan->unacked_frames) { - if (bt_cb(skb)->control.txseq == chan->expected_ack_seq) - break; - - skb = skb_dequeue(&chan->tx_q); - kfree_skb(skb); - - chan->unacked_frames--; - } - - if (!chan->unacked_frames) - __clear_retrans_timer(chan); -} - static int l2cap_streaming_send(struct l2cap_chan *chan, struct sk_buff_head *skbs) { @@ -1718,53 +1693,6 @@ static int l2cap_streaming_send(struct l2cap_chan *chan, return 0; } -static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq) -{ - struct sk_buff *skb, *tx_skb; - u16 fcs; - u32 control; - - skb = skb_peek(&chan->tx_q); - if (!skb) - return; - - while (bt_cb(skb)->control.txseq != tx_seq) { - if (skb_queue_is_last(&chan->tx_q, skb)) - return; - - skb = skb_queue_next(&chan->tx_q, skb); - } - - if (bt_cb(skb)->control.retries == chan->remote_max_tx && - chan->remote_max_tx) { - l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED); - return; - } - - tx_skb = skb_clone(skb, GFP_ATOMIC); - bt_cb(skb)->control.retries++; - - control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE); - control &= __get_sar_mask(chan); - - if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) - control |= __set_ctrl_final(chan); - - control |= __set_reqseq(chan, chan->buffer_seq); - control |= __set_txseq(chan, tx_seq); - - __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE); - - if (chan->fcs == L2CAP_FCS_CRC16) { - fcs = crc16(0, (u8 *)tx_skb->data, - tx_skb->len - L2CAP_FCS_SIZE); - put_unaligned_le16(fcs, - tx_skb->data + tx_skb->len - L2CAP_FCS_SIZE); - } - - l2cap_do_send(chan, tx_skb); -} - static int l2cap_ertm_send(struct l2cap_chan *chan) { struct sk_buff *skb, *tx_skb; @@ -1868,18 +1796,6 @@ static void l2cap_send_ack(struct l2cap_chan *chan) __l2cap_send_ack(chan); } -static void l2cap_send_srejtail(struct l2cap_chan *chan) -{ - struct srej_list *tail; - u32 control; - - control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); - control |= __set_ctrl_final(chan); - - tail = list_entry((&chan->srej_l)->prev, struct srej_list, list); - control |= __set_reqseq(chan, tail->tx_seq); -} - static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, struct msghdr *msg, int len, int count, struct sk_buff *skb) @@ -2639,7 +2555,6 @@ static inline int l2cap_ertm_init(struct l2cap_chan *chan) chan->expected_ack_seq = 0; chan->unacked_frames = 0; chan->buffer_seq = 0; - chan->num_acked = 0; chan->frames_sent = 0; chan->last_acked_seq = 0; chan->sdu = NULL; @@ -2660,7 +2575,6 @@ static inline int l2cap_ertm_init(struct l2cap_chan *chan) skb_queue_head_init(&chan->srej_q); - INIT_LIST_HEAD(&chan->srej_l); err = l2cap_seq_list_init(&chan->srej_list, chan->tx_win); if (err < 0) return err; @@ -4277,41 +4191,6 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) } } -static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u16 tx_seq, u8 sar) -{ - struct sk_buff *next_skb; - int tx_seq_offset, next_tx_seq_offset; - - bt_cb(skb)->control.txseq = tx_seq; - bt_cb(skb)->control.sar = sar; - - next_skb = skb_peek(&chan->srej_q); - - tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq); - - while (next_skb) { - if (bt_cb(next_skb)->control.txseq == tx_seq) - return -EINVAL; - - next_tx_seq_offset = __seq_offset(chan, - bt_cb(next_skb)->control.txseq, chan->buffer_seq); - - if (next_tx_seq_offset > tx_seq_offset) { - __skb_queue_before(&chan->srej_q, next_skb, skb); - return 0; - } - - if (skb_queue_is_last(&chan->srej_q, next_skb)) - next_skb = NULL; - else - next_skb = skb_queue_next(&chan->srej_q, next_skb); - } - - __skb_queue_tail(&chan->srej_q, skb); - - return 0; -} - static void append_skb_frag(struct sk_buff *skb, struct sk_buff *new_frag, struct sk_buff **last_frag) { @@ -4457,377 +4336,6 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy) } } -static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq) -{ - struct sk_buff *skb; - u32 control; - - while ((skb = skb_peek(&chan->srej_q)) && - !test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - int err; - - if (bt_cb(skb)->control.txseq != tx_seq) - break; - - skb = skb_dequeue(&chan->srej_q); - control = __set_ctrl_sar(chan, bt_cb(skb)->control.sar); - - if (err < 0) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - break; - } - - chan->buffer_seq_srej = __next_seq(chan, chan->buffer_seq_srej); - tx_seq = __next_seq(chan, tx_seq); - } -} - -static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq) -{ - struct srej_list *l, *tmp; - u32 control; - - list_for_each_entry_safe(l, tmp, &chan->srej_l, list) { - if (l->tx_seq == tx_seq) { - list_del(&l->list); - kfree(l); - return; - } - control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); - control |= __set_reqseq(chan, l->tx_seq); - list_del(&l->list); - list_add_tail(&l->list, &chan->srej_l); - } -} - -static int l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq) -{ - struct srej_list *new; - u32 control; - - while (tx_seq != chan->expected_tx_seq) { - control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); - control |= __set_reqseq(chan, chan->expected_tx_seq); - l2cap_seq_list_append(&chan->srej_list, chan->expected_tx_seq); - - new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC); - if (!new) - return -ENOMEM; - - new->tx_seq = chan->expected_tx_seq; - - chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq); - - list_add_tail(&new->list, &chan->srej_l); - } - - chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq); - - return 0; -} - -static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb) -{ - u16 tx_seq = __get_txseq(chan, rx_control); - u16 req_seq = __get_reqseq(chan, rx_control); - u8 sar = __get_ctrl_sar(chan, rx_control); - int tx_seq_offset, expected_tx_seq_offset; - int num_to_ack = (chan->tx_win/6) + 1; - int err = 0; - - BT_DBG("chan %p len %d tx_seq %d rx_control 0x%8.8x", chan, skb->len, - tx_seq, rx_control); - - if (__is_ctrl_final(chan, rx_control) && - test_bit(CONN_WAIT_F, &chan->conn_state)) { - __clear_monitor_timer(chan); - if (chan->unacked_frames > 0) - __set_retrans_timer(chan); - clear_bit(CONN_WAIT_F, &chan->conn_state); - } - - chan->expected_ack_seq = req_seq; - l2cap_drop_acked_frames(chan); - - tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq); - - /* invalid tx_seq */ - if (tx_seq_offset >= chan->tx_win) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - goto drop; - } - - if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - if (!test_bit(CONN_RNR_SENT, &chan->conn_state)) - l2cap_send_ack(chan); - goto drop; - } - - if (tx_seq == chan->expected_tx_seq) - goto expected; - - if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { - struct srej_list *first; - - first = list_first_entry(&chan->srej_l, - struct srej_list, list); - if (tx_seq == first->tx_seq) { - l2cap_add_to_srej_queue(chan, skb, tx_seq, sar); - l2cap_check_srej_gap(chan, tx_seq); - - list_del(&first->list); - kfree(first); - - if (list_empty(&chan->srej_l)) { - chan->buffer_seq = chan->buffer_seq_srej; - clear_bit(CONN_SREJ_SENT, &chan->conn_state); - l2cap_send_ack(chan); - BT_DBG("chan %p, Exit SREJ_SENT", chan); - } - } else { - struct srej_list *l; - - /* duplicated tx_seq */ - if (l2cap_add_to_srej_queue(chan, skb, tx_seq, sar) < 0) - goto drop; - - list_for_each_entry(l, &chan->srej_l, list) { - if (l->tx_seq == tx_seq) { - l2cap_resend_srejframe(chan, tx_seq); - return 0; - } - } - - err = l2cap_send_srejframe(chan, tx_seq); - if (err < 0) { - l2cap_send_disconn_req(chan->conn, chan, -err); - return err; - } - } - } else { - expected_tx_seq_offset = __seq_offset(chan, - chan->expected_tx_seq, chan->buffer_seq); - - /* duplicated tx_seq */ - if (tx_seq_offset < expected_tx_seq_offset) - goto drop; - - set_bit(CONN_SREJ_SENT, &chan->conn_state); - - BT_DBG("chan %p, Enter SREJ", chan); - - INIT_LIST_HEAD(&chan->srej_l); - chan->buffer_seq_srej = chan->buffer_seq; - - __skb_queue_head_init(&chan->srej_q); - l2cap_add_to_srej_queue(chan, skb, tx_seq, sar); - - /* Set P-bit only if there are some I-frames to ack. */ - if (__clear_ack_timer(chan)) - set_bit(CONN_SEND_PBIT, &chan->conn_state); - - err = l2cap_send_srejframe(chan, tx_seq); - if (err < 0) { - l2cap_send_disconn_req(chan->conn, chan, -err); - return err; - } - } - return 0; - -expected: - chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq); - - if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { - bt_cb(skb)->control.txseq = tx_seq; - bt_cb(skb)->control.sar = sar; - __skb_queue_tail(&chan->srej_q, skb); - return 0; - } - - chan->buffer_seq = __next_seq(chan, chan->buffer_seq); - - if (err < 0) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - return err; - } - - if (__is_ctrl_final(chan, rx_control)) { - if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) - l2cap_retransmit_frames(chan); - } - - - chan->num_acked = (chan->num_acked + 1) % num_to_ack; - if (chan->num_acked == num_to_ack - 1) - l2cap_send_ack(chan); - else - __set_ack_timer(chan); - - return 0; - -drop: - kfree_skb(skb); - return 0; -} - -static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u32 rx_control) -{ - BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, - __get_reqseq(chan, rx_control), rx_control); - - chan->expected_ack_seq = __get_reqseq(chan, rx_control); - l2cap_drop_acked_frames(chan); - - if (__is_ctrl_poll(chan, rx_control)) { - set_bit(CONN_SEND_FBIT, &chan->conn_state); - if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { - if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && - (chan->unacked_frames > 0)) - __set_retrans_timer(chan); - - clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - l2cap_send_srejtail(chan); - } else { - l2cap_send_i_or_rr_or_rnr(chan); - } - - } else if (__is_ctrl_final(chan, rx_control)) { - clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - - if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) - l2cap_retransmit_frames(chan); - - } else { - if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && - (chan->unacked_frames > 0)) - __set_retrans_timer(chan); - - clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) - l2cap_send_ack(chan); - else - l2cap_ertm_send(chan); - } -} - -static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u32 rx_control) -{ - u16 tx_seq = __get_reqseq(chan, rx_control); - - BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control); - - clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - - chan->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(chan); - - if (__is_ctrl_final(chan, rx_control)) { - if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) - l2cap_retransmit_frames(chan); - } else { - l2cap_retransmit_frames(chan); - - if (test_bit(CONN_WAIT_F, &chan->conn_state)) - set_bit(CONN_REJ_ACT, &chan->conn_state); - } -} -static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u32 rx_control) -{ - u16 tx_seq = __get_reqseq(chan, rx_control); - - BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control); - - clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - - if (__is_ctrl_poll(chan, rx_control)) { - chan->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(chan); - - set_bit(CONN_SEND_FBIT, &chan->conn_state); - l2cap_retransmit_one_frame(chan, tx_seq); - - l2cap_ertm_send(chan); - - if (test_bit(CONN_WAIT_F, &chan->conn_state)) { - chan->srej_save_reqseq = tx_seq; - set_bit(CONN_SREJ_ACT, &chan->conn_state); - } - } else if (__is_ctrl_final(chan, rx_control)) { - if (test_bit(CONN_SREJ_ACT, &chan->conn_state) && - chan->srej_save_reqseq == tx_seq) - clear_bit(CONN_SREJ_ACT, &chan->conn_state); - else - l2cap_retransmit_one_frame(chan, tx_seq); - } else { - l2cap_retransmit_one_frame(chan, tx_seq); - if (test_bit(CONN_WAIT_F, &chan->conn_state)) { - chan->srej_save_reqseq = tx_seq; - set_bit(CONN_SREJ_ACT, &chan->conn_state); - } - } -} - -static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u32 rx_control) -{ - u16 tx_seq = __get_reqseq(chan, rx_control); - - BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control); - - set_bit(CONN_REMOTE_BUSY, &chan->conn_state); - chan->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(chan); - - if (__is_ctrl_poll(chan, rx_control)) - set_bit(CONN_SEND_FBIT, &chan->conn_state); - - if (!test_bit(CONN_SREJ_SENT, &chan->conn_state)) { - __clear_retrans_timer(chan); - if (__is_ctrl_poll(chan, rx_control)) - l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL); - return; - } - - if (__is_ctrl_poll(chan, rx_control)) { - l2cap_send_srejtail(chan); - } else { - rx_control = __set_ctrl_super(chan, L2CAP_SUPER_RR); - } -} - -static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb) -{ - BT_DBG("chan %p rx_control 0x%8.8x len %d", chan, rx_control, skb->len); - - if (__is_ctrl_final(chan, rx_control) && - test_bit(CONN_WAIT_F, &chan->conn_state)) { - __clear_monitor_timer(chan); - if (chan->unacked_frames > 0) - __set_retrans_timer(chan); - clear_bit(CONN_WAIT_F, &chan->conn_state); - } - - switch (__get_ctrl_super(chan, rx_control)) { - case L2CAP_SUPER_RR: - l2cap_data_channel_rrframe(chan, rx_control); - break; - - case L2CAP_SUPER_REJ: - l2cap_data_channel_rejframe(chan, rx_control); - break; - - case L2CAP_SUPER_SREJ: - l2cap_data_channel_srejframe(chan, rx_control); - break; - - case L2CAP_SUPER_RNR: - l2cap_data_channel_rnrframe(chan, rx_control); - break; - } - - kfree_skb(skb); - return 0; -} - static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq) { BT_DBG("chan %p, txseq %d", chan, txseq); -- cgit v1.2.3 From 4239d16f360ce4c8a1798508dd171ebce93985ba Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Thu, 17 May 2012 20:53:49 -0700 Subject: Bluetooth: Check rules when setting retransmit or monitor timers The ERTM specification requires the retransmit timer to be cancelled when the monitor timer is set. The retransmit timer cannot be set again while the monitor timer is pending. Signed-off-by: Mat Martineau Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 4 ---- net/bluetooth/l2cap_core.c | 22 ++++++++++++++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 7d1da5a7d11e..117db8e4a5f4 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -706,11 +706,7 @@ static inline bool l2cap_clear_timer(struct l2cap_chan *chan, #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) #define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer) -#define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \ - msecs_to_jiffies(L2CAP_DEFAULT_RETRANS_TO)); #define __clear_retrans_timer(c) l2cap_clear_timer(c, &c->retrans_timer) -#define __set_monitor_timer(c) l2cap_set_timer(c, &c->monitor_timer, \ - msecs_to_jiffies(L2CAP_DEFAULT_MONITOR_TO)); #define __clear_monitor_timer(c) l2cap_clear_timer(c, &c->monitor_timer) #define __set_ack_timer(c) l2cap_set_timer(c, &chan->ack_timer, \ msecs_to_jiffies(L2CAP_DEFAULT_ACK_TO)); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8ea9ec648bfd..38e9a0ea4f48 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -227,6 +227,24 @@ static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err) release_sock(sk); } +static void __set_retrans_timer(struct l2cap_chan *chan) +{ + if (!delayed_work_pending(&chan->monitor_timer) && + chan->retrans_timeout) { + l2cap_set_timer(chan, &chan->retrans_timer, + msecs_to_jiffies(chan->retrans_timeout)); + } +} + +static void __set_monitor_timer(struct l2cap_chan *chan) +{ + __clear_retrans_timer(chan); + if (chan->monitor_timeout) { + l2cap_set_timer(chan, &chan->monitor_timer, + msecs_to_jiffies(chan->monitor_timeout)); + } +} + static struct sk_buff *l2cap_ertm_seq_in_queue(struct sk_buff_head *head, u16 seq) { @@ -1619,7 +1637,7 @@ int __l2cap_wait_ack(struct sock *sk) static void l2cap_monitor_timeout(struct work_struct *work) { struct l2cap_chan *chan = container_of(work, struct l2cap_chan, - monitor_timer.work); + monitor_timer.work); BT_DBG("chan %p", chan); @@ -1643,7 +1661,7 @@ static void l2cap_monitor_timeout(struct work_struct *work) static void l2cap_retrans_timeout(struct work_struct *work) { struct l2cap_chan *chan = container_of(work, struct l2cap_chan, - retrans_timer.work); + retrans_timer.work); BT_DBG("chan %p", chan); -- cgit v1.2.3 From 522cc2ee6e55ba49f4df338e0dfcfb989b46eb8c Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Thu, 17 May 2012 20:53:54 -0700 Subject: Bluetooth: Remove unused ERTM control field macros Now that l2cap_ctrl is used to set up control fields, these macros are not needed. Signed-off-by: Mat Martineau Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 168 ------------------------------------------ 1 file changed, 168 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 117db8e4a5f4..7bc40198f147 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -725,174 +725,6 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq) return (seq + 1) % (chan->tx_win_max + 1); } -static inline int l2cap_tx_window_full(struct l2cap_chan *ch) -{ - int sub; - - sub = (ch->next_tx_seq - ch->expected_ack_seq) % 64; - - if (sub < 0) - sub += 64; - - return sub == ch->remote_tx_win; -} - -static inline __u16 __get_reqseq(struct l2cap_chan *chan, __u32 ctrl) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return (ctrl & L2CAP_EXT_CTRL_REQSEQ) >> - L2CAP_EXT_CTRL_REQSEQ_SHIFT; - else - return (ctrl & L2CAP_CTRL_REQSEQ) >> L2CAP_CTRL_REQSEQ_SHIFT; -} - -static inline __u32 __set_reqseq(struct l2cap_chan *chan, __u32 reqseq) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return (reqseq << L2CAP_EXT_CTRL_REQSEQ_SHIFT) & - L2CAP_EXT_CTRL_REQSEQ; - else - return (reqseq << L2CAP_CTRL_REQSEQ_SHIFT) & L2CAP_CTRL_REQSEQ; -} - -static inline __u16 __get_txseq(struct l2cap_chan *chan, __u32 ctrl) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return (ctrl & L2CAP_EXT_CTRL_TXSEQ) >> - L2CAP_EXT_CTRL_TXSEQ_SHIFT; - else - return (ctrl & L2CAP_CTRL_TXSEQ) >> L2CAP_CTRL_TXSEQ_SHIFT; -} - -static inline __u32 __set_txseq(struct l2cap_chan *chan, __u32 txseq) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return (txseq << L2CAP_EXT_CTRL_TXSEQ_SHIFT) & - L2CAP_EXT_CTRL_TXSEQ; - else - return (txseq << L2CAP_CTRL_TXSEQ_SHIFT) & L2CAP_CTRL_TXSEQ; -} - -static inline bool __is_sframe(struct l2cap_chan *chan, __u32 ctrl) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return ctrl & L2CAP_EXT_CTRL_FRAME_TYPE; - else - return ctrl & L2CAP_CTRL_FRAME_TYPE; -} - -static inline __u32 __set_sframe(struct l2cap_chan *chan) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return L2CAP_EXT_CTRL_FRAME_TYPE; - else - return L2CAP_CTRL_FRAME_TYPE; -} - -static inline __u8 __get_ctrl_sar(struct l2cap_chan *chan, __u32 ctrl) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return (ctrl & L2CAP_EXT_CTRL_SAR) >> L2CAP_EXT_CTRL_SAR_SHIFT; - else - return (ctrl & L2CAP_CTRL_SAR) >> L2CAP_CTRL_SAR_SHIFT; -} - -static inline __u32 __set_ctrl_sar(struct l2cap_chan *chan, __u32 sar) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return (sar << L2CAP_EXT_CTRL_SAR_SHIFT) & L2CAP_EXT_CTRL_SAR; - else - return (sar << L2CAP_CTRL_SAR_SHIFT) & L2CAP_CTRL_SAR; -} - -static inline bool __is_sar_start(struct l2cap_chan *chan, __u32 ctrl) -{ - return __get_ctrl_sar(chan, ctrl) == L2CAP_SAR_START; -} - -static inline __u32 __get_sar_mask(struct l2cap_chan *chan) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return L2CAP_EXT_CTRL_SAR; - else - return L2CAP_CTRL_SAR; -} - -static inline __u8 __get_ctrl_super(struct l2cap_chan *chan, __u32 ctrl) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return (ctrl & L2CAP_EXT_CTRL_SUPERVISE) >> - L2CAP_EXT_CTRL_SUPER_SHIFT; - else - return (ctrl & L2CAP_CTRL_SUPERVISE) >> L2CAP_CTRL_SUPER_SHIFT; -} - -static inline __u32 __set_ctrl_super(struct l2cap_chan *chan, __u32 super) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return (super << L2CAP_EXT_CTRL_SUPER_SHIFT) & - L2CAP_EXT_CTRL_SUPERVISE; - else - return (super << L2CAP_CTRL_SUPER_SHIFT) & - L2CAP_CTRL_SUPERVISE; -} - -static inline __u32 __set_ctrl_final(struct l2cap_chan *chan) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return L2CAP_EXT_CTRL_FINAL; - else - return L2CAP_CTRL_FINAL; -} - -static inline bool __is_ctrl_final(struct l2cap_chan *chan, __u32 ctrl) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return ctrl & L2CAP_EXT_CTRL_FINAL; - else - return ctrl & L2CAP_CTRL_FINAL; -} - -static inline __u32 __set_ctrl_poll(struct l2cap_chan *chan) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return L2CAP_EXT_CTRL_POLL; - else - return L2CAP_CTRL_POLL; -} - -static inline bool __is_ctrl_poll(struct l2cap_chan *chan, __u32 ctrl) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return ctrl & L2CAP_EXT_CTRL_POLL; - else - return ctrl & L2CAP_CTRL_POLL; -} - -static inline __u32 __get_control(struct l2cap_chan *chan, void *p) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return get_unaligned_le32(p); - else - return get_unaligned_le16(p); -} - -static inline void __put_control(struct l2cap_chan *chan, __u32 control, - void *p) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return put_unaligned_le32(control, p); - else - return put_unaligned_le16(control, p); -} - -static inline __u8 __ctrl_size(struct l2cap_chan *chan) -{ - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - return L2CAP_EXT_HDR_SIZE - L2CAP_HDR_SIZE; - else - return L2CAP_ENH_HDR_SIZE - L2CAP_HDR_SIZE; -} extern bool disable_ertm; -- cgit v1.2.3 From 38351c66e407e610283e5332b819822055db473c Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Tue, 22 May 2012 19:00:20 -0300 Subject: Bluetooth: Fix trailing whitespaces in license text As reported by checkpatch.pl Signed-off-by: Gustavo Padovan Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 961669b648fd..b98181bd2b33 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -1,4 +1,4 @@ -/* +/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated @@ -12,13 +12,13 @@ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ -- cgit v1.2.3 From a6c511c636848f871f5b7aef38e25e5b894b3b48 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Wed, 23 May 2012 12:35:46 +0200 Subject: Bluetooth: Rename HCI_QUIRK_NO_RESET to HCI_QUIRK_RESET_ON_CLOSE HCI_QUIRK_NO_RESET name is misleading - purpose of this quirk is to reset device on close instead of init, not to not reset at all. Rename it to HCI_QUIRK_RESET_ON_CLOSE to avoid confusion. Signed-off-by: Szymon Janc Signed-off-by: Marcel Holtmann --- drivers/bluetooth/bpa10x.c | 2 +- drivers/bluetooth/btusb.c | 6 +++--- drivers/bluetooth/hci_ldisc.c | 2 +- include/net/bluetooth/hci.h | 2 +- net/bluetooth/hci_core.c | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 609861a53c28..29caaed2d715 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -470,7 +470,7 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id * hdev->flush = bpa10x_flush; hdev->send = bpa10x_send_frame; - set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); err = hci_register_dev(hdev); if (err < 0) { diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index c9463af8e564..3a6cdc9b75a3 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1026,7 +1026,7 @@ static int btusb_probe(struct usb_interface *intf, data->isoc = usb_ifnum_to_if(data->udev, 1); if (!reset) - set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); if (force_scofix || id->driver_info & BTUSB_WRONG_SCO_MTU) { if (!disable_scofix) @@ -1038,7 +1038,7 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_DIGIANSWER) { data->cmdreq_type = USB_TYPE_VENDOR; - set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); } if (id->driver_info & BTUSB_CSR) { @@ -1046,7 +1046,7 @@ static int btusb_probe(struct usb_interface *intf, /* Old firmware would otherwise execute USB reset */ if (le16_to_cpu(udev->descriptor.bcdDevice) < 0x117) - set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); } if (id->driver_info & BTUSB_SNIFFER) { diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index e564579a6115..2f9b796e106e 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -394,7 +394,7 @@ static int hci_uart_register_dev(struct hci_uart *hu) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags)) - set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags)) hdev->dev_type = HCI_AMP; diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 66a7b579e31c..97c57aa938f3 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -58,7 +58,7 @@ /* HCI device quirks */ enum { - HCI_QUIRK_NO_RESET, + HCI_QUIRK_RESET_ON_CLOSE, HCI_QUIRK_RAW_DEVICE, HCI_QUIRK_FIXUP_BUFFER_SIZE }; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index d0a960dabd53..0ed4edf0f77b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -203,7 +203,7 @@ static void bredr_init(struct hci_dev *hdev) /* Mandatory initialization */ /* Reset */ - if (!test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks)) { + if (!test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) { set_bit(HCI_RESET, &hdev->flags); hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); } @@ -792,7 +792,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) skb_queue_purge(&hdev->cmd_q); atomic_set(&hdev->cmd_cnt, 1); if (!test_bit(HCI_RAW, &hdev->flags) && - test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks)) { + test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) { set_bit(HCI_INIT, &hdev->flags); __hci_request(hdev, hci_reset_req, 0, msecs_to_jiffies(250)); -- cgit v1.2.3 From 9b3b44604ac8e06d299718c5d0fa0b91b675ae0b Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 23 May 2012 11:31:20 +0300 Subject: Bluetooth: Use defined link key size Remove magic number with defined link key size. Signed-off-by: Andrei Emeltchenko Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 6 ++++-- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 2 +- net/bluetooth/hci_event.c | 2 +- net/bluetooth/mgmt.c | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 97c57aa938f3..0bc5555510f3 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -30,6 +30,8 @@ #define HCI_MAX_EVENT_SIZE 260 #define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4) +#define HCI_LINK_KEY_SIZE 16 + /* HCI dev events */ #define HCI_DEV_REG 1 #define HCI_DEV_UNREG 2 @@ -371,7 +373,7 @@ struct hci_cp_reject_conn_req { #define HCI_OP_LINK_KEY_REPLY 0x040b struct hci_cp_link_key_reply { bdaddr_t bdaddr; - __u8 link_key[16]; + __u8 link_key[HCI_LINK_KEY_SIZE]; } __packed; #define HCI_OP_LINK_KEY_NEG_REPLY 0x040c @@ -1048,7 +1050,7 @@ struct hci_ev_link_key_req { #define HCI_EV_LINK_KEY_NOTIFY 0x18 struct hci_ev_link_key_notify { bdaddr_t bdaddr; - __u8 link_key[16]; + __u8 link_key[HCI_LINK_KEY_SIZE]; __u8 key_type; } __packed; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9fc7728f94e4..6c658fc7ac93 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -105,7 +105,7 @@ struct link_key { struct list_head list; bdaddr_t bdaddr; u8 type; - u8 val[16]; + u8 val[HCI_LINK_KEY_SIZE]; u8 pin_len; }; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 0ed4edf0f77b..027257d4b52a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1291,7 +1291,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, } bacpy(&key->bdaddr, bdaddr); - memcpy(key->val, val, 16); + memcpy(key->val, val, HCI_LINK_KEY_SIZE); key->pin_len = pin_len; if (type == HCI_LK_CHANGED_COMBINATION) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 6c2d7ccf26e5..1795c0c9b411 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2739,7 +2739,7 @@ static inline void hci_link_key_request_evt(struct hci_dev *hdev, } bacpy(&cp.bdaddr, &ev->bdaddr); - memcpy(cp.link_key, key->val, 16); + memcpy(cp.link_key, key->val, HCI_LINK_KEY_SIZE); hci_send_cmd(hdev, HCI_OP_LINK_KEY_REPLY, sizeof(cp), &cp); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 6a7e926c418f..1fd49e652694 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2955,7 +2955,7 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bacpy(&ev.key.addr.bdaddr, &key->bdaddr); ev.key.addr.type = BDADDR_BREDR; ev.key.type = key->type; - memcpy(ev.key.val, key->val, 16); + memcpy(ev.key.val, key->val, HCI_LINK_KEY_SIZE); ev.key.pin_len = key->pin_len; return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL); -- cgit v1.2.3 From c3c7ea65941a0b7a4f1b9655e7aaaab6ce1874d2 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Wed, 23 May 2012 04:04:20 -0300 Subject: Bluetooth: Fix coding style in include/net/bluetooth Fix all warning and errors reported by checkpatch but license trailing whitespace and bdaddr_t definition. Signed-off-by: Gustavo Padovan Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 26 ++++++++++++++------------ include/net/bluetooth/hci.h | 4 ++-- include/net/bluetooth/hci_core.h | 12 +++++++----- 3 files changed, 23 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index b98181bd2b33..7a9f9612db5a 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -25,7 +25,7 @@ #ifndef __BLUETOOTH_H #define __BLUETOOTH_H -#include +#include #include #include #include @@ -168,8 +168,8 @@ typedef struct { #define BDADDR_LE_PUBLIC 0x01 #define BDADDR_LE_RANDOM 0x02 -#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}}) -#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}}) +#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0} }) +#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff} }) /* Copy, swap, convert BD Address */ static inline int bacmp(bdaddr_t *ba1, bdaddr_t *ba2) @@ -215,7 +215,7 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags); int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags); -uint bt_sock_poll(struct file * file, struct socket *sock, poll_table *wait); +uint bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait); int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo); @@ -225,12 +225,12 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock); /* Skb helpers */ struct l2cap_ctrl { - unsigned int sframe : 1, - poll : 1, - final : 1, - fcs : 1, - sar : 2, - super : 2; + unsigned int sframe:1, + poll:1, + final:1, + fcs:1, + sar:2, + super:2; __u16 reqseq; __u16 txseq; __u8 retries; @@ -249,7 +249,8 @@ static inline struct sk_buff *bt_skb_alloc(unsigned int len, gfp_t how) { struct sk_buff *skb; - if ((skb = alloc_skb(len + BT_SKB_RESERVE, how))) { + skb = alloc_skb(len + BT_SKB_RESERVE, how); + if (skb) { skb_reserve(skb, BT_SKB_RESERVE); bt_cb(skb)->incoming = 0; } @@ -261,7 +262,8 @@ static inline struct sk_buff *bt_skb_send_alloc(struct sock *sk, { struct sk_buff *skb; - if ((skb = sock_alloc_send_skb(sk, len + BT_SKB_RESERVE, nb, err))) { + skb = sock_alloc_send_skb(sk, len + BT_SKB_RESERVE, nb, err); + if (skb) { skb_reserve(skb, BT_SKB_RESERVE); bt_cb(skb)->incoming = 0; } diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 0bc5555510f3..5de351e49d49 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1309,12 +1309,12 @@ static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb) } /* Command opcode pack/unpack */ -#define hci_opcode_pack(ogf, ocf) (__u16) ((ocf & 0x03ff)|(ogf << 10)) +#define hci_opcode_pack(ogf, ocf) ((__u16) ((ocf & 0x03ff)|(ogf << 10))) #define hci_opcode_ogf(op) (op >> 10) #define hci_opcode_ocf(op) (op & 0x03ff) /* ACL handle and flags pack/unpack */ -#define hci_handle_pack(h, f) (__u16) ((h & 0x0fff)|(f << 12)) +#define hci_handle_pack(h, f) ((__u16) ((h & 0x0fff)|(f << 12))) #define hci_handle(h) (h & 0x0fff) #define hci_flags(h) (h >> 12) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6c658fc7ac93..a8ba50d7a81e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -65,7 +65,7 @@ struct discovery_state { DISCOVERY_RESOLVING, DISCOVERY_STOPPING, } state; - struct list_head all; /* All devices found during inquiry */ + struct list_head all; /* All devices found during inquiry */ struct list_head unknown; /* Name state not known */ struct list_head resolve; /* Name needs to be resolved */ __u32 timestamp; @@ -360,7 +360,8 @@ extern int l2cap_connect_cfm(struct hci_conn *hcon, u8 status); extern int l2cap_disconn_ind(struct hci_conn *hcon); extern int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason); extern int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt); -extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags); +extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, + u16 flags); extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); extern int sco_connect_cfm(struct hci_conn *hcon, __u8 status); @@ -429,8 +430,8 @@ enum { static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; - return (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && - test_bit(HCI_CONN_SSP_ENABLED, &conn->flags)); + return test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && + test_bit(HCI_CONN_SSP_ENABLED, &conn->flags); } static inline void hci_conn_hash_init(struct hci_dev *hdev) @@ -661,7 +662,8 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg); int hci_get_auth_info(struct hci_dev *hdev, void __user *arg); int hci_inquiry(void __user *arg); -struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); +struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, + bdaddr_t *bdaddr); int hci_blacklist_clear(struct hci_dev *hdev); int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); -- cgit v1.2.3 From 8c520a59927a5600973782505dbb750d985057c4 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Wed, 23 May 2012 04:04:22 -0300 Subject: Bluetooth: Remove unnecessary headers include Most of the include were unnecessary or already included by some other header. Replace module.h by export.h where possible. Signed-off-by: Gustavo Padovan Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btusb.c | 8 -------- include/net/bluetooth/bluetooth.h | 3 --- include/net/bluetooth/hci.h | 1 - include/net/bluetooth/hci_core.h | 1 - net/bluetooth/af_bluetooth.c | 11 ----------- net/bluetooth/bnep/core.c | 17 ----------------- net/bluetooth/bnep/netdev.c | 10 +--------- net/bluetooth/bnep/sock.c | 18 +----------------- net/bluetooth/hci_conn.c | 16 +--------------- net/bluetooth/hci_core.c | 23 ++--------------------- net/bluetooth/hci_event.c | 15 +-------------- net/bluetooth/hci_sock.c | 20 +------------------- net/bluetooth/hci_sysfs.c | 4 ---- net/bluetooth/hidp/core.c | 19 ------------------- net/bluetooth/hidp/sock.c | 16 +--------------- net/bluetooth/l2cap_core.c | 19 ------------------- net/bluetooth/l2cap_sock.c | 1 - net/bluetooth/lib.c | 7 +------ net/bluetooth/mgmt.c | 2 -- net/bluetooth/rfcomm/core.c | 14 -------------- net/bluetooth/rfcomm/sock.c | 21 +-------------------- net/bluetooth/rfcomm/tty.c | 5 ----- net/bluetooth/sco.c | 18 ------------------ net/bluetooth/smp.c | 7 ++++--- 24 files changed, 14 insertions(+), 262 deletions(-) (limited to 'include') diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 3a6cdc9b75a3..a45e717f5f84 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -21,15 +21,7 @@ * */ -#include #include -#include -#include -#include -#include -#include -#include - #include #include diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 7a9f9612db5a..565d4bee1e49 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -25,9 +25,6 @@ #ifndef __BLUETOOTH_H #define __BLUETOOTH_H -#include -#include -#include #include #include diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 5de351e49d49..edb663908121 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1292,7 +1292,6 @@ struct hci_sco_hdr { __u8 dlen; } __packed; -#include static inline struct hci_event_hdr *hci_event_hdr(const struct sk_buff *skb) { return (struct hci_event_hdr *) skb->data; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index a8ba50d7a81e..d584a47d1c86 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -25,7 +25,6 @@ #ifndef __HCI_CORE_H #define __HCI_CORE_H -#include #include /* HCI priority */ diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index e31a20f5b6be..251747269d37 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -25,18 +25,7 @@ /* Bluetooth address family and sockets. */ #include - -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include #include diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index a918f6e4f003..4a6620bc1570 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -26,26 +26,9 @@ */ #include - -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include - -#include #include - -#include #include -#include - #include #include diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c index 46c9ece7b04a..98f86f91d47c 100644 --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -25,16 +25,8 @@ SOFTWARE IS DISCLAIMED. */ -#include -#include - -#include -#include +#include #include -#include -#include - -#include #include #include diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c index 180bfc45810d..5e5f5b410e0b 100644 --- a/net/bluetooth/bnep/sock.c +++ b/net/bluetooth/bnep/sock.c @@ -24,24 +24,8 @@ SOFTWARE IS DISCLAIMED. */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include - #include "bnep.h" diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 231fc4400f37..3bb2d552a888 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -24,21 +24,7 @@ /* Bluetooth HCI connection handling. */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include +#include #include #include diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index bee425ad25b5..3431ec908c02 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -25,28 +25,9 @@ /* Bluetooth HCI core. */ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include -#include -#include +#include #include #include diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 87e6f74af6fe..5e24a57a4613 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -24,20 +24,7 @@ /* Bluetooth HCI event handling. */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include +#include #include #include diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 9d8e1c39955e..a7f04de03d79 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -24,25 +24,7 @@ /* Bluetooth HCI sockets. */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include +#include #include #include diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index ee8d9ea6bf3c..a20e61c3653d 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -1,10 +1,6 @@ /* Bluetooth HCI driver model support. */ -#include -#include -#include #include -#include #include #include diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index c8625b8ccb6a..8a4afc7515a2 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -21,27 +21,8 @@ */ #include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include #include -#include - -#include -#include #include #include diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c index 73a32d705c1f..18b3f6892a36 100644 --- a/net/bluetooth/hidp/sock.c +++ b/net/bluetooth/hidp/sock.c @@ -20,22 +20,8 @@ SOFTWARE IS DISCLAIMED. */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include #include "hidp.h" diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index db76a7750ee6..f6b785593ec3 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -30,27 +30,8 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include #include -#include - -#include #include #include diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 3bb1611b9d48..4d3660540c05 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -27,7 +27,6 @@ /* Bluetooth L2CAP sockets. */ -#include #include #include diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index 994bc3c7ddc7..e1c97527e16c 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -26,12 +26,7 @@ #define pr_fmt(fmt) "Bluetooth: " fmt -#include - -#include -#include -#include -#include +#include #include diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 984afe4ef407..205574edff20 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -24,8 +24,6 @@ /* Bluetooth HCI Management interface */ -#include -#include #include #include diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 585d3916d3d4..c75107ef8920 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -26,22 +26,8 @@ */ #include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include #include -#include - -#include -#include #include #include diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index e8707debb864..7e1e59645c05 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -25,27 +25,8 @@ * RFCOMM sockets. */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include - -#include #include #include diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index f2f4d064df94..cb960773c002 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -31,11 +31,6 @@ #include #include -#include -#include -#include -#include - #include #include #include diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 6401ccae2045..40bbe25dcff7 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -25,26 +25,8 @@ /* Bluetooth SCO sockets. */ #include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include -#include -#include - -#include #include #include diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 6fc7c4708f3e..ff4835b61de9 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -20,14 +20,15 @@ SOFTWARE IS DISCLAIMED. */ +#include +#include +#include + #include #include #include #include #include -#include -#include -#include #define SMP_TIMEOUT msecs_to_jiffies(30000) -- cgit v1.2.3 From 59e54bd15d63f102c71c3ce695bca5ed90926e46 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 23 May 2012 15:44:06 +0300 Subject: Bluetooth: Define L2CAP conf continuation flag Define Continuation flag which the only flag used from Flags field in L2CAP Configuration Request and Response. Signed-off-by: Andrei Emeltchenko Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 3 +++ net/bluetooth/l2cap_core.c | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 7bc40198f147..01422578cc78 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -271,6 +271,9 @@ struct l2cap_conf_rsp { #define L2CAP_CONF_PENDING 0x0004 #define L2CAP_CONF_EFS_REJECT 0x0005 +/* configuration req/rsp continuation flag */ +#define L2CAP_CONF_FLAG_CONTINUATION 0x0001 + struct l2cap_conf_opt { __u8 type; __u8 len; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index f6b785593ec3..e31b005f9827 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2944,7 +2944,7 @@ done: } req->dcid = cpu_to_le16(chan->dcid); - req->flags = cpu_to_le16(0); + req->flags = __constant_cpu_to_le16(0); return ptr - data; } @@ -3164,7 +3164,7 @@ done: } rsp->scid = cpu_to_le16(chan->dcid); rsp->result = cpu_to_le16(result); - rsp->flags = cpu_to_le16(0x0000); + rsp->flags = __constant_cpu_to_le16(0); return ptr - data; } @@ -3263,7 +3263,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi } req->dcid = cpu_to_le16(chan->dcid); - req->flags = cpu_to_le16(0x0000); + req->flags = __constant_cpu_to_le16(0); return ptr - data; } @@ -3618,7 +3618,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr memcpy(chan->conf_req + chan->conf_len, req->data, len); chan->conf_len += len; - if (flags & 0x0001) { + if (flags & L2CAP_CONF_FLAG_CONTINUATION) { /* Incomplete config. Send empty response. */ l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(chan, rsp, @@ -3769,7 +3769,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr goto done; } - if (flags & 0x01) + if (flags & L2CAP_CONF_FLAG_CONTINUATION) goto done; set_bit(CONF_INPUT_DONE, &chan->conf_state); -- cgit v1.2.3 From 2983fd682444180e45567ce8147a612b97ba69da Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 24 May 2012 15:42:50 +0300 Subject: Bluetooth: Define and use PSM identifiers Define assigned Protocol and Service Multiplexor (PSM) identifiers and use them instead of magic numbers. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 4 ++++ net/bluetooth/l2cap_core.c | 4 ++-- net/bluetooth/l2cap_sock.c | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 01422578cc78..f44344b92d2d 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -229,6 +229,10 @@ struct l2cap_conn_rsp { __le16 status; } __packed; +/* protocol/service multiplexer (PSM) */ +#define L2CAP_PSM_SDP 0x0001 +#define L2CAP_PSM_RFCOMM 0x0003 + /* channel indentifier */ #define L2CAP_CID_SIGNALING 0x0001 #define L2CAP_CID_CONN_LESS 0x0002 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index c9e6ae4a3363..65c3f4e13965 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -648,7 +648,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) default: return HCI_AT_NO_BONDING; } - } else if (chan->psm == cpu_to_le16(0x0001)) { + } else if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_SDP)) { if (chan->sec_level == BT_SECURITY_LOW) chan->sec_level = BT_SECURITY_SDP; @@ -3393,7 +3393,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd lock_sock(parent); /* Check if the ACL is secure enough (if not SDP) */ - if (psm != cpu_to_le16(0x0001) && + if (psm != __constant_cpu_to_le16(L2CAP_PSM_SDP) && !hci_conn_check_link_mode(conn->hcon)) { conn->disc_reason = HCI_ERROR_AUTH_FAILURE; result = L2CAP_CR_SEC_BLOCK; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 4d3660540c05..d244361a455c 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -88,8 +88,8 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) if (err < 0) goto done; - if (__le16_to_cpu(la.l2_psm) == 0x0001 || - __le16_to_cpu(la.l2_psm) == 0x0003) + if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_SDP || + __le16_to_cpu(la.l2_psm) == L2CAP_PSM_RFCOMM) chan->sec_level = BT_SECURITY_SDP; bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); -- cgit v1.2.3 From 523e93cdb39086b25af2ed19d2a69248510727a2 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 25 May 2012 15:09:26 +0300 Subject: Bluetooth: Define HCI AMP cmd struct Add HCI commands to deal with Bluetooth AMP controllers. Those commands will be used by bluetooth and softamp code. Signed-off-by: Andrei Emeltchenko Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 81 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index edb663908121..de09a26e4223 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -31,6 +31,7 @@ #define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4) #define HCI_LINK_KEY_SIZE 16 +#define HCI_AMP_LINK_KEY_SIZE (2 * HCI_LINK_KEY_SIZE) /* HCI dev events */ #define HCI_DEV_REG 1 @@ -525,6 +526,28 @@ struct hci_cp_io_capability_neg_reply { __u8 reason; } __packed; +#define HCI_OP_CREATE_PHY_LINK 0x0435 +struct hci_cp_create_phy_link { + __u8 phy_handle; + __u8 key_len; + __u8 key_type; + __u8 key[HCI_AMP_LINK_KEY_SIZE]; +} __packed; + +#define HCI_OP_ACCEPT_PHY_LINK 0x0436 +struct hci_cp_accept_phy_link { + __u8 phy_handle; + __u8 key_len; + __u8 key_type; + __u8 key[HCI_AMP_LINK_KEY_SIZE]; +} __packed; + +#define HCI_OP_DISCONN_PHY_LINK 0x0437 +struct hci_cp_disconn_phy_link { + __u8 phy_handle; + __u8 reason; +} __packed; + #define HCI_OP_SNIFF_MODE 0x0803 struct hci_cp_sniff_mode { __le16 handle; @@ -820,6 +843,31 @@ struct hci_rp_read_local_amp_info { __le32 be_flush_to; } __packed; +#define HCI_OP_READ_LOCAL_AMP_ASSOC 0x140a +struct hci_cp_read_local_amp_assoc { + __u8 phy_handle; + __le16 len_so_far; + __le16 max_len; +} __packed; +struct hci_rp_read_local_amp_assoc { + __u8 status; + __u8 phy_handle; + __le16 rem_len; + __u8 frag[0]; +} __packed; + +#define HCI_OP_WRITE_REMOTE_AMP_ASSOC 0x140b +struct hci_cp_write_remote_amp_assoc { + __u8 phy_handle; + __le16 len_so_far; + __le16 rem_len; + __u8 frag[0]; +} __packed; +struct hci_rp_write_remote_amp_assoc { + __u8 status; + __u8 phy_handle; +} __packed; + #define HCI_OP_LE_SET_EVENT_MASK 0x2001 struct hci_cp_le_set_event_mask { __u8 mask[8]; @@ -1192,6 +1240,39 @@ struct hci_ev_le_meta { __u8 subevent; } __packed; +#define HCI_EV_PHY_LINK_COMPLETE 0x40 +struct hci_ev_phy_link_complete { + __u8 status; + __u8 phy_handle; +} __packed; + +#define HCI_EV_CHANNEL_SELECTED 0x41 +struct hci_ev_channel_selected { + __u8 phy_handle; +} __packed; + +#define HCI_EV_DISCONN_PHY_LINK_COMPLETE 0x42 +struct hci_ev_disconn_phy_link_complete { + __u8 status; + __u8 phy_handle; + __u8 reason; +} __packed; + +#define HCI_EV_LOGICAL_LINK_COMPLETE 0x45 +struct hci_ev_logical_link_complete { + __u8 status; + __le16 handle; + __u8 phy_handle; + __u8 flow_spec_id; +} __packed; + +#define HCI_EV_DISCONN_LOGICAL_LINK_COMPLETE 0x46 +struct hci_ev_disconn_logical_link_complete { + __u8 status; + __le16 handle; + __u8 reason; +} __packed; + #define HCI_EV_NUM_COMP_BLOCKS 0x48 struct hci_comp_blocks_info { __le16 handle; -- cgit v1.2.3 From 80b980279508edd1a92d8d77ec99b0ddad00c5fe Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Sun, 27 May 2012 22:27:51 -0300 Subject: Bluetooth: Use chan as parameters for l2cap chan ops Use chan instead of void * makes more sense here. Signed-off-by: Gustavo Padovan Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 10 ++++++---- net/bluetooth/l2cap_core.c | 30 +++++++++++++++--------------- net/bluetooth/l2cap_sock.c | 16 ++++++++-------- 3 files changed, 29 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index f44344b92d2d..aa2dbc680d5c 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -527,10 +527,12 @@ struct l2cap_chan { struct l2cap_ops { char *name; - struct l2cap_chan *(*new_connection) (void *data); - int (*recv) (void *data, struct sk_buff *skb); - void (*close) (void *data); - void (*state_change) (void *data, int state); + struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan); + int (*recv) (struct l2cap_chan * chan, + struct sk_buff *skb); + void (*close) (struct l2cap_chan *chan); + void (*state_change) (struct l2cap_chan *chan, + int state); struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan, unsigned long len, int nb); }; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index de0dc9ec9862..7edc8146db26 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -180,7 +180,7 @@ static void __l2cap_state_change(struct l2cap_chan *chan, int state) state_to_string(state)); chan->state = state; - chan->ops->state_change(chan->data, state); + chan->ops->state_change(chan, state); } static void l2cap_state_change(struct l2cap_chan *chan, int state) @@ -381,7 +381,7 @@ static void l2cap_chan_timeout(struct work_struct *work) l2cap_chan_unlock(chan); - chan->ops->close(chan->data); + chan->ops->close(chan); mutex_unlock(&conn->chan_lock); l2cap_chan_put(chan); @@ -569,7 +569,7 @@ static void l2cap_chan_cleanup_listen(struct sock *parent) l2cap_chan_close(chan, ECONNRESET); l2cap_chan_unlock(chan); - chan->ops->close(chan->data); + chan->ops->close(chan); } } @@ -1213,7 +1213,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) goto clean; } - chan = pchan->ops->new_connection(pchan->data); + chan = pchan->ops->new_connection(pchan); if (!chan) goto clean; @@ -1324,7 +1324,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) l2cap_chan_unlock(chan); - chan->ops->close(chan->data); + chan->ops->close(chan); l2cap_chan_put(chan); } @@ -2568,7 +2568,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) if (!nskb) continue; - if (chan->ops->recv(chan->data, nskb)) + if (chan->ops->recv(chan, nskb)) kfree_skb(nskb); } @@ -3411,7 +3411,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd goto response; } - chan = pchan->ops->new_connection(pchan->data); + chan = pchan->ops->new_connection(pchan); if (!chan) goto response; @@ -3420,7 +3420,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd /* Check if we already have channel with that dcid */ if (__l2cap_get_chan_by_dcid(conn, scid)) { sock_set_flag(sk, SOCK_ZAPPED); - chan->ops->close(chan->data); + chan->ops->close(chan); goto response; } @@ -3831,7 +3831,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd l2cap_chan_unlock(chan); - chan->ops->close(chan->data); + chan->ops->close(chan); l2cap_chan_put(chan); mutex_unlock(&conn->chan_lock); @@ -3865,7 +3865,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd l2cap_chan_unlock(chan); - chan->ops->close(chan->data); + chan->ops->close(chan); l2cap_chan_put(chan); mutex_unlock(&conn->chan_lock); @@ -4435,7 +4435,7 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, if (chan->sdu) break; - err = chan->ops->recv(chan->data, skb); + err = chan->ops->recv(chan, skb); break; case L2CAP_SAR_START: @@ -4485,7 +4485,7 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, if (chan->sdu->len != chan->sdu_len) break; - err = chan->ops->recv(chan->data, chan->sdu); + err = chan->ops->recv(chan, chan->sdu); if (!err) { /* Reassembly complete */ @@ -5207,7 +5207,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (chan->imtu < skb->len) goto drop; - if (!chan->ops->recv(chan->data, skb)) + if (!chan->ops->recv(chan, skb)) goto done; break; @@ -5246,7 +5246,7 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str if (chan->imtu < skb->len) goto drop; - if (!chan->ops->recv(chan->data, skb)) + if (!chan->ops->recv(chan, skb)) return 0; drop: @@ -5272,7 +5272,7 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, u16 cid, if (chan->imtu < skb->len) goto drop; - if (!chan->ops->recv(chan->data, skb)) + if (!chan->ops->recv(chan, skb)) return 0; drop: diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index d244361a455c..db787f67c52a 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -872,9 +872,9 @@ static int l2cap_sock_release(struct socket *sock) return err; } -static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data) +static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) { - struct sock *sk, *parent = data; + struct sock *sk, *parent = chan->data; sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC); @@ -888,10 +888,10 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data) return l2cap_pi(sk)->chan; } -static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb) +static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) { int err; - struct sock *sk = data; + struct sock *sk = chan->data; struct l2cap_pinfo *pi = l2cap_pi(sk); lock_sock(sk); @@ -924,16 +924,16 @@ done: return err; } -static void l2cap_sock_close_cb(void *data) +static void l2cap_sock_close_cb(struct l2cap_chan *chan) { - struct sock *sk = data; + struct sock *sk = chan->data; l2cap_sock_kill(sk); } -static void l2cap_sock_state_change_cb(void *data, int state) +static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state) { - struct sock *sk = data; + struct sock *sk = chan->data; sk->sk_state = state; } -- cgit v1.2.3 From c0df7f6e06e1aeccee39c801af7f78cadeb9f345 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Sun, 27 May 2012 22:27:52 -0300 Subject: Bluetooth: Move clean up code and set of SOCK_ZAPPED to l2cap_sock.c This remove a bit more of socket code from l2cap core, this calls set the SOCK_ZAPPED and do some clean up depending on the socket state. Reported-by: Mat Martineau Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 55 +++++++------------------------------- net/bluetooth/l2cap_sock.c | 61 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index aa2dbc680d5c..76b0e7e5dec2 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -530,6 +530,7 @@ struct l2cap_ops { struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan); int (*recv) (struct l2cap_chan * chan, struct sk_buff *skb); + void (*teardown) (struct l2cap_chan *chan, int err); void (*close) (struct l2cap_chan *chan); void (*state_change) (struct l2cap_chan *chan, int state); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 7edc8146db26..1f4c72074154 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -493,9 +493,7 @@ static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) static void l2cap_chan_del(struct l2cap_chan *chan, int err) { - struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; - struct sock *parent = bt_sk(sk)->parent; __clear_chan_timer(chan); @@ -511,21 +509,8 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) hci_conn_put(conn->hcon); } - lock_sock(sk); - - __l2cap_state_change(chan, BT_CLOSED); - sock_set_flag(sk, SOCK_ZAPPED); - - if (err) - __l2cap_chan_set_err(chan, err); - - if (parent) { - bt_accept_unlink(sk); - parent->sk_data_ready(parent, 0); - } else - sk->sk_state_change(sk); - - release_sock(sk); + if (chan->ops->teardown) + chan->ops->teardown(chan, err); if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state)) return; @@ -554,25 +539,6 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) return; } -static void l2cap_chan_cleanup_listen(struct sock *parent) -{ - struct sock *sk; - - BT_DBG("parent %p", parent); - - /* Close not yet accepted channels */ - while ((sk = bt_accept_dequeue(parent, NULL))) { - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - - l2cap_chan_lock(chan); - __clear_chan_timer(chan); - l2cap_chan_close(chan, ECONNRESET); - l2cap_chan_unlock(chan); - - chan->ops->close(chan); - } -} - void l2cap_chan_close(struct l2cap_chan *chan, int reason) { struct l2cap_conn *conn = chan->conn; @@ -583,12 +549,8 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) switch (chan->state) { case BT_LISTEN: - lock_sock(sk); - l2cap_chan_cleanup_listen(sk); - - __l2cap_state_change(chan, BT_CLOSED); - sock_set_flag(sk, SOCK_ZAPPED); - release_sock(sk); + if (chan->ops->teardown) + chan->ops->teardown(chan, 0); break; case BT_CONNECTED: @@ -630,9 +592,8 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) break; default: - lock_sock(sk); - sock_set_flag(sk, SOCK_ZAPPED); - release_sock(sk); + if (chan->ops->teardown) + chan->ops->teardown(chan, 0); break; } } @@ -3419,7 +3380,9 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd /* Check if we already have channel with that dcid */ if (__l2cap_get_chan_by_dcid(conn, scid)) { - sock_set_flag(sk, SOCK_ZAPPED); + if (chan->ops->teardown) + chan->ops->teardown(chan, 0); + chan->ops->close(chan); goto response; } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index db787f67c52a..3f5946351fb9 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -872,6 +872,25 @@ static int l2cap_sock_release(struct socket *sock) return err; } +static void l2cap_sock_cleanup_listen(struct sock *parent) +{ + struct sock *sk; + + BT_DBG("parent %p", parent); + + /* Close not yet accepted channels */ + while ((sk = bt_accept_dequeue(parent, NULL))) { + struct l2cap_chan *chan = l2cap_pi(sk)->chan; + + l2cap_chan_lock(chan); + __clear_chan_timer(chan); + l2cap_chan_close(chan, ECONNRESET); + l2cap_chan_unlock(chan); + + l2cap_sock_kill(sk); + } +} + static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) { struct sock *sk, *parent = chan->data; @@ -931,6 +950,47 @@ static void l2cap_sock_close_cb(struct l2cap_chan *chan) l2cap_sock_kill(sk); } +static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err) +{ + struct sock *sk = chan->data; + struct sock *parent; + + lock_sock(sk); + + parent = bt_sk(sk)->parent; + + sock_set_flag(sk, SOCK_ZAPPED); + + switch (chan->state) { + case BT_OPEN: + case BT_BOUND: + case BT_CLOSED: + break; + case BT_LISTEN: + l2cap_sock_cleanup_listen(sk); + sk->sk_state = BT_CLOSED; + chan->state = BT_CLOSED; + + break; + default: + sk->sk_state = BT_CLOSED; + chan->state = BT_CLOSED; + + sk->sk_err = err; + + if (parent) { + bt_accept_unlink(sk); + parent->sk_data_ready(parent, 0); + } else { + sk->sk_state_change(sk); + } + + break; + } + + release_sock(sk); +} + static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state) { struct sock *sk = chan->data; @@ -959,6 +1019,7 @@ static struct l2cap_ops l2cap_chan_ops = { .new_connection = l2cap_sock_new_connection_cb, .recv = l2cap_sock_recv_cb, .close = l2cap_sock_close_cb, + .teardown = l2cap_sock_teardown_cb, .state_change = l2cap_sock_state_change_cb, .alloc_skb = l2cap_sock_alloc_skb_cb, }; -- cgit v1.2.3 From 54a59aa2b562872781d6a8fc89f300d360941691 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Sun, 27 May 2012 22:27:53 -0300 Subject: Bluetooth: Add l2cap_chan->ops->ready() This move socket specific code to l2cap_sock.c. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 18 +++--------------- net/bluetooth/l2cap_sock.c | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 76b0e7e5dec2..c5726c24ee03 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -534,6 +534,7 @@ struct l2cap_ops { void (*close) (struct l2cap_chan *chan); void (*state_change) (struct l2cap_chan *chan, int state); + void (*ready) (struct l2cap_chan *chan); struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan, unsigned long len, int nb); }; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 1f4c72074154..5947eb1c1bee 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -931,26 +931,14 @@ static void l2cap_send_conn_req(struct l2cap_chan *chan) static void l2cap_chan_ready(struct l2cap_chan *chan) { - struct sock *sk = chan->sk; - struct sock *parent; - - lock_sock(sk); - - parent = bt_sk(sk)->parent; - - BT_DBG("sk %p, parent %p", sk, parent); - /* This clears all conf flags, including CONF_NOT_COMPLETE */ chan->conf_state = 0; __clear_chan_timer(chan); - __l2cap_state_change(chan, BT_CONNECTED); - sk->sk_state_change(sk); + chan->state = BT_CONNECTED; - if (parent) - parent->sk_data_ready(parent, 0); - - release_sock(sk); + if (chan->ops->ready) + chan->ops->ready(chan); } static void l2cap_do_start(struct l2cap_chan *chan) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 3f5946351fb9..5563023001c6 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1014,6 +1014,26 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, return skb; } +static void l2cap_sock_ready_cb(struct l2cap_chan *chan) +{ + struct sock *sk = chan->data; + struct sock *parent; + + lock_sock(sk); + + parent = bt_sk(sk)->parent; + + BT_DBG("sk %p, parent %p", sk, parent); + + sk->sk_state = BT_CONNECTED; + sk->sk_state_change(sk); + + if (parent) + parent->sk_data_ready(parent, 0); + + release_sock(sk); +} + static struct l2cap_ops l2cap_chan_ops = { .name = "L2CAP Socket Interface", .new_connection = l2cap_sock_new_connection_cb, @@ -1021,6 +1041,7 @@ static struct l2cap_ops l2cap_chan_ops = { .close = l2cap_sock_close_cb, .teardown = l2cap_sock_teardown_cb, .state_change = l2cap_sock_state_change_cb, + .ready = l2cap_sock_ready_cb, .alloc_skb = l2cap_sock_alloc_skb_cb, }; -- cgit v1.2.3 From 466f8004f364e9cb46d9124109972489eccfb404 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 29 May 2012 13:59:01 +0300 Subject: Bluetooth: A2MP: Create A2MP channel Create and initialize fixed A2MP channel Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 6 ++++ net/bluetooth/Makefile | 3 +- net/bluetooth/a2mp.c | 69 +++++++++++++++++++++++++++++++++++++++++++ net/bluetooth/l2cap_core.c | 6 ++-- 4 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 net/bluetooth/a2mp.c (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index c5726c24ee03..aaba222306b6 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -52,6 +52,8 @@ #define L2CAP_CONN_TIMEOUT msecs_to_jiffies(40000) #define L2CAP_INFO_TIMEOUT msecs_to_jiffies(4000) +#define L2CAP_A2MP_DEFAULT_MTU 670 + /* L2CAP socket address */ struct sockaddr_l2 { sa_family_t l2_family; @@ -236,6 +238,7 @@ struct l2cap_conn_rsp { /* channel indentifier */ #define L2CAP_CID_SIGNALING 0x0001 #define L2CAP_CID_CONN_LESS 0x0002 +#define L2CAP_CID_A2MP 0x0003 #define L2CAP_CID_LE_DATA 0x0004 #define L2CAP_CID_LE_SIGNALING 0x0005 #define L2CAP_CID_SMP 0x0006 @@ -758,5 +761,8 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, void l2cap_chan_busy(struct l2cap_chan *chan, int busy); int l2cap_chan_check_security(struct l2cap_chan *chan); void l2cap_chan_set_defaults(struct l2cap_chan *chan); +int l2cap_ertm_init(struct l2cap_chan *chan); +void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); +void l2cap_chan_del(struct l2cap_chan *chan, int err); #endif /* __L2CAP_H */ diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 2dc5a5700f53..fa6d94a4602a 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -9,4 +9,5 @@ obj-$(CONFIG_BT_CMTP) += cmtp/ obj-$(CONFIG_BT_HIDP) += hidp/ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ - hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o + hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ + a2mp.o diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c new file mode 100644 index 000000000000..de455a264451 --- /dev/null +++ b/net/bluetooth/a2mp.c @@ -0,0 +1,69 @@ +/* + Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved. + Copyright (c) 2011,2012 Intel Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 and + only version 2 as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +#include +#include +#include + +static struct l2cap_ops a2mp_chan_ops = { + .name = "L2CAP A2MP channel", +}; + +static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn) +{ + struct l2cap_chan *chan; + int err; + + chan = l2cap_chan_create(); + if (!chan) + return NULL; + + BT_DBG("chan %p", chan); + + hci_conn_hold(conn->hcon); + + chan->omtu = L2CAP_A2MP_DEFAULT_MTU; + chan->imtu = L2CAP_A2MP_DEFAULT_MTU; + chan->flush_to = L2CAP_DEFAULT_FLUSH_TO; + + chan->ops = &a2mp_chan_ops; + + l2cap_chan_set_defaults(chan); + chan->remote_max_tx = chan->max_tx; + chan->remote_tx_win = chan->tx_win; + + chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO; + chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO; + + skb_queue_head_init(&chan->tx_q); + + chan->mode = L2CAP_MODE_ERTM; + + err = l2cap_ertm_init(chan); + if (err < 0) { + l2cap_chan_del(chan, 0); + return NULL; + } + + chan->conf_state = 0; + + l2cap_chan_add(conn, chan); + + chan->remote_mps = chan->omtu; + chan->mps = chan->omtu; + + chan->state = BT_CONNECTED; + + return chan; +} diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 778c0c8cdc59..2c616cf24c71 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -484,14 +484,14 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) list_add(&chan->list, &conn->chan_l); } -static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) +void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { mutex_lock(&conn->chan_lock); __l2cap_chan_add(conn, chan); mutex_unlock(&conn->chan_lock); } -static void l2cap_chan_del(struct l2cap_chan *chan, int err) +void l2cap_chan_del(struct l2cap_chan *chan, int err) { struct l2cap_conn *conn = chan->conn; @@ -2691,7 +2691,7 @@ static void l2cap_ack_timeout(struct work_struct *work) l2cap_chan_put(chan); } -static inline int l2cap_ertm_init(struct l2cap_chan *chan) +int l2cap_ertm_init(struct l2cap_chan *chan) { int err; -- cgit v1.2.3 From 9740e49d17e55f3832661fd99a8e0a17e921a82e Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 29 May 2012 13:59:02 +0300 Subject: Bluetooth: A2MP: AMP Manager basic functions Define AMP Manager and some basic functions. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 30 ++++++++++++++++++++++ include/net/bluetooth/hci_core.h | 1 + net/bluetooth/a2mp.c | 54 ++++++++++++++++++++++++++++++++++++++++ net/bluetooth/hci_conn.c | 4 +++ 4 files changed, 89 insertions(+) create mode 100644 include/net/bluetooth/a2mp.h (limited to 'include') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h new file mode 100644 index 000000000000..ff4754000cf8 --- /dev/null +++ b/include/net/bluetooth/a2mp.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved. + Copyright (c) 2011,2012 Intel Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 and + only version 2 as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +#ifndef __A2MP_H +#define __A2MP_H + +struct amp_mgr { + struct l2cap_conn *l2cap_conn; + struct l2cap_chan *a2mp_chan; + struct kref kref; + __u8 ident; + __u8 handle; + unsigned long flags; +}; + +void amp_mgr_get(struct amp_mgr *mgr); +int amp_mgr_put(struct amp_mgr *mgr); + +#endif /* __A2MP_H */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d584a47d1c86..6e64b76e30aa 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -332,6 +332,7 @@ struct hci_conn { void *l2cap_data; void *sco_data; void *smp_conn; + struct amp_mgr *amp_mgr; struct hci_conn *link; diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index de455a264451..3c241c2b3e1a 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -15,6 +15,7 @@ #include #include #include +#include static struct l2cap_ops a2mp_chan_ops = { .name = "L2CAP A2MP channel", @@ -67,3 +68,56 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn) return chan; } + +/* AMP Manager functions */ +void amp_mgr_get(struct amp_mgr *mgr) +{ + BT_DBG("mgr %p", mgr); + + kref_get(&mgr->kref); +} + +static void amp_mgr_destroy(struct kref *kref) +{ + struct amp_mgr *mgr = container_of(kref, struct amp_mgr, kref); + + BT_DBG("mgr %p", mgr); + + kfree(mgr); +} + +int amp_mgr_put(struct amp_mgr *mgr) +{ + BT_DBG("mgr %p", mgr); + + return kref_put(&mgr->kref, &_mgr_destroy); +} + +static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn) +{ + struct amp_mgr *mgr; + struct l2cap_chan *chan; + + mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); + if (!mgr) + return NULL; + + BT_DBG("conn %p mgr %p", conn, mgr); + + mgr->l2cap_conn = conn; + + chan = a2mp_chan_open(conn); + if (!chan) { + kfree(mgr); + return NULL; + } + + mgr->a2mp_chan = chan; + chan->data = mgr; + + conn->hcon->amp_mgr = mgr; + + kref_init(&mgr->kref); + + return mgr; +} diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 126876d915f5..1458667b2845 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -28,6 +28,7 @@ #include #include +#include static void hci_le_connect(struct hci_conn *conn) { @@ -411,6 +412,9 @@ int hci_conn_del(struct hci_conn *conn) hci_chan_list_flush(conn); + if (conn->amp_mgr) + amp_mgr_put(conn->amp_mgr); + hci_conn_hash_del(hdev, conn); if (hdev->notify) hdev->notify(hdev, HCI_NOTIFY_CONN_DEL); -- cgit v1.2.3 From f6d3c6e783b0e9f75b18232f8ff8cd5dbc3f7301 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 29 May 2012 13:59:03 +0300 Subject: Bluetooth: A2MP: Build and Send msg helpers Helper function to build and send A2MP messages. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 7 +++++++ net/bluetooth/a2mp.c | 46 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index ff4754000cf8..654df60cfd6d 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -24,6 +24,13 @@ struct amp_mgr { unsigned long flags; }; +struct a2mp_cmd { + __u8 code; + __u8 ident; + __le16 len; + __u8 data[0]; +} __packed; + void amp_mgr_get(struct amp_mgr *mgr); int amp_mgr_put(struct amp_mgr *mgr); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 3c241c2b3e1a..53f49a0b7f9a 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -17,6 +17,52 @@ #include #include +/* A2MP build & send command helper functions */ +static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data) +{ + struct a2mp_cmd *cmd; + int plen; + + plen = sizeof(*cmd) + len; + cmd = kzalloc(plen, GFP_KERNEL); + if (!cmd) + return NULL; + + cmd->code = code; + cmd->ident = ident; + cmd->len = cpu_to_le16(len); + + memcpy(cmd->data, data, len); + + return cmd; +} + +static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, + void *data) +{ + struct l2cap_chan *chan = mgr->a2mp_chan; + struct a2mp_cmd *cmd; + u16 total_len = len + sizeof(*cmd); + struct kvec iv; + struct msghdr msg; + + cmd = __a2mp_build(code, ident, len, data); + if (!cmd) + return; + + iv.iov_base = cmd; + iv.iov_len = total_len; + + memset(&msg, 0, sizeof(msg)); + + msg.msg_iov = (struct iovec *) &iv; + msg.msg_iovlen = 1; + + l2cap_chan_send(chan, &msg, total_len, 0); + + kfree(cmd); +} + static struct l2cap_ops a2mp_chan_ops = { .name = "L2CAP A2MP channel", }; -- cgit v1.2.3 From b9058fb67c42851b4f852d90b11f43279586aae9 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 29 May 2012 13:59:05 +0300 Subject: Bluetooth: A2MP: Definitions for A2MP commands Define A2MP command IDs and packet structures. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 73 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index 654df60cfd6d..7cbeb911fbd1 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -31,6 +31,79 @@ struct a2mp_cmd { __u8 data[0]; } __packed; +/* A2MP command codes */ +#define A2MP_COMMAND_REJ 0x01 +struct a2mp_cmd_rej { + __le16 reason; + __u8 data[0]; +} __packed; + +#define A2MP_DISCOVER_REQ 0x02 +struct a2mp_discov_req { + __le16 mtu; + __le16 ext_feat; +} __packed; + +struct a2mp_cl { + __u8 id; + __u8 type; + __u8 status; +} __packed; + +#define A2MP_DISCOVER_RSP 0x03 +struct a2mp_discov_rsp { + __le16 mtu; + __le16 ext_feat; + struct a2mp_cl cl[0]; +} __packed; + +#define A2MP_CHANGE_NOTIFY 0x04 +#define A2MP_CHANGE_RSP 0x05 + +#define A2MP_GETINFO_REQ 0x06 +struct a2mp_info_req { + __u8 id; +} __packed; + +#define A2MP_GETINFO_RSP 0x07 +struct a2mp_info_rsp { + __u8 id; + __u8 status; + __le32 total_bw; + __le32 max_bw; + __le32 min_latency; + __le16 pal_cap; + __le16 assoc_size; +} __packed; + +#define A2MP_GETAMPASSOC_REQ 0x08 +struct a2mp_amp_assoc_req { + __u8 id; +} __packed; + +#define A2MP_GETAMPASSOC_RSP 0x09 +struct a2mp_amp_assoc_rsp { + __u8 id; + __u8 status; + __u8 amp_assoc[0]; +} __packed; + +#define A2MP_CREATEPHYSLINK_REQ 0x0A +#define A2MP_DISCONNPHYSLINK_REQ 0x0C +struct a2mp_physlink_req { + __u8 local_id; + __u8 remote_id; + __u8 amp_assoc[0]; +} __packed; + +#define A2MP_CREATEPHYSLINK_RSP 0x0B +#define A2MP_DISCONNPHYSLINK_RSP 0x0D +struct a2mp_physlink_rsp { + __u8 local_id; + __u8 remote_id; + __u8 status; +} __packed; + void amp_mgr_get(struct amp_mgr *mgr); int amp_mgr_put(struct amp_mgr *mgr); -- cgit v1.2.3 From e7af522e04bcf68caae6802722efc5c6e8fa63a7 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 29 May 2012 13:59:06 +0300 Subject: Bluetooth: A2MP: Define A2MP status codes A2MP status codes copied from Bluez patch sent by Peter Krystad . Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index 7cbeb911fbd1..391acd7a67d4 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -104,6 +104,16 @@ struct a2mp_physlink_rsp { __u8 status; } __packed; +/* A2MP response status */ +#define A2MP_STATUS_SUCCESS 0x00 +#define A2MP_STATUS_INVALID_CTRL_ID 0x01 +#define A2MP_STATUS_UNABLE_START_LINK_CREATION 0x02 +#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS 0x02 +#define A2MP_STATUS_COLLISION_OCCURED 0x03 +#define A2MP_STATUS_DISCONN_REQ_RECVD 0x04 +#define A2MP_STATUS_PHYS_LINK_EXISTS 0x05 +#define A2MP_STATUS_SECURITY_VIOLATION 0x06 + void amp_mgr_get(struct amp_mgr *mgr); int amp_mgr_put(struct amp_mgr *mgr); -- cgit v1.2.3 From 8598d064cbf22b2d84c7cd8a9fcb97138baffe3f Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 29 May 2012 13:59:09 +0300 Subject: Bluetooth: A2MP: Process A2MP Discover Request Adds helper functions to count HCI devs and process A2MP Discover Request, code makes sure that first controller in the list is BREDR one. Trace is shown below: ... > ACL data: handle 11 flags 0x02 dlen 16 A2MP: Discover req: mtu/mps 670 mask: 0x0000 < ACL data: handle 11 flags 0x00 dlen 22 A2MP: Discover rsp: mtu/mps 670 mask: 0x0000 Controller list: id 0 type 0 (BR-EDR) status 0x01 (Bluetooth only) id 1 type 1 (802.11 AMP) status 0x01 (Bluetooth only) ... Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 2 + include/net/bluetooth/hci.h | 3 ++ include/net/bluetooth/hci_core.h | 13 ++++++ net/bluetooth/a2mp.c | 85 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index 391acd7a67d4..96f9cc2cf59b 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -15,6 +15,8 @@ #ifndef __A2MP_H #define __A2MP_H +#define A2MP_FEAT_EXT 0x8000 + struct amp_mgr { struct l2cap_conn *l2cap_conn; struct l2cap_chan *a2mp_chan; diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index de09a26e4223..66af2c6193d5 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -59,6 +59,9 @@ #define HCI_BREDR 0x00 #define HCI_AMP 0x01 +/* First BR/EDR Controller shall have ID = 0 */ +#define HCI_BREDR_ID 0 + /* HCI device quirks */ enum { HCI_QUIRK_RESET_ON_CLOSE, diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6e64b76e30aa..20fd57367ddc 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -641,6 +641,19 @@ static inline void hci_set_drvdata(struct hci_dev *hdev, void *data) dev_set_drvdata(&hdev->dev, data); } +/* hci_dev_list shall be locked */ +static inline uint8_t __hci_num_ctrl(void) +{ + uint8_t count = 0; + struct list_head *p; + + list_for_each(p, &hci_dev_list) { + count++; + } + + return count; +} + struct hci_dev *hci_dev_get(int index); struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 188b42120074..1cc920a62b0f 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -63,6 +63,36 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, kfree(cmd); } +static inline void __a2mp_cl_bredr(struct a2mp_cl *cl) +{ + cl->id = 0; + cl->type = 0; + cl->status = 1; +} + +/* hci_dev_list shall be locked */ +static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl, u8 num_ctrl) +{ + int i = 0; + struct hci_dev *hdev; + + __a2mp_cl_bredr(cl); + + list_for_each_entry(hdev, &hci_dev_list, list) { + /* Iterate through AMP controllers */ + if (hdev->id == HCI_BREDR_ID) + continue; + + /* Starting from second entry */ + if (++i >= num_ctrl) + return; + + cl[i].id = hdev->id; + cl[i].type = hdev->amp_type; + cl[i].status = hdev->amp_status; + } +} + /* Processing A2MP messages */ static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) @@ -79,6 +109,58 @@ static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb, return 0; } +static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb, + struct a2mp_cmd *hdr) +{ + struct a2mp_discov_req *req = (void *) skb->data; + u16 len = le16_to_cpu(hdr->len); + struct a2mp_discov_rsp *rsp; + u16 ext_feat; + u8 num_ctrl; + + if (len < sizeof(*req)) + return -EINVAL; + + skb_pull(skb, sizeof(*req)); + + ext_feat = le16_to_cpu(req->ext_feat); + + BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(req->mtu), ext_feat); + + /* check that packet is not broken for now */ + while (ext_feat & A2MP_FEAT_EXT) { + if (len < sizeof(ext_feat)) + return -EINVAL; + + ext_feat = get_unaligned_le16(skb->data); + BT_DBG("efm 0x%4.4x", ext_feat); + len -= sizeof(ext_feat); + skb_pull(skb, sizeof(ext_feat)); + } + + read_lock(&hci_dev_list_lock); + + num_ctrl = __hci_num_ctrl(); + len = num_ctrl * sizeof(struct a2mp_cl) + sizeof(*rsp); + rsp = kmalloc(len, GFP_ATOMIC); + if (!rsp) { + read_unlock(&hci_dev_list_lock); + return -ENOMEM; + } + + rsp->mtu = __constant_cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU); + rsp->ext_feat = 0; + + __a2mp_add_cl(mgr, rsp->cl, num_ctrl); + + read_unlock(&hci_dev_list_lock); + + a2mp_send(mgr, A2MP_DISCOVER_RSP, hdr->ident, len, rsp); + + kfree(rsp); + return 0; +} + /* Handle A2MP signalling */ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) { @@ -109,6 +191,9 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) break; case A2MP_DISCOVER_REQ: + err = a2mp_discover_req(mgr, skb, hdr); + break; + case A2MP_CHANGE_NOTIFY: case A2MP_GETINFO_REQ: case A2MP_GETAMPASSOC_REQ: -- cgit v1.2.3 From 416fa7527d6bf658e5517ea36d2de9270be2c11e Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 29 May 2012 13:59:16 +0300 Subject: Bluetooth: A2MP: Handling fixed channels A2MP fixed channel do not have sk Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/a2mp.c | 3 +-- net/bluetooth/l2cap_core.c | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index aaba222306b6..a00b43ecbc77 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -581,6 +581,7 @@ struct l2cap_conn { #define L2CAP_CHAN_RAW 1 #define L2CAP_CHAN_CONN_LESS 2 #define L2CAP_CHAN_CONN_ORIENTED 3 +#define L2CAP_CHAN_CONN_FIX_A2MP 4 /* ----- L2CAP socket info ----- */ #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk) diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 6a933dab1b7f..f1ec1b1d308f 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -483,8 +483,7 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn) hci_conn_hold(conn->hcon); - chan->omtu = L2CAP_A2MP_DEFAULT_MTU; - chan->imtu = L2CAP_A2MP_DEFAULT_MTU; + chan->chan_type = L2CAP_CHAN_CONN_FIX_A2MP; chan->flush_to = L2CAP_DEFAULT_FLUSH_TO; chan->ops = &a2mp_chan_ops; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 2c616cf24c71..fc572795497a 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -465,6 +465,13 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) chan->omtu = L2CAP_DEFAULT_MTU; break; + case L2CAP_CHAN_CONN_FIX_A2MP: + chan->scid = L2CAP_CID_A2MP; + chan->dcid = L2CAP_CID_A2MP; + chan->omtu = L2CAP_A2MP_DEFAULT_MTU; + chan->imtu = L2CAP_A2MP_DEFAULT_MTU; + break; + default: /* Raw socket can send/recv signalling messages only */ chan->scid = L2CAP_CID_SIGNALING; @@ -1001,6 +1008,11 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c __clear_ack_timer(chan); } + if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) { + __l2cap_state_change(chan, BT_DISCONN); + return; + } + req.dcid = cpu_to_le16(chan->dcid); req.scid = cpu_to_le16(chan->scid); l2cap_send_cmd(conn, l2cap_get_ident(conn), @@ -1195,6 +1207,11 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) l2cap_chan_lock(chan); + if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) { + l2cap_chan_unlock(chan); + continue; + } + if (conn->hcon->type == LE_LINK) { if (smp_conn_security(conn, chan->sec_level)) l2cap_chan_ready(chan); -- cgit v1.2.3 From 97e8e89d2d8185b7644c9941636d3682eedc517b Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 29 May 2012 13:59:17 +0300 Subject: Bluetooth: A2MP: Manage incoming connections Handle incoming A2MP connection by creating AMP manager and processing A2MP messages. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 4 ++++ net/bluetooth/a2mp.c | 16 ++++++++++++++++ net/bluetooth/l2cap_core.c | 19 +++++++++++++++---- 3 files changed, 35 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index 96f9cc2cf59b..6a76e0a0705e 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -15,6 +15,8 @@ #ifndef __A2MP_H #define __A2MP_H +#include + #define A2MP_FEAT_EXT 0x8000 struct amp_mgr { @@ -118,5 +120,7 @@ struct a2mp_physlink_rsp { void amp_mgr_get(struct amp_mgr *mgr); int amp_mgr_put(struct amp_mgr *mgr); +struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, + struct sk_buff *skb); #endif /* __A2MP_H */ diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index f1ec1b1d308f..e08ca2ac31aa 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -569,3 +569,19 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn) return mgr; } + +struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, + struct sk_buff *skb) +{ + struct amp_mgr *mgr; + + mgr = amp_mgr_create(conn); + if (!mgr) { + BT_ERR("Could not create AMP manager"); + return NULL; + } + + BT_DBG("mgr: %p chan %p", mgr, mgr->a2mp_chan); + + return mgr->a2mp_chan; +} diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index fc572795497a..3daac2c6b7b4 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -37,6 +37,7 @@ #include #include #include +#include bool disable_ertm; @@ -5132,10 +5133,20 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk chan = l2cap_get_chan_by_scid(conn, cid); if (!chan) { - BT_DBG("unknown cid 0x%4.4x", cid); - /* Drop packet and return */ - kfree_skb(skb); - return 0; + if (cid == L2CAP_CID_A2MP) { + chan = a2mp_channel_create(conn, skb); + if (!chan) { + kfree_skb(skb); + return 0; + } + + l2cap_chan_lock(chan); + } else { + BT_DBG("unknown cid 0x%4.4x", cid); + /* Drop packet and return */ + kfree_skb(skb); + return 0; + } } BT_DBG("chan %p, len %d", chan, skb->len); -- cgit v1.2.3 From 1afd5be87e30997a5623260d9177ed62cf88adbe Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Mon, 28 May 2012 11:47:20 +0200 Subject: Bluetooth: Remove unused HCI timeouts definitions Those are not used anywhere in code (and never were since introduction in 2006) so just remove them. Signed-off-by: Szymon Janc Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 66af2c6193d5..3f5d682e866f 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -139,10 +139,8 @@ enum { #define HCIINQUIRY _IOR('H', 240, int) /* HCI timeouts */ -#define HCI_CONNECT_TIMEOUT (40000) /* 40 seconds */ #define HCI_DISCONN_TIMEOUT (2000) /* 2 seconds */ #define HCI_PAIRING_TIMEOUT (60000) /* 60 seconds */ -#define HCI_IDLE_TIMEOUT (6000) /* 6 seconds */ #define HCI_INIT_TIMEOUT (10000) /* 10 seconds */ #define HCI_CMD_TIMEOUT (1000) /* 1 seconds */ #define HCI_ACL_TX_TIMEOUT (45000) /* 45 seconds */ -- cgit v1.2.3 From 8c3a4f004e706fd7e681c68c6de4946c8c76b976 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 31 May 2012 17:01:35 -0300 Subject: Bluetooth: Rename L2CAP_LE_DEFAULT_MTU This patch renames L2CAP_LE_DEFAULT_MTU macro to L2CAP_LE_MIN_MTU since it represents the minimum MTU value, not the default MTU value for LE. Signed-off-by: Andre Guedes Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 2 +- net/bluetooth/l2cap_sock.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index a00b43ecbc77..ce99c5683d9e 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -40,11 +40,11 @@ #define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */ #define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */ #define L2CAP_DEFAULT_ACK_TO 200 -#define L2CAP_LE_DEFAULT_MTU 23 #define L2CAP_DEFAULT_MAX_SDU_SIZE 0xFFFF #define L2CAP_DEFAULT_SDU_ITIME 0xFFFFFFFF #define L2CAP_DEFAULT_ACC_LAT 0xFFFFFFFF #define L2CAP_BREDR_MAX_PAYLOAD 1019 /* 3-DH5 packet */ +#define L2CAP_LE_MIN_MTU 23 #define L2CAP_DISC_TIMEOUT msecs_to_jiffies(100) #define L2CAP_DISC_REJ_TIMEOUT msecs_to_jiffies(5000) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index ab5868d94307..a4bb27e8427e 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -449,7 +449,7 @@ static bool l2cap_valid_mtu(struct l2cap_chan *chan, u16 mtu) { switch (chan->scid) { case L2CAP_CID_LE_DATA: - if (mtu < L2CAP_LE_DEFAULT_MTU) + if (mtu < L2CAP_LE_MIN_MTU) return false; break; -- cgit v1.2.3 From 7e1af8a3a51dbf5dc7392fb294a0830f7e853aa8 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Tue, 29 May 2012 13:19:26 -0300 Subject: Bluetooth: Create empty l2cap ops function A2MP doesn't use part of the L2CAP chan ops API so we just create general empty function instead of the A2MP specific one. Signed-off-by: Gustavo Padovan Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 12 ++++++++++++ net/bluetooth/a2mp.c | 23 +++-------------------- 2 files changed, 15 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index ce99c5683d9e..d80e3f0691b4 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -740,6 +740,18 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq) return (seq + 1) % (chan->tx_win_max + 1); } +static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan *chan) +{ + return NULL; +} + +static inline void l2cap_chan_no_teardown(struct l2cap_chan *chan, int err) +{ +} + +static inline void l2cap_chan_no_ready(struct l2cap_chan *chan) +{ +} extern bool disable_ertm; diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 0772c680abe6..fb93250b3938 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -440,23 +440,6 @@ static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan, return bt_skb_alloc(len, GFP_KERNEL); } -static struct l2cap_chan *a2mp_chan_no_new_conn_cb(struct l2cap_chan *chan) -{ - BT_ERR("new_connection for chan %p not implemented", chan); - - return NULL; -} - -static void a2mp_chan_no_teardown_cb(struct l2cap_chan *chan, int err) -{ - BT_ERR("teardown for chan %p not implemented", chan); -} - -static void a2mp_chan_no_ready(struct l2cap_chan *chan) -{ - BT_ERR("ready for chan %p not implemented", chan); -} - static struct l2cap_ops a2mp_chan_ops = { .name = "L2CAP A2MP channel", .recv = a2mp_chan_recv_cb, @@ -465,9 +448,9 @@ static struct l2cap_ops a2mp_chan_ops = { .alloc_skb = a2mp_chan_alloc_skb_cb, /* Not implemented for A2MP */ - .new_connection = a2mp_chan_no_new_conn_cb, - .teardown = a2mp_chan_no_teardown_cb, - .ready = a2mp_chan_no_ready, + .new_connection = l2cap_chan_no_new_connection, + .teardown = l2cap_chan_no_teardown, + .ready = l2cap_chan_no_ready, }; static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn) -- cgit v1.2.3 From cd1678f963298a9e777f3edb72d28bc18a3a32c2 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 29 May 2012 12:41:19 +0200 Subject: iio: frequency: New driver for AD9523 SPI Low Jitter Clock Generator Changes since V1: Apply Jonathan's review feedback: Revise device status attribute names, and split documentation into two sections. Add additional comments, and fix indention issues. Remove pointless zero initializations. Revise return value handling. Simplify some code sections. Split store_eeprom and sync handling into separate functions. Use strtobool where applicable. Document platform data structures using kernel-doc style. Use dev_to_iio_dev write_raw IIO_CHAN_INFO_FREQUENCY: Reject values <= 0 Make patch target drivers/iio Changes since V2: Use for_each_clear_bit() and __set_bit() where applicable. Add descriptive comment. Avoid temporary for struct regulator. spi_device_id name use ad9523-1, ad9523 will be added later. Signed-off-by: Michael Hennerich Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-bus-iio-frequency-ad9523 | 37 + drivers/iio/Kconfig | 1 + drivers/iio/Makefile | 1 + drivers/iio/frequency/Kconfig | 23 + drivers/iio/frequency/Makefile | 5 + drivers/iio/frequency/ad9523.c | 1057 ++++++++++++++++++++ include/linux/iio/frequency/ad9523.h | 195 ++++ 7 files changed, 1319 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 create mode 100644 drivers/iio/frequency/Kconfig create mode 100644 drivers/iio/frequency/Makefile create mode 100644 drivers/iio/frequency/ad9523.c create mode 100644 include/linux/iio/frequency/ad9523.h (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 new file mode 100644 index 000000000000..2ce9c3f68eee --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 @@ -0,0 +1,37 @@ +What: /sys/bus/iio/devices/iio:deviceX/pll2_feedback_clk_present +What: /sys/bus/iio/devices/iio:deviceX/pll2_reference_clk_present +What: /sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_a_present +What: /sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_b_present +What: /sys/bus/iio/devices/iio:deviceX/pll1_reference_clk_test_present +What: /sys/bus/iio/devices/iio:deviceX/vcxo_clk_present +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns either '1' or '0'. + '1' means that the clock in question is present. + '0' means that the clock is missing. + +What: /sys/bus/iio/devices/iio:deviceX/pllY_locked +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns either '1' or '0'. '1' means that the + pllY is locked. + +What: /sys/bus/iio/devices/iio:deviceX/store_eeprom +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + Writing '1' stores the current device configuration into + on-chip EEPROM. After power-up or chip reset the device will + automatically load the saved configuration. + +What: /sys/bus/iio/devices/iio:deviceX/sync_dividers +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + Writing '1' triggers the clock distribution synchronization + functionality. All dividers are reset and the channels start + with their predefined phase offsets (out_altvoltageY_phase). + Writing this file has the effect as driving the external + /SYNC pin low. diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index cacc74d70241..64c88e5cda4d 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -51,5 +51,6 @@ config IIO_CONSUMERS_PER_TRIGGER source "drivers/iio/adc/Kconfig" source "drivers/iio/amplifiers/Kconfig" source "drivers/iio/light/Kconfig" +source "drivers/iio/frequency/Kconfig" endif # IIO diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 060b674d278c..bd801c0bbc2f 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o obj-y += adc/ obj-y += amplifiers/ obj-y += light/ +obj-y += frequency/ diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig new file mode 100644 index 000000000000..0458c92464a3 --- /dev/null +++ b/drivers/iio/frequency/Kconfig @@ -0,0 +1,23 @@ +# +# Frequency +# Direct Digital Synthesis drivers (DDS) +# Clock Distribution device drivers +# Phase-Locked Loop (PLL) frequency synthesizers +# + +menu "Frequency Synthesizers DDS/PLL" + +menu "Clock Generator/Distribution" + +config AD9523 + tristate "Analog Devices AD9523 Low Jitter Clock Generator" + depends on SPI + help + Say yes here to build support for Analog Devices AD9523 Low Jitter + Clock Generator. The driver provides direct access via sysfs. + + To compile this driver as a module, choose M here: the + module will be called ad9523. + +endmenu +endmenu diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile new file mode 100644 index 000000000000..1b5b22417da1 --- /dev/null +++ b/drivers/iio/frequency/Makefile @@ -0,0 +1,5 @@ +# +# Makefile iio/frequency +# + +obj-$(CONFIG_AD9523) += ad9523.o diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c new file mode 100644 index 000000000000..7272924484c1 --- /dev/null +++ b/drivers/iio/frequency/ad9523.c @@ -0,0 +1,1057 @@ +/* + * AD9523 SPI Low Jitter Clock Generator + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define AD9523_READ (1 << 15) +#define AD9523_WRITE (0 << 15) +#define AD9523_CNT(x) (((x) - 1) << 13) +#define AD9523_ADDR(x) ((x) & 0xFFF) + +#define AD9523_R1B (1 << 16) +#define AD9523_R2B (2 << 16) +#define AD9523_R3B (3 << 16) +#define AD9523_TRANSF_LEN(x) ((x) >> 16) + +#define AD9523_SERIAL_PORT_CONFIG (AD9523_R1B | 0x0) +#define AD9523_VERSION_REGISTER (AD9523_R1B | 0x2) +#define AD9523_PART_REGISTER (AD9523_R1B | 0x3) +#define AD9523_READBACK_CTRL (AD9523_R1B | 0x4) + +#define AD9523_EEPROM_CUSTOMER_VERSION_ID (AD9523_R2B | 0x6) + +#define AD9523_PLL1_REF_A_DIVIDER (AD9523_R2B | 0x11) +#define AD9523_PLL1_REF_B_DIVIDER (AD9523_R2B | 0x13) +#define AD9523_PLL1_REF_TEST_DIVIDER (AD9523_R1B | 0x14) +#define AD9523_PLL1_FEEDBACK_DIVIDER (AD9523_R2B | 0x17) +#define AD9523_PLL1_CHARGE_PUMP_CTRL (AD9523_R2B | 0x19) +#define AD9523_PLL1_INPUT_RECEIVERS_CTRL (AD9523_R1B | 0x1A) +#define AD9523_PLL1_REF_CTRL (AD9523_R1B | 0x1B) +#define AD9523_PLL1_MISC_CTRL (AD9523_R1B | 0x1C) +#define AD9523_PLL1_LOOP_FILTER_CTRL (AD9523_R1B | 0x1D) + +#define AD9523_PLL2_CHARGE_PUMP (AD9523_R1B | 0xF0) +#define AD9523_PLL2_FEEDBACK_DIVIDER_AB (AD9523_R1B | 0xF1) +#define AD9523_PLL2_CTRL (AD9523_R1B | 0xF2) +#define AD9523_PLL2_VCO_CTRL (AD9523_R1B | 0xF3) +#define AD9523_PLL2_VCO_DIVIDER (AD9523_R1B | 0xF4) +#define AD9523_PLL2_LOOP_FILTER_CTRL (AD9523_R2B | 0xF6) +#define AD9523_PLL2_R2_DIVIDER (AD9523_R1B | 0xF7) + +#define AD9523_CHANNEL_CLOCK_DIST(ch) (AD9523_R3B | (0x192 + 3 * ch)) + +#define AD9523_PLL1_OUTPUT_CTRL (AD9523_R1B | 0x1BA) +#define AD9523_PLL1_OUTPUT_CHANNEL_CTRL (AD9523_R1B | 0x1BB) + +#define AD9523_READBACK_0 (AD9523_R1B | 0x22C) +#define AD9523_READBACK_1 (AD9523_R1B | 0x22D) + +#define AD9523_STATUS_SIGNALS (AD9523_R3B | 0x232) +#define AD9523_POWER_DOWN_CTRL (AD9523_R1B | 0x233) +#define AD9523_IO_UPDATE (AD9523_R1B | 0x234) + +#define AD9523_EEPROM_DATA_XFER_STATUS (AD9523_R1B | 0xB00) +#define AD9523_EEPROM_ERROR_READBACK (AD9523_R1B | 0xB01) +#define AD9523_EEPROM_CTRL1 (AD9523_R1B | 0xB02) +#define AD9523_EEPROM_CTRL2 (AD9523_R1B | 0xB03) + +/* AD9523_SERIAL_PORT_CONFIG */ + +#define AD9523_SER_CONF_SDO_ACTIVE (1 << 7) +#define AD9523_SER_CONF_SOFT_RESET (1 << 5) + +/* AD9523_READBACK_CTRL */ +#define AD9523_READBACK_CTRL_READ_BUFFERED (1 << 0) + +/* AD9523_PLL1_CHARGE_PUMP_CTRL */ +#define AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(x) (((x) / 500) & 0x7F) +#define AD9523_PLL1_CHARGE_PUMP_TRISTATE (1 << 7) +#define AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL (3 << 8) +#define AD9523_PLL1_CHARGE_PUMP_MODE_PUMP_DOWN (2 << 8) +#define AD9523_PLL1_CHARGE_PUMP_MODE_PUMP_UP (1 << 8) +#define AD9523_PLL1_CHARGE_PUMP_MODE_TRISTATE (0 << 8) +#define AD9523_PLL1_BACKLASH_PW_MIN (0 << 10) +#define AD9523_PLL1_BACKLASH_PW_LOW (1 << 10) +#define AD9523_PLL1_BACKLASH_PW_HIGH (2 << 10) +#define AD9523_PLL1_BACKLASH_PW_MAX (3 << 10) + +/* AD9523_PLL1_INPUT_RECEIVERS_CTRL */ +#define AD9523_PLL1_REF_TEST_RCV_EN (1 << 7) +#define AD9523_PLL1_REFB_DIFF_RCV_EN (1 << 6) +#define AD9523_PLL1_REFA_DIFF_RCV_EN (1 << 5) +#define AD9523_PLL1_REFB_RCV_EN (1 << 4) +#define AD9523_PLL1_REFA_RCV_EN (1 << 3) +#define AD9523_PLL1_REFA_REFB_PWR_CTRL_EN (1 << 2) +#define AD9523_PLL1_OSC_IN_CMOS_NEG_INP_EN (1 << 1) +#define AD9523_PLL1_OSC_IN_DIFF_EN (1 << 0) + +/* AD9523_PLL1_REF_CTRL */ +#define AD9523_PLL1_BYPASS_REF_TEST_DIV_EN (1 << 7) +#define AD9523_PLL1_BYPASS_FEEDBACK_DIV_EN (1 << 6) +#define AD9523_PLL1_ZERO_DELAY_MODE_INT (1 << 5) +#define AD9523_PLL1_ZERO_DELAY_MODE_EXT (0 << 5) +#define AD9523_PLL1_OSC_IN_PLL_FEEDBACK_EN (1 << 4) +#define AD9523_PLL1_ZD_IN_CMOS_NEG_INP_EN (1 << 3) +#define AD9523_PLL1_ZD_IN_DIFF_EN (1 << 2) +#define AD9523_PLL1_REFB_CMOS_NEG_INP_EN (1 << 1) +#define AD9523_PLL1_REFA_CMOS_NEG_INP_EN (1 << 0) + +/* AD9523_PLL1_MISC_CTRL */ +#define AD9523_PLL1_REFB_INDEP_DIV_CTRL_EN (1 << 7) +#define AD9523_PLL1_OSC_CTRL_FAIL_VCC_BY2_EN (1 << 6) +#define AD9523_PLL1_REF_MODE(x) ((x) << 2) +#define AD9523_PLL1_BYPASS_REFB_DIV (1 << 1) +#define AD9523_PLL1_BYPASS_REFA_DIV (1 << 0) + +/* AD9523_PLL1_LOOP_FILTER_CTRL */ +#define AD9523_PLL1_LOOP_FILTER_RZERO(x) ((x) & 0xF) + +/* AD9523_PLL2_CHARGE_PUMP */ +#define AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(x) ((x) / 3500) + +/* AD9523_PLL2_FEEDBACK_DIVIDER_AB */ +#define AD9523_PLL2_FB_NDIV_A_CNT(x) (((x) & 0x3) << 6) +#define AD9523_PLL2_FB_NDIV_B_CNT(x) (((x) & 0x3F) << 0) +#define AD9523_PLL2_FB_NDIV(a, b) (4 * (b) + (a)) + +/* AD9523_PLL2_CTRL */ +#define AD9523_PLL2_CHARGE_PUMP_MODE_NORMAL (3 << 0) +#define AD9523_PLL2_CHARGE_PUMP_MODE_PUMP_DOWN (2 << 0) +#define AD9523_PLL2_CHARGE_PUMP_MODE_PUMP_UP (1 << 0) +#define AD9523_PLL2_CHARGE_PUMP_MODE_TRISTATE (0 << 0) +#define AD9523_PLL2_BACKLASH_PW_MIN (0 << 2) +#define AD9523_PLL2_BACKLASH_PW_LOW (1 << 2) +#define AD9523_PLL2_BACKLASH_PW_HIGH (2 << 2) +#define AD9523_PLL2_BACKLASH_PW_MAX (3 << 1) +#define AD9523_PLL2_BACKLASH_CTRL_EN (1 << 4) +#define AD9523_PLL2_FREQ_DOUBLER_EN (1 << 5) +#define AD9523_PLL2_LOCK_DETECT_PWR_DOWN_EN (1 << 7) + +/* AD9523_PLL2_VCO_CTRL */ +#define AD9523_PLL2_VCO_CALIBRATE (1 << 1) +#define AD9523_PLL2_FORCE_VCO_MIDSCALE (1 << 2) +#define AD9523_PLL2_FORCE_REFERENCE_VALID (1 << 3) +#define AD9523_PLL2_FORCE_RELEASE_SYNC (1 << 4) + +/* AD9523_PLL2_VCO_DIVIDER */ +#define AD9523_PLL2_VCO_DIV_M1(x) ((((x) - 3) & 0x3) << 0) +#define AD9523_PLL2_VCO_DIV_M2(x) ((((x) - 3) & 0x3) << 4) +#define AD9523_PLL2_VCO_DIV_M1_PWR_DOWN_EN (1 << 2) +#define AD9523_PLL2_VCO_DIV_M2_PWR_DOWN_EN (1 << 6) + +/* AD9523_PLL2_LOOP_FILTER_CTRL */ +#define AD9523_PLL2_LOOP_FILTER_CPOLE1(x) (((x) & 0x7) << 0) +#define AD9523_PLL2_LOOP_FILTER_RZERO(x) (((x) & 0x7) << 3) +#define AD9523_PLL2_LOOP_FILTER_RPOLE2(x) (((x) & 0x7) << 6) +#define AD9523_PLL2_LOOP_FILTER_RZERO_BYPASS_EN (1 << 8) + +/* AD9523_PLL2_R2_DIVIDER */ +#define AD9523_PLL2_R2_DIVIDER_VAL(x) (((x) & 0x1F) << 0) + +/* AD9523_CHANNEL_CLOCK_DIST */ +#define AD9523_CLK_DIST_DIV_PHASE(x) (((x) & 0x3F) << 18) +#define AD9523_CLK_DIST_DIV_PHASE_REV(x) ((ret >> 18) & 0x3F) +#define AD9523_CLK_DIST_DIV(x) ((((x) - 1) & 0x3FF) << 8) +#define AD9523_CLK_DIST_DIV_REV(x) (((ret >> 8) & 0x3FF) + 1) +#define AD9523_CLK_DIST_INV_DIV_OUTPUT_EN (1 << 7) +#define AD9523_CLK_DIST_IGNORE_SYNC_EN (1 << 6) +#define AD9523_CLK_DIST_PWR_DOWN_EN (1 << 5) +#define AD9523_CLK_DIST_LOW_PWR_MODE_EN (1 << 4) +#define AD9523_CLK_DIST_DRIVER_MODE(x) (((x) & 0xF) << 0) + +/* AD9523_PLL1_OUTPUT_CTRL */ +#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH6_M2 (1 << 7) +#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH5_M2 (1 << 6) +#define AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH4_M2 (1 << 5) +#define AD9523_PLL1_OUTP_CTRL_CMOS_DRV_WEAK (1 << 4) +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_1 (0 << 0) +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_2 (1 << 0) +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_4 (2 << 0) +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_8 (4 << 0) +#define AD9523_PLL1_OUTP_CTRL_OUTPUT_DIV_16 (8 << 0) + +/* AD9523_PLL1_OUTPUT_CHANNEL_CTRL */ +#define AD9523_PLL1_OUTP_CH_CTRL_OUTPUT_PWR_DOWN_EN (1 << 7) +#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH9_M2 (1 << 6) +#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH8_M2 (1 << 5) +#define AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH7_M2 (1 << 4) +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH3 (1 << 3) +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH2 (1 << 2) +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH1 (1 << 1) +#define AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH0 (1 << 0) + +/* AD9523_READBACK_0 */ +#define AD9523_READBACK_0_STAT_PLL2_REF_CLK (1 << 7) +#define AD9523_READBACK_0_STAT_PLL2_FB_CLK (1 << 6) +#define AD9523_READBACK_0_STAT_VCXO (1 << 5) +#define AD9523_READBACK_0_STAT_REF_TEST (1 << 4) +#define AD9523_READBACK_0_STAT_REFB (1 << 3) +#define AD9523_READBACK_0_STAT_REFA (1 << 2) +#define AD9523_READBACK_0_STAT_PLL2_LD (1 << 1) +#define AD9523_READBACK_0_STAT_PLL1_LD (1 << 0) + +/* AD9523_READBACK_1 */ +#define AD9523_READBACK_1_HOLDOVER_ACTIVE (1 << 3) +#define AD9523_READBACK_1_AUTOMODE_SEL_REFB (1 << 2) +#define AD9523_READBACK_1_VCO_CALIB_IN_PROGRESS (1 << 0) + +/* AD9523_STATUS_SIGNALS */ +#define AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL (1 << 16) +#define AD9523_STATUS_MONITOR_01_PLL12_LOCKED (0x302) +/* AD9523_POWER_DOWN_CTRL */ +#define AD9523_POWER_DOWN_CTRL_PLL1_PWR_DOWN (1 << 2) +#define AD9523_POWER_DOWN_CTRL_PLL2_PWR_DOWN (1 << 1) +#define AD9523_POWER_DOWN_CTRL_DIST_PWR_DOWN (1 << 0) + +/* AD9523_IO_UPDATE */ +#define AD9523_IO_UPDATE_EN (1 << 0) + +/* AD9523_EEPROM_DATA_XFER_STATUS */ +#define AD9523_EEPROM_DATA_XFER_IN_PROGRESS (1 << 0) + +/* AD9523_EEPROM_ERROR_READBACK */ +#define AD9523_EEPROM_ERROR_READBACK_FAIL (1 << 0) + +/* AD9523_EEPROM_CTRL1 */ +#define AD9523_EEPROM_CTRL1_SOFT_EEPROM (1 << 1) +#define AD9523_EEPROM_CTRL1_EEPROM_WRITE_PROT_DIS (1 << 0) + +/* AD9523_EEPROM_CTRL2 */ +#define AD9523_EEPROM_CTRL2_REG2EEPROM (1 << 0) + +#define AD9523_NUM_CHAN 14 +#define AD9523_NUM_CHAN_ALT_CLK_SRC 10 + +/* Helpers to avoid excess line breaks */ +#define AD_IFE(_pde, _a, _b) ((pdata->_pde) ? _a : _b) +#define AD_IF(_pde, _a) AD_IFE(_pde, _a, 0) + +enum { + AD9523_STAT_PLL1_LD, + AD9523_STAT_PLL2_LD, + AD9523_STAT_REFA, + AD9523_STAT_REFB, + AD9523_STAT_REF_TEST, + AD9523_STAT_VCXO, + AD9523_STAT_PLL2_FB_CLK, + AD9523_STAT_PLL2_REF_CLK, + AD9523_SYNC, + AD9523_EEPROM, +}; + +enum { + AD9523_VCO1, + AD9523_VCO2, + AD9523_VCXO, + AD9523_NUM_CLK_SRC, +}; + +struct ad9523_state { + struct spi_device *spi; + struct regulator *reg; + struct ad9523_platform_data *pdata; + struct iio_chan_spec ad9523_channels[AD9523_NUM_CHAN]; + + unsigned long vcxo_freq; + unsigned long vco_freq; + unsigned long vco_out_freq[AD9523_NUM_CLK_SRC]; + unsigned char vco_out_map[AD9523_NUM_CHAN_ALT_CLK_SRC]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + union { + __be32 d32; + u8 d8[4]; + } data[2] ____cacheline_aligned; +}; + +static int ad9523_read(struct iio_dev *indio_dev, unsigned addr) +{ + struct ad9523_state *st = iio_priv(indio_dev); + struct spi_message m; + int ret; + + /* We encode the register size 1..3 bytes into the register address. + * On transfer we get the size from the register datum, and make sure + * the result is properly aligned. + */ + + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[2], + .len = 2, + }, { + .rx_buf = &st->data[1].d8[4 - AD9523_TRANSF_LEN(addr)], + .len = AD9523_TRANSF_LEN(addr), + }, + }; + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + st->data[0].d32 = cpu_to_be32(AD9523_READ | + AD9523_CNT(AD9523_TRANSF_LEN(addr)) | + AD9523_ADDR(addr)); + + ret = spi_sync(st->spi, &m); + if (ret < 0) + dev_err(&indio_dev->dev, "read failed (%d)", ret); + else + ret = be32_to_cpu(st->data[1].d32) & (0xFFFFFF >> + (8 * (3 - AD9523_TRANSF_LEN(addr)))); + + return ret; +}; + +static int ad9523_write(struct iio_dev *indio_dev, unsigned addr, unsigned val) +{ + struct ad9523_state *st = iio_priv(indio_dev); + struct spi_message m; + int ret; + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[2], + .len = 2, + }, { + .tx_buf = &st->data[1].d8[4 - AD9523_TRANSF_LEN(addr)], + .len = AD9523_TRANSF_LEN(addr), + }, + }; + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + st->data[0].d32 = cpu_to_be32(AD9523_WRITE | + AD9523_CNT(AD9523_TRANSF_LEN(addr)) | + AD9523_ADDR(addr)); + st->data[1].d32 = cpu_to_be32(val); + + ret = spi_sync(st->spi, &m); + + if (ret < 0) + dev_err(&indio_dev->dev, "write failed (%d)", ret); + + return ret; +} + +static int ad9523_io_update(struct iio_dev *indio_dev) +{ + return ad9523_write(indio_dev, AD9523_IO_UPDATE, AD9523_IO_UPDATE_EN); +} + +static int ad9523_vco_out_map(struct iio_dev *indio_dev, + unsigned ch, bool out) +{ + struct ad9523_state *st = iio_priv(indio_dev); + int ret; + unsigned mask; + + switch (ch) { + case 0 ... 3: + ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CHANNEL_CTRL); + if (ret < 0) + break; + mask = AD9523_PLL1_OUTP_CH_CTRL_VCXO_SRC_SEL_CH0 << ch; + if (out) { + ret |= mask; + out = 2; + } else { + ret &= ~mask; + } + ret = ad9523_write(indio_dev, + AD9523_PLL1_OUTPUT_CHANNEL_CTRL, ret); + break; + case 4 ... 6: + ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CTRL); + if (ret < 0) + break; + mask = AD9523_PLL1_OUTP_CTRL_VCO_DIV_SEL_CH4_M2 << (ch - 4); + if (out) + ret |= mask; + else + ret &= ~mask; + ret = ad9523_write(indio_dev, AD9523_PLL1_OUTPUT_CTRL, ret); + break; + case 7 ... 9: + ret = ad9523_read(indio_dev, AD9523_PLL1_OUTPUT_CHANNEL_CTRL); + if (ret < 0) + break; + mask = AD9523_PLL1_OUTP_CH_CTRL_VCO_DIV_SEL_CH7_M2 << (ch - 7); + if (out) + ret |= mask; + else + ret &= ~mask; + ret = ad9523_write(indio_dev, + AD9523_PLL1_OUTPUT_CHANNEL_CTRL, ret); + break; + default: + return 0; + } + + st->vco_out_map[ch] = out; + + return ret; +} + +static int ad9523_set_clock_provider(struct iio_dev *indio_dev, + unsigned ch, unsigned long freq) +{ + struct ad9523_state *st = iio_priv(indio_dev); + long tmp1, tmp2; + bool use_alt_clk_src; + + switch (ch) { + case 0 ... 3: + use_alt_clk_src = (freq == st->vco_out_freq[AD9523_VCXO]); + break; + case 4 ... 9: + tmp1 = st->vco_out_freq[AD9523_VCO1] / freq; + tmp2 = st->vco_out_freq[AD9523_VCO2] / freq; + tmp1 *= freq; + tmp2 *= freq; + use_alt_clk_src = (abs(tmp1 - freq) > abs(tmp2 - freq)); + break; + default: + /* Ch 10..14: No action required, return success */ + return 0; + } + + return ad9523_vco_out_map(indio_dev, ch, use_alt_clk_src); +} + +static int ad9523_store_eeprom(struct iio_dev *indio_dev) +{ + int ret, tmp; + + ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL1, + AD9523_EEPROM_CTRL1_EEPROM_WRITE_PROT_DIS); + if (ret < 0) + return ret; + ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL2, + AD9523_EEPROM_CTRL2_REG2EEPROM); + if (ret < 0) + return ret; + + tmp = 4; + do { + msleep(16); + ret = ad9523_read(indio_dev, + AD9523_EEPROM_DATA_XFER_STATUS); + if (ret < 0) + return ret; + } while ((ret & AD9523_EEPROM_DATA_XFER_IN_PROGRESS) && tmp--); + + ret = ad9523_write(indio_dev, AD9523_EEPROM_CTRL1, 0); + if (ret < 0) + return ret; + + ret = ad9523_read(indio_dev, AD9523_EEPROM_ERROR_READBACK); + if (ret < 0) + return ret; + + if (ret & AD9523_EEPROM_ERROR_READBACK_FAIL) { + dev_err(&indio_dev->dev, "Verify EEPROM failed"); + ret = -EIO; + } + + return ret; +} + +static int ad9523_sync(struct iio_dev *indio_dev) +{ + int ret, tmp; + + ret = ad9523_read(indio_dev, AD9523_STATUS_SIGNALS); + if (ret < 0) + return ret; + + tmp = ret; + tmp |= AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL; + + ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, tmp); + if (ret < 0) + return ret; + + ad9523_io_update(indio_dev); + tmp &= ~AD9523_STATUS_SIGNALS_SYNC_MAN_CTRL; + + ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, tmp); + if (ret < 0) + return ret; + + return ad9523_io_update(indio_dev); +} + +static ssize_t ad9523_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + bool state; + int ret; + + ret = strtobool(buf, &state); + if (ret < 0) + return ret; + + if (!state) + return 0; + + mutex_lock(&indio_dev->mlock); + switch ((u32)this_attr->address) { + case AD9523_SYNC: + ret = ad9523_sync(indio_dev); + break; + case AD9523_EEPROM: + ret = ad9523_store_eeprom(indio_dev); + break; + default: + ret = -ENODEV; + } + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static ssize_t ad9523_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + + mutex_lock(&indio_dev->mlock); + ret = ad9523_read(indio_dev, AD9523_READBACK_0); + if (ret >= 0) { + ret = sprintf(buf, "%d\n", !!(ret & (1 << + (u32)this_attr->address))); + } + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static IIO_DEVICE_ATTR(pll1_locked, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_PLL1_LD); + +static IIO_DEVICE_ATTR(pll2_locked, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_PLL2_LD); + +static IIO_DEVICE_ATTR(pll1_reference_clk_a_present, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_REFA); + +static IIO_DEVICE_ATTR(pll1_reference_clk_b_present, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_REFB); + +static IIO_DEVICE_ATTR(pll1_reference_clk_test_present, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_REF_TEST); + +static IIO_DEVICE_ATTR(vcxo_clk_present, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_VCXO); + +static IIO_DEVICE_ATTR(pll2_feedback_clk_present, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_PLL2_FB_CLK); + +static IIO_DEVICE_ATTR(pll2_reference_clk_present, S_IRUGO, + ad9523_show, + NULL, + AD9523_STAT_PLL2_REF_CLK); + +static IIO_DEVICE_ATTR(sync_dividers, S_IWUSR, + NULL, + ad9523_store, + AD9523_SYNC); + +static IIO_DEVICE_ATTR(store_eeprom, S_IWUSR, + NULL, + ad9523_store, + AD9523_EEPROM); + +static struct attribute *ad9523_attributes[] = { + &iio_dev_attr_sync_dividers.dev_attr.attr, + &iio_dev_attr_store_eeprom.dev_attr.attr, + &iio_dev_attr_pll2_feedback_clk_present.dev_attr.attr, + &iio_dev_attr_pll2_reference_clk_present.dev_attr.attr, + &iio_dev_attr_pll1_reference_clk_a_present.dev_attr.attr, + &iio_dev_attr_pll1_reference_clk_b_present.dev_attr.attr, + &iio_dev_attr_pll1_reference_clk_test_present.dev_attr.attr, + &iio_dev_attr_vcxo_clk_present.dev_attr.attr, + &iio_dev_attr_pll1_locked.dev_attr.attr, + &iio_dev_attr_pll2_locked.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad9523_attribute_group = { + .attrs = ad9523_attributes, +}; + +static int ad9523_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad9523_state *st = iio_priv(indio_dev); + unsigned code; + int ret; + + mutex_lock(&indio_dev->mlock); + ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel)); + mutex_unlock(&indio_dev->mlock); + + if (ret < 0) + return ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + *val = !(ret & AD9523_CLK_DIST_PWR_DOWN_EN); + return IIO_VAL_INT; + case IIO_CHAN_INFO_FREQUENCY: + *val = st->vco_out_freq[st->vco_out_map[chan->channel]] / + AD9523_CLK_DIST_DIV_REV(ret); + return IIO_VAL_INT; + case IIO_CHAN_INFO_PHASE: + code = (AD9523_CLK_DIST_DIV_PHASE_REV(ret) * 3141592) / + AD9523_CLK_DIST_DIV_REV(ret); + *val = code / 1000000; + *val2 = (code % 1000000) * 10; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +}; + +static int ad9523_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad9523_state *st = iio_priv(indio_dev); + unsigned reg; + int ret, tmp, code; + + mutex_lock(&indio_dev->mlock); + ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel)); + if (ret < 0) + goto out; + + reg = ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val) + reg &= ~AD9523_CLK_DIST_PWR_DOWN_EN; + else + reg |= AD9523_CLK_DIST_PWR_DOWN_EN; + break; + case IIO_CHAN_INFO_FREQUENCY: + if (val <= 0) { + ret = -EINVAL; + goto out; + } + ret = ad9523_set_clock_provider(indio_dev, chan->channel, val); + if (ret < 0) + goto out; + tmp = st->vco_out_freq[st->vco_out_map[chan->channel]] / val; + tmp = clamp(tmp, 1, 1024); + reg &= ~(0x3FF << 8); + reg |= AD9523_CLK_DIST_DIV(tmp); + break; + case IIO_CHAN_INFO_PHASE: + code = val * 1000000 + val2 % 1000000; + tmp = (code * AD9523_CLK_DIST_DIV_REV(ret)) / 3141592; + tmp = clamp(tmp, 0, 63); + reg &= ~AD9523_CLK_DIST_DIV_PHASE(~0); + reg |= AD9523_CLK_DIST_DIV_PHASE(tmp); + break; + default: + ret = -EINVAL; + goto out; + } + + ret = ad9523_write(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel), + reg); + if (ret < 0) + goto out; + + ad9523_io_update(indio_dev); +out: + mutex_unlock(&indio_dev->mlock); + return ret; +} + +static int ad9523_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + int ret; + + mutex_lock(&indio_dev->mlock); + if (readval == NULL) { + ret = ad9523_write(indio_dev, reg | AD9523_R1B, writeval); + ad9523_io_update(indio_dev); + } else { + ret = ad9523_read(indio_dev, reg | AD9523_R1B); + if (ret < 0) + return ret; + *readval = ret; + ret = 0; + } + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static const struct iio_info ad9523_info = { + .read_raw = &ad9523_read_raw, + .write_raw = &ad9523_write_raw, + .debugfs_reg_access = &ad9523_reg_access, + .attrs = &ad9523_attribute_group, + .driver_module = THIS_MODULE, +}; + +static int ad9523_setup(struct iio_dev *indio_dev) +{ + struct ad9523_state *st = iio_priv(indio_dev); + struct ad9523_platform_data *pdata = st->pdata; + struct ad9523_channel_spec *chan; + unsigned long active_mask = 0; + int ret, i; + + ret = ad9523_write(indio_dev, AD9523_SERIAL_PORT_CONFIG, + AD9523_SER_CONF_SOFT_RESET | + (st->spi->mode & SPI_3WIRE ? 0 : + AD9523_SER_CONF_SDO_ACTIVE)); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_READBACK_CTRL, + AD9523_READBACK_CTRL_READ_BUFFERED); + if (ret < 0) + return ret; + + ret = ad9523_io_update(indio_dev); + if (ret < 0) + return ret; + + /* + * PLL1 Setup + */ + ret = ad9523_write(indio_dev, AD9523_PLL1_REF_A_DIVIDER, + pdata->refa_r_div); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL1_REF_B_DIVIDER, + pdata->refb_r_div); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL1_FEEDBACK_DIVIDER, + pdata->pll1_feedback_div); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL1_CHARGE_PUMP_CTRL, + AD9523_PLL1_CHARGE_PUMP_CURRENT_nA(pdata-> + pll1_charge_pump_current_nA) | + AD9523_PLL1_CHARGE_PUMP_MODE_NORMAL | + AD9523_PLL1_BACKLASH_PW_MIN); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL1_INPUT_RECEIVERS_CTRL, + AD_IF(refa_diff_rcv_en, AD9523_PLL1_REFA_RCV_EN) | + AD_IF(refb_diff_rcv_en, AD9523_PLL1_REFB_RCV_EN) | + AD_IF(osc_in_diff_en, AD9523_PLL1_OSC_IN_DIFF_EN) | + AD_IF(osc_in_cmos_neg_inp_en, + AD9523_PLL1_OSC_IN_CMOS_NEG_INP_EN) | + AD_IF(refa_diff_rcv_en, AD9523_PLL1_REFA_DIFF_RCV_EN) | + AD_IF(refb_diff_rcv_en, AD9523_PLL1_REFB_DIFF_RCV_EN)); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL1_REF_CTRL, + AD_IF(zd_in_diff_en, AD9523_PLL1_ZD_IN_DIFF_EN) | + AD_IF(zd_in_cmos_neg_inp_en, + AD9523_PLL1_ZD_IN_CMOS_NEG_INP_EN) | + AD_IF(zero_delay_mode_internal_en, + AD9523_PLL1_ZERO_DELAY_MODE_INT) | + AD_IF(osc_in_feedback_en, AD9523_PLL1_OSC_IN_PLL_FEEDBACK_EN) | + AD_IF(refa_cmos_neg_inp_en, AD9523_PLL1_REFA_CMOS_NEG_INP_EN) | + AD_IF(refb_cmos_neg_inp_en, AD9523_PLL1_REFB_CMOS_NEG_INP_EN)); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL1_MISC_CTRL, + AD9523_PLL1_REFB_INDEP_DIV_CTRL_EN | + AD9523_PLL1_REF_MODE(pdata->ref_mode)); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL1_LOOP_FILTER_CTRL, + AD9523_PLL1_LOOP_FILTER_RZERO(pdata->pll1_loop_filter_rzero)); + if (ret < 0) + return ret; + /* + * PLL2 Setup + */ + + ret = ad9523_write(indio_dev, AD9523_PLL2_CHARGE_PUMP, + AD9523_PLL2_CHARGE_PUMP_CURRENT_nA(pdata-> + pll2_charge_pump_current_nA)); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL2_FEEDBACK_DIVIDER_AB, + AD9523_PLL2_FB_NDIV_A_CNT(pdata->pll2_ndiv_a_cnt) | + AD9523_PLL2_FB_NDIV_B_CNT(pdata->pll2_ndiv_b_cnt)); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL2_CTRL, + AD9523_PLL2_CHARGE_PUMP_MODE_NORMAL | + AD9523_PLL2_BACKLASH_CTRL_EN | + AD_IF(pll2_freq_doubler_en, AD9523_PLL2_FREQ_DOUBLER_EN)); + if (ret < 0) + return ret; + + st->vco_freq = (pdata->vcxo_freq * (pdata->pll2_freq_doubler_en ? 2 : 1) + / pdata->pll2_r2_div) * AD9523_PLL2_FB_NDIV(pdata-> + pll2_ndiv_a_cnt, pdata->pll2_ndiv_b_cnt); + + ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_CTRL, + AD9523_PLL2_VCO_CALIBRATE); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_DIVIDER, + AD9523_PLL2_VCO_DIV_M1(pdata->pll2_vco_diff_m1) | + AD9523_PLL2_VCO_DIV_M2(pdata->pll2_vco_diff_m2) | + AD_IFE(pll2_vco_diff_m1, 0, + AD9523_PLL2_VCO_DIV_M1_PWR_DOWN_EN) | + AD_IFE(pll2_vco_diff_m2, 0, + AD9523_PLL2_VCO_DIV_M2_PWR_DOWN_EN)); + if (ret < 0) + return ret; + + if (pdata->pll2_vco_diff_m1) + st->vco_out_freq[AD9523_VCO1] = + st->vco_freq / pdata->pll2_vco_diff_m1; + + if (pdata->pll2_vco_diff_m2) + st->vco_out_freq[AD9523_VCO2] = + st->vco_freq / pdata->pll2_vco_diff_m2; + + st->vco_out_freq[AD9523_VCXO] = pdata->vcxo_freq; + + ret = ad9523_write(indio_dev, AD9523_PLL2_R2_DIVIDER, + AD9523_PLL2_R2_DIVIDER_VAL(pdata->pll2_r2_div)); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_PLL2_LOOP_FILTER_CTRL, + AD9523_PLL2_LOOP_FILTER_CPOLE1(pdata->cpole1) | + AD9523_PLL2_LOOP_FILTER_RZERO(pdata->rzero) | + AD9523_PLL2_LOOP_FILTER_RPOLE2(pdata->rpole2) | + AD_IF(rzero_bypass_en, + AD9523_PLL2_LOOP_FILTER_RZERO_BYPASS_EN)); + if (ret < 0) + return ret; + + for (i = 0; i < pdata->num_channels; i++) { + chan = &pdata->channels[i]; + if (chan->channel_num < AD9523_NUM_CHAN) { + __set_bit(chan->channel_num, &active_mask); + ret = ad9523_write(indio_dev, + AD9523_CHANNEL_CLOCK_DIST(chan->channel_num), + AD9523_CLK_DIST_DRIVER_MODE(chan->driver_mode) | + AD9523_CLK_DIST_DIV(chan->channel_divider) | + AD9523_CLK_DIST_DIV_PHASE(chan->divider_phase) | + (chan->sync_ignore_en ? + AD9523_CLK_DIST_IGNORE_SYNC_EN : 0) | + (chan->divider_output_invert_en ? + AD9523_CLK_DIST_INV_DIV_OUTPUT_EN : 0) | + (chan->low_power_mode_en ? + AD9523_CLK_DIST_LOW_PWR_MODE_EN : 0) | + (chan->output_dis ? + AD9523_CLK_DIST_PWR_DOWN_EN : 0)); + if (ret < 0) + return ret; + + ret = ad9523_vco_out_map(indio_dev, chan->channel_num, + chan->use_alt_clock_src); + if (ret < 0) + return ret; + + st->ad9523_channels[i].type = IIO_ALTVOLTAGE; + st->ad9523_channels[i].output = 1; + st->ad9523_channels[i].indexed = 1; + st->ad9523_channels[i].channel = chan->channel_num; + st->ad9523_channels[i].extend_name = + chan->extended_name; + st->ad9523_channels[i].info_mask = + IIO_CHAN_INFO_RAW_SEPARATE_BIT | + IIO_CHAN_INFO_PHASE_SEPARATE_BIT | + IIO_CHAN_INFO_FREQUENCY_SEPARATE_BIT; + } + } + + for_each_clear_bit(i, &active_mask, AD9523_NUM_CHAN) + ad9523_write(indio_dev, + AD9523_CHANNEL_CLOCK_DIST(i), + AD9523_CLK_DIST_DRIVER_MODE(TRISTATE) | + AD9523_CLK_DIST_PWR_DOWN_EN); + + ret = ad9523_write(indio_dev, AD9523_POWER_DOWN_CTRL, 0); + if (ret < 0) + return ret; + + ret = ad9523_write(indio_dev, AD9523_STATUS_SIGNALS, + AD9523_STATUS_MONITOR_01_PLL12_LOCKED); + if (ret < 0) + return ret; + + ret = ad9523_io_update(indio_dev); + if (ret < 0) + return ret; + + return 0; +} + +static int __devinit ad9523_probe(struct spi_device *spi) +{ + struct ad9523_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev; + struct ad9523_state *st; + int ret; + + if (!pdata) { + dev_err(&spi->dev, "no platform data?\n"); + return -EINVAL; + } + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = regulator_get(&spi->dev, "vcc"); + if (!IS_ERR(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + } + + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + st->pdata = pdata; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = (pdata->name[0] != 0) ? pdata->name : + spi_get_device_id(spi)->name; + indio_dev->info = &ad9523_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->ad9523_channels; + indio_dev->num_channels = pdata->num_channels; + + ret = ad9523_setup(indio_dev); + if (ret < 0) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_disable_reg; + + dev_info(&spi->dev, "probed %s\n", indio_dev->name); + + return 0; + +error_disable_reg: + if (!IS_ERR(st->reg)) + regulator_disable(st->reg); +error_put_reg: + if (!IS_ERR(st->reg)) + regulator_put(st->reg); + + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad9523_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad9523_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + if (!IS_ERR(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad9523_id[] = { + {"ad9523-1", 9523}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad9523_id); + +static struct spi_driver ad9523_driver = { + .driver = { + .name = "ad9523", + .owner = THIS_MODULE, + }, + .probe = ad9523_probe, + .remove = __devexit_p(ad9523_remove), + .id_table = ad9523_id, +}; +module_spi_driver(ad9523_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD9523 CLOCKDIST/PLL"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/iio/frequency/ad9523.h b/include/linux/iio/frequency/ad9523.h new file mode 100644 index 000000000000..12ce3ee427fd --- /dev/null +++ b/include/linux/iio/frequency/ad9523.h @@ -0,0 +1,195 @@ +/* + * AD9523 SPI Low Jitter Clock Generator + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef IIO_FREQUENCY_AD9523_H_ +#define IIO_FREQUENCY_AD9523_H_ + +enum outp_drv_mode { + TRISTATE, + LVPECL_8mA, + LVDS_4mA, + LVDS_7mA, + HSTL0_16mA, + HSTL1_8mA, + CMOS_CONF1, + CMOS_CONF2, + CMOS_CONF3, + CMOS_CONF4, + CMOS_CONF5, + CMOS_CONF6, + CMOS_CONF7, + CMOS_CONF8, + CMOS_CONF9 +}; + +enum ref_sel_mode { + NONEREVERTIVE_STAY_ON_REFB, + REVERT_TO_REFA, + SELECT_REFA, + SELECT_REFB, + EXT_REF_SEL +}; + +/** + * struct ad9523_channel_spec - Output channel configuration + * + * @channel_num: Output channel number. + * @divider_output_invert_en: Invert the polarity of the output clock. + * @sync_ignore_en: Ignore chip-level SYNC signal. + * @low_power_mode_en: Reduce power used in the differential output modes. + * @use_alt_clock_src: Channel divider uses alternative clk source. + * @output_dis: Disables, powers down the entire channel. + * @driver_mode: Output driver mode (logic level family). + * @divider_phase: Divider initial phase after a SYNC. Range 0..63 + LSB = 1/2 of a period of the divider input clock. + * @channel_divider: 10-bit channel divider. + * @extended_name: Optional descriptive channel name. + */ + +struct ad9523_channel_spec { + unsigned channel_num; + bool divider_output_invert_en; + bool sync_ignore_en; + bool low_power_mode_en; + /* CH0..CH3 VCXO, CH4..CH9 VCO2 */ + bool use_alt_clock_src; + bool output_dis; + enum outp_drv_mode driver_mode; + unsigned char divider_phase; + unsigned short channel_divider; + char extended_name[16]; +}; + +enum pll1_rzero_resistor { + RZERO_883_OHM, + RZERO_677_OHM, + RZERO_341_OHM, + RZERO_135_OHM, + RZERO_10_OHM, + RZERO_USE_EXT_RES = 8, +}; + +enum rpole2_resistor { + RPOLE2_900_OHM, + RPOLE2_450_OHM, + RPOLE2_300_OHM, + RPOLE2_225_OHM, +}; + +enum rzero_resistor { + RZERO_3250_OHM, + RZERO_2750_OHM, + RZERO_2250_OHM, + RZERO_2100_OHM, + RZERO_3000_OHM, + RZERO_2500_OHM, + RZERO_2000_OHM, + RZERO_1850_OHM, +}; + +enum cpole1_capacitor { + CPOLE1_0_PF, + CPOLE1_8_PF, + CPOLE1_16_PF, + CPOLE1_24_PF, + _CPOLE1_24_PF, /* place holder */ + CPOLE1_32_PF, + CPOLE1_40_PF, + CPOLE1_48_PF, +}; + +/** + * struct ad9523_platform_data - platform specific information + * + * @vcxo_freq: External VCXO frequency in Hz + * @refa_diff_rcv_en: REFA differential/single-ended input selection. + * @refb_diff_rcv_en: REFB differential/single-ended input selection. + * @zd_in_diff_en: Zero Delay differential/single-ended input selection. + * @osc_in_diff_en: OSC differential/ single-ended input selection. + * @refa_cmos_neg_inp_en: REFA single-ended neg./pos. input enable. + * @refb_cmos_neg_inp_en: REFB single-ended neg./pos. input enable. + * @zd_in_cmos_neg_inp_en: Zero Delay single-ended neg./pos. input enable. + * @osc_in_cmos_neg_inp_en: OSC single-ended neg./pos. input enable. + * @refa_r_div: PLL1 10-bit REFA R divider. + * @refb_r_div: PLL1 10-bit REFB R divider. + * @pll1_feedback_div: PLL1 10-bit Feedback N divider. + * @pll1_charge_pump_current_nA: Magnitude of PLL1 charge pump current (nA). + * @zero_delay_mode_internal_en: Internal, external Zero Delay mode selection. + * @osc_in_feedback_en: PLL1 feedback path, local feedback from + * the OSC_IN receiver or zero delay mode + * @pll1_loop_filter_rzero: PLL1 Loop Filter Zero Resistor selection. + * @ref_mode: Reference selection mode. + * @pll2_charge_pump_current_nA: Magnitude of PLL2 charge pump current (nA). + * @pll2_ndiv_a_cnt: PLL2 Feedback N-divider, A Counter, range 0..4. + * @pll2_ndiv_b_cnt: PLL2 Feedback N-divider, B Counter, range 0..63. + * @pll2_freq_doubler_en: PLL2 frequency doubler enable. + * @pll2_r2_div: PLL2 R2 divider, range 0..31. + * @pll2_vco_diff_m1: VCO1 divider, range 3..5. + * @pll2_vco_diff_m2: VCO2 divider, range 3..5. + * @rpole2: PLL2 loop filter Rpole resistor value. + * @rzero: PLL2 loop filter Rzero resistor value. + * @cpole1: PLL2 loop filter Cpole capacitor value. + * @rzero_bypass_en: PLL2 loop filter Rzero bypass enable. + * @num_channels: Array size of struct ad9523_channel_spec. + * @channels: Pointer to channel array. + * @name: Optional alternative iio device name. + */ + +struct ad9523_platform_data { + unsigned long vcxo_freq; + + /* Differential/ Single-Ended Input Configuration */ + bool refa_diff_rcv_en; + bool refb_diff_rcv_en; + bool zd_in_diff_en; + bool osc_in_diff_en; + + /* + * Valid if differential input disabled + * if false defaults to pos input + */ + bool refa_cmos_neg_inp_en; + bool refb_cmos_neg_inp_en; + bool zd_in_cmos_neg_inp_en; + bool osc_in_cmos_neg_inp_en; + + /* PLL1 Setting */ + unsigned short refa_r_div; + unsigned short refb_r_div; + unsigned short pll1_feedback_div; + unsigned short pll1_charge_pump_current_nA; + bool zero_delay_mode_internal_en; + bool osc_in_feedback_en; + enum pll1_rzero_resistor pll1_loop_filter_rzero; + + /* Reference */ + enum ref_sel_mode ref_mode; + + /* PLL2 Setting */ + unsigned int pll2_charge_pump_current_nA; + unsigned char pll2_ndiv_a_cnt; + unsigned char pll2_ndiv_b_cnt; + bool pll2_freq_doubler_en; + unsigned char pll2_r2_div; + unsigned char pll2_vco_diff_m1; /* 3..5 */ + unsigned char pll2_vco_diff_m2; /* 3..5 */ + + /* Loop Filter PLL2 */ + enum rpole2_resistor rpole2; + enum rzero_resistor rzero; + enum cpole1_capacitor cpole1; + bool rzero_bypass_en; + + /* Output Channel Configuration */ + int num_channels; + struct ad9523_channel_spec *channels; + + char name[SPI_NAME_SIZE]; +}; + +#endif /* IIO_FREQUENCY_AD9523_H_ */ -- cgit v1.2.3 From e31166f0fd48478866ee9661c36789126435ebe8 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 29 May 2012 12:41:20 +0200 Subject: iio: frequency: New driver for Analog Devices ADF4350/ADF4351 Wideband Synthesizers Changes since V1: Apply Jonathan's review feedback: Introduce and use IIO_ALTVOLTAGE. Fix up comments and documentation. Remove dead code. Reorder some code fragments. Add missing iio_device_free. Convert to new API. Fix-up out of staging includes. Removed pll_locked attribute. Changes since V2: Use module_spi_driver. adf4350_remove: move gpio_free after regulator. target patch to drivers/iio Signed-off-by: Michael Hennerich Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-bus-iio-frequency-adf4350 | 21 + drivers/iio/frequency/Kconfig | 18 + drivers/iio/frequency/Makefile | 1 + drivers/iio/frequency/adf4350.c | 478 +++++++++++++++++++++ include/linux/iio/frequency/adf4350.h | 126 ++++++ 5 files changed, 644 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 create mode 100644 drivers/iio/frequency/adf4350.c create mode 100644 include/linux/iio/frequency/adf4350.h (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 new file mode 100644 index 000000000000..d89aded01c5a --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 @@ -0,0 +1,21 @@ +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_resolution +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + Stores channel Y frequency resolution/channel spacing in Hz. + The value given directly influences the MODULUS used by + the fractional-N PLL. It is assumed that the algorithm + that is used to compute the various dividers, is able to + generate proper values for multiples of channel spacing. + +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_refin_frequency +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + Sets channel Y REFin frequency in Hz. In some clock chained + applications, the reference frequency used by the PLL may + change during runtime. This attribute allows the user to + adjust the reference frequency accordingly. + The value written has no effect until out_altvoltageY_frequency + is updated. Consider to use out_altvoltageY_powerdown to power + down the PLL and it's RFOut buffers during REFin changes. diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index 0458c92464a3..6aaa33ef4544 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -19,5 +19,23 @@ config AD9523 To compile this driver as a module, choose M here: the module will be called ad9523. +endmenu + +# +# Phase-Locked Loop (PLL) frequency synthesizers +# + +menu "Phase-Locked Loop (PLL) frequency synthesizers" + +config ADF4350 + tristate "Analog Devices ADF4350/ADF4351 Wideband Synthesizers" + depends on SPI + help + Say yes here to build support for Analog Devices ADF4350/ADF4351 + Wideband Synthesizers. The driver provides direct access via sysfs. + + To compile this driver as a module, choose M here: the + module will be called adf4350. + endmenu endmenu diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile index 1b5b22417da1..00d26e5d1dc2 100644 --- a/drivers/iio/frequency/Makefile +++ b/drivers/iio/frequency/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_AD9523) += ad9523.o +obj-$(CONFIG_ADF4350) += adf4350.o diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c new file mode 100644 index 000000000000..fd4c8501aba9 --- /dev/null +++ b/drivers/iio/frequency/adf4350.c @@ -0,0 +1,478 @@ +/* + * ADF4350/ADF4351 SPI Wideband Synthesizer driver + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +enum { + ADF4350_FREQ, + ADF4350_FREQ_REFIN, + ADF4350_FREQ_RESOLUTION, + ADF4350_PWRDOWN, +}; + +struct adf4350_state { + struct spi_device *spi; + struct regulator *reg; + struct adf4350_platform_data *pdata; + unsigned long clkin; + unsigned long chspc; /* Channel Spacing */ + unsigned long fpfd; /* Phase Frequency Detector */ + unsigned long min_out_freq; + unsigned r0_fract; + unsigned r0_int; + unsigned r1_mod; + unsigned r4_rf_div_sel; + unsigned long regs[6]; + unsigned long regs_hw[6]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be32 val ____cacheline_aligned; +}; + +static struct adf4350_platform_data default_pdata = { + .clkin = 122880000, + .channel_spacing = 10000, + .r2_user_settings = ADF4350_REG2_PD_POLARITY_POS, + ADF4350_REG2_CHARGE_PUMP_CURR_uA(2500), + .r3_user_settings = ADF4350_REG3_12BIT_CLKDIV_MODE(0), + .r4_user_settings = ADF4350_REG4_OUTPUT_PWR(3) | + ADF4350_REG4_MUTE_TILL_LOCK_EN, + .gpio_lock_detect = -1, +}; + +static int adf4350_sync_config(struct adf4350_state *st) +{ + int ret, i, doublebuf = 0; + + for (i = ADF4350_REG5; i >= ADF4350_REG0; i--) { + if ((st->regs_hw[i] != st->regs[i]) || + ((i == ADF4350_REG0) && doublebuf)) { + + switch (i) { + case ADF4350_REG1: + case ADF4350_REG4: + doublebuf = 1; + break; + } + + st->val = cpu_to_be32(st->regs[i] | i); + ret = spi_write(st->spi, &st->val, 4); + if (ret < 0) + return ret; + st->regs_hw[i] = st->regs[i]; + dev_dbg(&st->spi->dev, "[%d] 0x%X\n", + i, (u32)st->regs[i] | i); + } + } + return 0; +} + +static int adf4350_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct adf4350_state *st = iio_priv(indio_dev); + int ret; + + if (reg > ADF4350_REG5) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + if (readval == NULL) { + st->regs[reg] = writeval & ~(BIT(0) | BIT(1) | BIT(2)); + ret = adf4350_sync_config(st); + } else { + *readval = st->regs_hw[reg]; + ret = 0; + } + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int adf4350_tune_r_cnt(struct adf4350_state *st, unsigned short r_cnt) +{ + struct adf4350_platform_data *pdata = st->pdata; + + do { + r_cnt++; + st->fpfd = (st->clkin * (pdata->ref_doubler_en ? 2 : 1)) / + (r_cnt * (pdata->ref_div2_en ? 2 : 1)); + } while (st->fpfd > ADF4350_MAX_FREQ_PFD); + + return r_cnt; +} + +static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq) +{ + struct adf4350_platform_data *pdata = st->pdata; + u64 tmp; + u32 div_gcd, prescaler; + u16 mdiv, r_cnt = 0; + u8 band_sel_div; + + if (freq > ADF4350_MAX_OUT_FREQ || freq < st->min_out_freq) + return -EINVAL; + + if (freq > ADF4350_MAX_FREQ_45_PRESC) { + prescaler = ADF4350_REG1_PRESCALER; + mdiv = 75; + } else { + prescaler = 0; + mdiv = 23; + } + + st->r4_rf_div_sel = 0; + + while (freq < ADF4350_MIN_VCO_FREQ) { + freq <<= 1; + st->r4_rf_div_sel++; + } + + /* + * Allow a predefined reference division factor + * if not set, compute our own + */ + if (pdata->ref_div_factor) + r_cnt = pdata->ref_div_factor - 1; + + do { + r_cnt = adf4350_tune_r_cnt(st, r_cnt); + + st->r1_mod = st->fpfd / st->chspc; + while (st->r1_mod > ADF4350_MAX_MODULUS) { + r_cnt = adf4350_tune_r_cnt(st, r_cnt); + st->r1_mod = st->fpfd / st->chspc; + } + + tmp = freq * (u64)st->r1_mod + (st->fpfd > 1); + do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */ + st->r0_fract = do_div(tmp, st->r1_mod); + st->r0_int = tmp; + } while (mdiv > st->r0_int); + + band_sel_div = DIV_ROUND_UP(st->fpfd, ADF4350_MAX_BANDSEL_CLK); + + if (st->r0_fract && st->r1_mod) { + div_gcd = gcd(st->r1_mod, st->r0_fract); + st->r1_mod /= div_gcd; + st->r0_fract /= div_gcd; + } else { + st->r0_fract = 0; + st->r1_mod = 1; + } + + dev_dbg(&st->spi->dev, "VCO: %llu Hz, PFD %lu Hz\n" + "REF_DIV %d, R0_INT %d, R0_FRACT %d\n" + "R1_MOD %d, RF_DIV %d\nPRESCALER %s, BAND_SEL_DIV %d\n", + freq, st->fpfd, r_cnt, st->r0_int, st->r0_fract, st->r1_mod, + 1 << st->r4_rf_div_sel, prescaler ? "8/9" : "4/5", + band_sel_div); + + st->regs[ADF4350_REG0] = ADF4350_REG0_INT(st->r0_int) | + ADF4350_REG0_FRACT(st->r0_fract); + + st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(0) | + ADF4350_REG1_MOD(st->r1_mod) | + prescaler; + + st->regs[ADF4350_REG2] = + ADF4350_REG2_10BIT_R_CNT(r_cnt) | + ADF4350_REG2_DOUBLE_BUFF_EN | + (pdata->ref_doubler_en ? ADF4350_REG2_RMULT2_EN : 0) | + (pdata->ref_div2_en ? ADF4350_REG2_RDIV2_EN : 0) | + (pdata->r2_user_settings & (ADF4350_REG2_PD_POLARITY_POS | + ADF4350_REG2_LDP_6ns | ADF4350_REG2_LDF_INT_N | + ADF4350_REG2_CHARGE_PUMP_CURR_uA(5000) | + ADF4350_REG2_MUXOUT(0x7) | ADF4350_REG2_NOISE_MODE(0x9))); + + st->regs[ADF4350_REG3] = pdata->r3_user_settings & + (ADF4350_REG3_12BIT_CLKDIV(0xFFF) | + ADF4350_REG3_12BIT_CLKDIV_MODE(0x3) | + ADF4350_REG3_12BIT_CSR_EN | + ADF4351_REG3_CHARGE_CANCELLATION_EN | + ADF4351_REG3_ANTI_BACKLASH_3ns_EN | + ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH); + + st->regs[ADF4350_REG4] = + ADF4350_REG4_FEEDBACK_FUND | + ADF4350_REG4_RF_DIV_SEL(st->r4_rf_div_sel) | + ADF4350_REG4_8BIT_BAND_SEL_CLKDIV(band_sel_div) | + ADF4350_REG4_RF_OUT_EN | + (pdata->r4_user_settings & + (ADF4350_REG4_OUTPUT_PWR(0x3) | + ADF4350_REG4_AUX_OUTPUT_PWR(0x3) | + ADF4350_REG4_AUX_OUTPUT_EN | + ADF4350_REG4_AUX_OUTPUT_FUND | + ADF4350_REG4_MUTE_TILL_LOCK_EN)); + + st->regs[ADF4350_REG5] = ADF4350_REG5_LD_PIN_MODE_DIGITAL; + + return adf4350_sync_config(st); +} + +static ssize_t adf4350_write(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct adf4350_state *st = iio_priv(indio_dev); + unsigned long long readin; + int ret; + + ret = kstrtoull(buf, 10, &readin); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + switch ((u32)private) { + case ADF4350_FREQ: + ret = adf4350_set_freq(st, readin); + break; + case ADF4350_FREQ_REFIN: + if (readin > ADF4350_MAX_FREQ_REFIN) + ret = -EINVAL; + else + st->clkin = readin; + break; + case ADF4350_FREQ_RESOLUTION: + if (readin == 0) + ret = -EINVAL; + else + st->chspc = readin; + break; + case ADF4350_PWRDOWN: + if (readin) + st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN; + else + st->regs[ADF4350_REG2] &= ~ADF4350_REG2_POWER_DOWN_EN; + + adf4350_sync_config(st); + break; + default: + ret = -ENODEV; + } + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static ssize_t adf4350_read(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct adf4350_state *st = iio_priv(indio_dev); + unsigned long long val; + int ret = 0; + + mutex_lock(&indio_dev->mlock); + switch ((u32)private) { + case ADF4350_FREQ: + val = (u64)((st->r0_int * st->r1_mod) + st->r0_fract) * + (u64)st->fpfd; + do_div(val, st->r1_mod * (1 << st->r4_rf_div_sel)); + /* PLL unlocked? return error */ + if (gpio_is_valid(st->pdata->gpio_lock_detect)) + if (!gpio_get_value(st->pdata->gpio_lock_detect)) { + dev_dbg(&st->spi->dev, "PLL un-locked\n"); + ret = -EBUSY; + } + break; + case ADF4350_FREQ_REFIN: + val = st->clkin; + break; + case ADF4350_FREQ_RESOLUTION: + val = st->chspc; + break; + case ADF4350_PWRDOWN: + val = !!(st->regs[ADF4350_REG2] & ADF4350_REG2_POWER_DOWN_EN); + break; + } + mutex_unlock(&indio_dev->mlock); + + return ret < 0 ? ret : sprintf(buf, "%llu\n", val); +} + +#define _ADF4350_EXT_INFO(_name, _ident) { \ + .name = _name, \ + .read = adf4350_read, \ + .write = adf4350_write, \ + .private = _ident, \ +} + +static const struct iio_chan_spec_ext_info adf4350_ext_info[] = { + /* Ideally we use IIO_CHAN_INFO_FREQUENCY, but there are + * values > 2^32 in order to support the entire frequency range + * in Hz. Using scale is a bit ugly. + */ + _ADF4350_EXT_INFO("frequency", ADF4350_FREQ), + _ADF4350_EXT_INFO("frequency_resolution", ADF4350_FREQ_RESOLUTION), + _ADF4350_EXT_INFO("refin_frequency", ADF4350_FREQ_REFIN), + _ADF4350_EXT_INFO("powerdown", ADF4350_PWRDOWN), + { }, +}; + +static const struct iio_chan_spec adf4350_chan = { + .type = IIO_ALTVOLTAGE, + .indexed = 1, + .output = 1, + .ext_info = adf4350_ext_info, +}; + +static const struct iio_info adf4350_info = { + .debugfs_reg_access = &adf4350_reg_access, + .driver_module = THIS_MODULE, +}; + +static int __devinit adf4350_probe(struct spi_device *spi) +{ + struct adf4350_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev; + struct adf4350_state *st; + int ret; + + if (!pdata) { + dev_warn(&spi->dev, "no platform data? using default\n"); + + pdata = &default_pdata; + } + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = regulator_get(&spi->dev, "vcc"); + if (!IS_ERR(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + } + + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + st->pdata = pdata; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = (pdata->name[0] != 0) ? pdata->name : + spi_get_device_id(spi)->name; + + indio_dev->info = &adf4350_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &adf4350_chan; + indio_dev->num_channels = 1; + + st->chspc = pdata->channel_spacing; + st->clkin = pdata->clkin; + + st->min_out_freq = spi_get_device_id(spi)->driver_data == 4351 ? + ADF4351_MIN_OUT_FREQ : ADF4350_MIN_OUT_FREQ; + + memset(st->regs_hw, 0xFF, sizeof(st->regs_hw)); + + if (gpio_is_valid(pdata->gpio_lock_detect)) { + ret = gpio_request(pdata->gpio_lock_detect, indio_dev->name); + if (ret) { + dev_err(&spi->dev, "fail to request lock detect GPIO-%d", + pdata->gpio_lock_detect); + goto error_disable_reg; + } + gpio_direction_input(pdata->gpio_lock_detect); + } + + if (pdata->power_up_frequency) { + ret = adf4350_set_freq(st, pdata->power_up_frequency); + if (ret) + goto error_free_gpio; + } + + ret = iio_device_register(indio_dev); + if (ret) + goto error_free_gpio; + + return 0; + +error_free_gpio: + if (gpio_is_valid(pdata->gpio_lock_detect)) + gpio_free(pdata->gpio_lock_detect); + +error_disable_reg: + if (!IS_ERR(st->reg)) + regulator_disable(st->reg); +error_put_reg: + if (!IS_ERR(st->reg)) + regulator_put(st->reg); + + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit adf4350_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adf4350_state *st = iio_priv(indio_dev); + struct regulator *reg = st->reg; + + st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN; + adf4350_sync_config(st); + + iio_device_unregister(indio_dev); + + if (!IS_ERR(reg)) { + regulator_disable(reg); + regulator_put(reg); + } + + if (gpio_is_valid(st->pdata->gpio_lock_detect)) + gpio_free(st->pdata->gpio_lock_detect); + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id adf4350_id[] = { + {"adf4350", 4350}, + {"adf4351", 4351}, + {} +}; + +static struct spi_driver adf4350_driver = { + .driver = { + .name = "adf4350", + .owner = THIS_MODULE, + }, + .probe = adf4350_probe, + .remove = __devexit_p(adf4350_remove), + .id_table = adf4350_id, +}; +module_spi_driver(adf4350_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices ADF4350/ADF4351 PLL"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/iio/frequency/adf4350.h b/include/linux/iio/frequency/adf4350.h new file mode 100644 index 000000000000..b76b4a87065e --- /dev/null +++ b/include/linux/iio/frequency/adf4350.h @@ -0,0 +1,126 @@ +/* + * ADF4350/ADF4351 SPI PLL driver + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef IIO_PLL_ADF4350_H_ +#define IIO_PLL_ADF4350_H_ + +/* Registers */ +#define ADF4350_REG0 0 +#define ADF4350_REG1 1 +#define ADF4350_REG2 2 +#define ADF4350_REG3 3 +#define ADF4350_REG4 4 +#define ADF4350_REG5 5 + +/* REG0 Bit Definitions */ +#define ADF4350_REG0_FRACT(x) (((x) & 0xFFF) << 3) +#define ADF4350_REG0_INT(x) (((x) & 0xFFFF) << 15) + +/* REG1 Bit Definitions */ +#define ADF4350_REG1_MOD(x) (((x) & 0xFFF) << 3) +#define ADF4350_REG1_PHASE(x) (((x) & 0xFFF) << 15) +#define ADF4350_REG1_PRESCALER (1 << 27) + +/* REG2 Bit Definitions */ +#define ADF4350_REG2_COUNTER_RESET_EN (1 << 3) +#define ADF4350_REG2_CP_THREESTATE_EN (1 << 4) +#define ADF4350_REG2_POWER_DOWN_EN (1 << 5) +#define ADF4350_REG2_PD_POLARITY_POS (1 << 6) +#define ADF4350_REG2_LDP_6ns (1 << 7) +#define ADF4350_REG2_LDP_10ns (0 << 7) +#define ADF4350_REG2_LDF_FRACT_N (0 << 8) +#define ADF4350_REG2_LDF_INT_N (1 << 8) +#define ADF4350_REG2_CHARGE_PUMP_CURR_uA(x) (((((x)-312) / 312) & 0xF) << 9) +#define ADF4350_REG2_DOUBLE_BUFF_EN (1 << 13) +#define ADF4350_REG2_10BIT_R_CNT(x) ((x) << 14) +#define ADF4350_REG2_RDIV2_EN (1 << 24) +#define ADF4350_REG2_RMULT2_EN (1 << 25) +#define ADF4350_REG2_MUXOUT(x) ((x) << 26) +#define ADF4350_REG2_NOISE_MODE(x) ((x) << 29) +#define ADF4350_MUXOUT_THREESTATE 0 +#define ADF4350_MUXOUT_DVDD 1 +#define ADF4350_MUXOUT_GND 2 +#define ADF4350_MUXOUT_R_DIV_OUT 3 +#define ADF4350_MUXOUT_N_DIV_OUT 4 +#define ADF4350_MUXOUT_ANALOG_LOCK_DETECT 5 +#define ADF4350_MUXOUT_DIGITAL_LOCK_DETECT 6 + +/* REG3 Bit Definitions */ +#define ADF4350_REG3_12BIT_CLKDIV(x) ((x) << 3) +#define ADF4350_REG3_12BIT_CLKDIV_MODE(x) ((x) << 16) +#define ADF4350_REG3_12BIT_CSR_EN (1 << 18) +#define ADF4351_REG3_CHARGE_CANCELLATION_EN (1 << 21) +#define ADF4351_REG3_ANTI_BACKLASH_3ns_EN (1 << 22) +#define ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH (1 << 23) + +/* REG4 Bit Definitions */ +#define ADF4350_REG4_OUTPUT_PWR(x) ((x) << 3) +#define ADF4350_REG4_RF_OUT_EN (1 << 5) +#define ADF4350_REG4_AUX_OUTPUT_PWR(x) ((x) << 6) +#define ADF4350_REG4_AUX_OUTPUT_EN (1 << 8) +#define ADF4350_REG4_AUX_OUTPUT_FUND (1 << 9) +#define ADF4350_REG4_AUX_OUTPUT_DIV (0 << 9) +#define ADF4350_REG4_MUTE_TILL_LOCK_EN (1 << 10) +#define ADF4350_REG4_VCO_PWRDOWN_EN (1 << 11) +#define ADF4350_REG4_8BIT_BAND_SEL_CLKDIV(x) ((x) << 12) +#define ADF4350_REG4_RF_DIV_SEL(x) ((x) << 20) +#define ADF4350_REG4_FEEDBACK_DIVIDED (0 << 23) +#define ADF4350_REG4_FEEDBACK_FUND (1 << 23) + +/* REG5 Bit Definitions */ +#define ADF4350_REG5_LD_PIN_MODE_LOW (0 << 22) +#define ADF4350_REG5_LD_PIN_MODE_DIGITAL (1 << 22) +#define ADF4350_REG5_LD_PIN_MODE_HIGH (3 << 22) + +/* Specifications */ +#define ADF4350_MAX_OUT_FREQ 4400000000ULL /* Hz */ +#define ADF4350_MIN_OUT_FREQ 137500000 /* Hz */ +#define ADF4351_MIN_OUT_FREQ 34375000 /* Hz */ +#define ADF4350_MIN_VCO_FREQ 2200000000ULL /* Hz */ +#define ADF4350_MAX_FREQ_45_PRESC 3000000000ULL /* Hz */ +#define ADF4350_MAX_FREQ_PFD 32000000 /* Hz */ +#define ADF4350_MAX_BANDSEL_CLK 125000 /* Hz */ +#define ADF4350_MAX_FREQ_REFIN 250000000 /* Hz */ +#define ADF4350_MAX_MODULUS 4095 + +/** + * struct adf4350_platform_data - platform specific information + * @name: Optional device name. + * @clkin: REFin frequency in Hz. + * @channel_spacing: Channel spacing in Hz (influences MODULUS). + * @power_up_frequency: Optional, If set in Hz the PLL tunes to the desired + * frequency on probe. + * @ref_div_factor: Optional, if set the driver skips dynamic calculation + * and uses this default value instead. + * @ref_doubler_en: Enables reference doubler. + * @ref_div2_en: Enables reference divider. + * @r2_user_settings: User defined settings for ADF4350/1 REGISTER_2. + * @r3_user_settings: User defined settings for ADF4350/1 REGISTER_3. + * @r4_user_settings: User defined settings for ADF4350/1 REGISTER_4. + * @gpio_lock_detect: Optional, if set with a valid GPIO number, + * pll lock state is tested upon read. + * If not used - set to -1. + */ + +struct adf4350_platform_data { + char name[32]; + unsigned long clkin; + unsigned long channel_spacing; + unsigned long long power_up_frequency; + + unsigned short ref_div_factor; /* 10-bit R counter */ + bool ref_doubler_en; + bool ref_div2_en; + + unsigned r2_user_settings; + unsigned r3_user_settings; + unsigned r4_user_settings; + int gpio_lock_detect; +}; + +#endif /* IIO_PLL_ADF4350_H_ */ -- cgit v1.2.3 From e4e8b7765867e8f4705bcc18b8930edbe0e4ef3c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 4 Jun 2012 10:50:02 +0200 Subject: iio: Add iio_device_get() This patch add the iio_device_get() function, which increases the reference count of a iio device. The matching function to decrease the reference count - iio_device_put() - already exists. Signed-off-by: Lars-Peter Clausen Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/iio.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 3a4f6a3ab80d..3238fa3374f7 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -438,6 +438,17 @@ static inline struct iio_dev *dev_to_iio_dev(struct device *dev) return container_of(dev, struct iio_dev, dev); } +/** + * iio_device_get() - increment reference count for the device + * @indio_dev: IIO device structure + * + * Returns: The passed IIO device + **/ +static inline struct iio_dev *iio_device_get(struct iio_dev *indio_dev) +{ + return indio_dev ? dev_to_iio_dev(get_device(&indio_dev->dev)) : NULL; +} + /* Can we make this smaller? */ #define IIO_ALIGN L1_CACHE_BYTES /** -- cgit v1.2.3 From 5212cc8a9d833791a7aec566db136e78951f203d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 4 Jun 2012 11:36:11 +0200 Subject: iio: Add helper functions for enum style channel attributes We often have the case were we do have a enum style channel attribute. These attributes have in common that they are a list of string values which usually map in a 1-to-1 fashion to integer values. This patch implements some common helper code for implementing enum style channel attributes using extended channel attributes. The helper functions take care of converting between the string and integer values, as well providing a function for "_available" attributes which list all available enum items. Signed-off-by: Lars-Peter Clausen Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/industrialio-core.c | 63 ++++++++++++++++++++++++++++++++++++++++ include/linux/iio/iio.h | 64 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 1ddd8861c71b..56a3c0bc996c 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -289,6 +289,69 @@ static ssize_t iio_write_channel_ext_info(struct device *dev, this_attr->c, buf, len); } +ssize_t iio_enum_available_read(struct iio_dev *indio_dev, + uintptr_t priv, const struct iio_chan_spec *chan, char *buf) +{ + const struct iio_enum *e = (const struct iio_enum *)priv; + unsigned int i; + size_t len = 0; + + if (!e->num_items) + return 0; + + for (i = 0; i < e->num_items; ++i) + len += snprintf(buf + len, PAGE_SIZE - len, "%s ", e->items[i]); + + /* replace last space with a newline */ + buf[len - 1] = '\n'; + + return len; +} +EXPORT_SYMBOL_GPL(iio_enum_available_read); + +ssize_t iio_enum_read(struct iio_dev *indio_dev, + uintptr_t priv, const struct iio_chan_spec *chan, char *buf) +{ + const struct iio_enum *e = (const struct iio_enum *)priv; + int i; + + if (!e->get) + return -EINVAL; + + i = e->get(indio_dev, chan); + if (i < 0) + return i; + else if (i >= e->num_items) + return -EINVAL; + + return sprintf(buf, "%s\n", e->items[i]); +} +EXPORT_SYMBOL_GPL(iio_enum_read); + +ssize_t iio_enum_write(struct iio_dev *indio_dev, + uintptr_t priv, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + const struct iio_enum *e = (const struct iio_enum *)priv; + unsigned int i; + int ret; + + if (!e->set) + return -EINVAL; + + for (i = 0; i < e->num_items; i++) { + if (sysfs_streq(buf, e->items[i])) + break; + } + + if (i == e->num_items) + return -EINVAL; + + ret = e->set(indio_dev, chan, i); + return ret ? ret : len; +} +EXPORT_SYMBOL_GPL(iio_enum_write); + static ssize_t iio_read_channel_info(struct device *dev, struct device_attribute *attr, char *buf) diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 3238fa3374f7..944c63511731 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -129,6 +129,70 @@ struct iio_chan_spec_ext_info { uintptr_t private; }; +/** + * struct iio_enum - Enum channel info attribute + * items: A array of strings. + * num_items: Length of the item array. + * set: Set callback function, may be NULL. + * get: Get callback function, may be NULL. + * + * The iio_enum struct can be used to implement enum style channel attributes. + * Enum style attributes are those which have a set of strings which map to + * unsigned integer values. The IIO enum helper code takes care of mapping + * between value and string as well as generating a "_available" file which + * contains a list of all available items. The set callback will be called when + * the attribute is updated. The last parameter is the index to the newly + * activated item. The get callback will be used to query the currently active + * item and is supposed to return the index for it. + */ +struct iio_enum { + const char * const *items; + unsigned int num_items; + int (*set)(struct iio_dev *, const struct iio_chan_spec *, unsigned int); + int (*get)(struct iio_dev *, const struct iio_chan_spec *); +}; + +ssize_t iio_enum_available_read(struct iio_dev *indio_dev, + uintptr_t priv, const struct iio_chan_spec *chan, char *buf); +ssize_t iio_enum_read(struct iio_dev *indio_dev, + uintptr_t priv, const struct iio_chan_spec *chan, char *buf); +ssize_t iio_enum_write(struct iio_dev *indio_dev, + uintptr_t priv, const struct iio_chan_spec *chan, const char *buf, + size_t len); + +/** + * IIO_ENUM() - Initialize enum extended channel attribute + * @_name: Attribute name + * @_shared: Whether the attribute is shared between all channels + * @_e: Pointer to a iio_enum struct + * + * This should usually be used together with IIO_ENUM_AVAILABLE() + */ +#define IIO_ENUM(_name, _shared, _e) \ +{ \ + .name = (_name), \ + .shared = (_shared), \ + .read = iio_enum_read, \ + .write = iio_enum_write, \ + .private = (uintptr_t)(_e), \ +} + +/** + * IIO_ENUM_AVAILABLE() - Initialize enum available extended channel attribute + * @_name: Attribute name ("_available" will be appended to the name) + * @_e: Pointer to a iio_enum struct + * + * Creates a read only attribute which list all the available enum items in a + * space separated list. This should usually be used together with IIO_ENUM() + */ +#define IIO_ENUM_AVAILABLE(_name, _e) \ +{ \ + .name = (_name "_available"), \ + .shared = true, \ + .read = iio_enum_available_read, \ + .private = (uintptr_t)(_e), \ +} + /** * struct iio_chan_spec - specification of a single channel * @type: What type of measurement is the channel making. -- cgit v1.2.3 From dbdc025bb239ce62c9b4d28c459a98f22ce9ec0a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 4 Jun 2012 11:36:28 +0200 Subject: staging:iio: Move DAC drivers out of staging The IIO DAC drivers are in a reasonably good shape. They all make use of channel spec and non of them provides non-documented sysfs attributes. Code style should be OK as well, both checkpatch and coccicheck only report trivial issues. So lets move the whole folder out of staging. Signed-off-by: Lars-Peter Clausen Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/Kconfig | 1 + drivers/iio/Makefile | 1 + drivers/iio/dac/Kconfig | 121 +++++++ drivers/iio/dac/Makefile | 15 + drivers/iio/dac/ad5064.c | 538 ++++++++++++++++++++++++++++ drivers/iio/dac/ad5360.c | 570 +++++++++++++++++++++++++++++ drivers/iio/dac/ad5380.c | 657 ++++++++++++++++++++++++++++++++++ drivers/iio/dac/ad5421.c | 544 ++++++++++++++++++++++++++++ drivers/iio/dac/ad5446.c | 381 ++++++++++++++++++++ drivers/iio/dac/ad5446.h | 89 +++++ drivers/iio/dac/ad5504.c | 393 ++++++++++++++++++++ drivers/iio/dac/ad5624r.h | 79 ++++ drivers/iio/dac/ad5624r_spi.c | 324 +++++++++++++++++ drivers/iio/dac/ad5686.c | 418 +++++++++++++++++++++ drivers/iio/dac/ad5764.c | 382 ++++++++++++++++++++ drivers/iio/dac/ad5791.c | 485 +++++++++++++++++++++++++ drivers/iio/dac/max517.c | 243 +++++++++++++ drivers/staging/iio/Kconfig | 1 - drivers/staging/iio/Makefile | 1 - drivers/staging/iio/dac/Kconfig | 121 ------- drivers/staging/iio/dac/Makefile | 15 - drivers/staging/iio/dac/ad5064.c | 538 ---------------------------- drivers/staging/iio/dac/ad5360.c | 570 ----------------------------- drivers/staging/iio/dac/ad5380.c | 657 ---------------------------------- drivers/staging/iio/dac/ad5421.c | 544 ---------------------------- drivers/staging/iio/dac/ad5421.h | 32 -- drivers/staging/iio/dac/ad5446.c | 381 -------------------- drivers/staging/iio/dac/ad5446.h | 89 ----- drivers/staging/iio/dac/ad5504.c | 394 -------------------- drivers/staging/iio/dac/ad5504.h | 20 -- drivers/staging/iio/dac/ad5624r.h | 79 ---- drivers/staging/iio/dac/ad5624r_spi.c | 324 ----------------- drivers/staging/iio/dac/ad5686.c | 418 --------------------- drivers/staging/iio/dac/ad5764.c | 382 -------------------- drivers/staging/iio/dac/ad5791.c | 486 ------------------------- drivers/staging/iio/dac/ad5791.h | 29 -- drivers/staging/iio/dac/max517.c | 244 ------------- drivers/staging/iio/dac/max517.h | 19 - include/linux/iio/dac/ad5421.h | 28 ++ include/linux/iio/dac/ad5504.h | 16 + include/linux/iio/dac/ad5791.h | 25 ++ include/linux/iio/dac/max517.h | 15 + 42 files changed, 5325 insertions(+), 5344 deletions(-) create mode 100644 drivers/iio/dac/Kconfig create mode 100644 drivers/iio/dac/Makefile create mode 100644 drivers/iio/dac/ad5064.c create mode 100644 drivers/iio/dac/ad5360.c create mode 100644 drivers/iio/dac/ad5380.c create mode 100644 drivers/iio/dac/ad5421.c create mode 100644 drivers/iio/dac/ad5446.c create mode 100644 drivers/iio/dac/ad5446.h create mode 100644 drivers/iio/dac/ad5504.c create mode 100644 drivers/iio/dac/ad5624r.h create mode 100644 drivers/iio/dac/ad5624r_spi.c create mode 100644 drivers/iio/dac/ad5686.c create mode 100644 drivers/iio/dac/ad5764.c create mode 100644 drivers/iio/dac/ad5791.c create mode 100644 drivers/iio/dac/max517.c delete mode 100644 drivers/staging/iio/dac/Kconfig delete mode 100644 drivers/staging/iio/dac/Makefile delete mode 100644 drivers/staging/iio/dac/ad5064.c delete mode 100644 drivers/staging/iio/dac/ad5360.c delete mode 100644 drivers/staging/iio/dac/ad5380.c delete mode 100644 drivers/staging/iio/dac/ad5421.c delete mode 100644 drivers/staging/iio/dac/ad5421.h delete mode 100644 drivers/staging/iio/dac/ad5446.c delete mode 100644 drivers/staging/iio/dac/ad5446.h delete mode 100644 drivers/staging/iio/dac/ad5504.c delete mode 100644 drivers/staging/iio/dac/ad5504.h delete mode 100644 drivers/staging/iio/dac/ad5624r.h delete mode 100644 drivers/staging/iio/dac/ad5624r_spi.c delete mode 100644 drivers/staging/iio/dac/ad5686.c delete mode 100644 drivers/staging/iio/dac/ad5764.c delete mode 100644 drivers/staging/iio/dac/ad5791.c delete mode 100644 drivers/staging/iio/dac/ad5791.h delete mode 100644 drivers/staging/iio/dac/max517.c delete mode 100644 drivers/staging/iio/dac/max517.h create mode 100644 include/linux/iio/dac/ad5421.h create mode 100644 include/linux/iio/dac/ad5504.h create mode 100644 include/linux/iio/dac/ad5791.h create mode 100644 include/linux/iio/dac/max517.h (limited to 'include') diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 64c88e5cda4d..103349f2b3b5 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -52,5 +52,6 @@ source "drivers/iio/adc/Kconfig" source "drivers/iio/amplifiers/Kconfig" source "drivers/iio/light/Kconfig" source "drivers/iio/frequency/Kconfig" +source "drivers/iio/dac/Kconfig" endif # IIO diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index bd801c0bbc2f..c38fa2a40af2 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -13,3 +13,4 @@ obj-y += adc/ obj-y += amplifiers/ obj-y += light/ obj-y += frequency/ +obj-y += dac/ diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig new file mode 100644 index 000000000000..a626f03871ec --- /dev/null +++ b/drivers/iio/dac/Kconfig @@ -0,0 +1,121 @@ +# +# DAC drivers +# +menu "Digital to analog converters" + +config AD5064 + tristate "Analog Devices AD5064/64-1/65/44/45/24/25, AD5628/48/66/68 DAC driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5024, AD5025, AD5044, + AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5648, AD5666, AD5668 Digital + to Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad5064. + +config AD5360 + tristate "Analog Devices Analog Devices AD5360/61/62/63/70/71/73 DAC driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5360, AD5361, + AD5362, AD5363, AD5370, AD5371, AD5373 multi-channel + Digital to Analog Converters (DAC). + + To compile this driver as module choose M here: the module will be called + ad5360. + +config AD5380 + tristate "Analog Devices AD5380/81/82/83/84/90/91/92 DAC driver" + depends on (SPI_MASTER || I2C) + select REGMAP_I2C if I2C + select REGMAP_SPI if SPI_MASTER + help + Say yes here to build support for Analog Devices AD5380, AD5381, + AD5382, AD5383, AD5384, AD5390, AD5391, AD5392 multi-channel + Digital to Analog Converters (DAC). + + To compile this driver as module choose M here: the module will be called + ad5380. + +config AD5421 + tristate "Analog Devices AD5421 DAC driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5421 loop-powered + digital-to-analog convertors (DAC). + + To compile this driver as module choose M here: the module will be called + ad5421. + +config AD5624R_SPI + tristate "Analog Devices AD5624/44/64R DAC spi driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5624R, AD5644R and + AD5664R converters (DAC). This driver uses the common SPI interface. + +config AD5446 + tristate "Analog Devices AD5446 and similar single channel DACs driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5444, AD5446, + AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5611, AD5620, + AD5621, AD5640, AD5660, AD5662 DACs. + + To compile this driver as a module, choose M here: the + module will be called ad5446. + +config AD5504 + tristate "Analog Devices AD5504/AD5501 DAC SPI driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5504, AD5501, + High Voltage Digital to Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad5504. + +config AD5764 + tristate "Analog Devices AD5764/64R/44/44R DAC driver" + depends on SPI_MASTER + help + Say yes here to build support for Analog Devices AD5764, AD5764R, AD5744, + AD5744R Digital to Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad5764. + +config AD5791 + tristate "Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC SPI driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5760, AD5780, + AD5781, AD5790, AD5791 High Resolution Voltage Output Digital to + Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad5791. + +config AD5686 + tristate "Analog Devices AD5686R/AD5685R/AD5684R DAC SPI driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD5686R, AD5685R, + AD5684R, AD5791 Voltage Output Digital to + Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad5686. + +config MAX517 + tristate "Maxim MAX517/518/519 DAC driver" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the Maxim chips MAX517, + MAX518 and MAX519 (I2C 8-Bit DACs with rail-to-rail outputs). + + This driver can also be built as a module. If so, the module + will be called max517. + +endmenu diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile new file mode 100644 index 000000000000..8ab1d264aab7 --- /dev/null +++ b/drivers/iio/dac/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for industrial I/O DAC drivers +# + +obj-$(CONFIG_AD5360) += ad5360.o +obj-$(CONFIG_AD5380) += ad5380.o +obj-$(CONFIG_AD5421) += ad5421.o +obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o +obj-$(CONFIG_AD5064) += ad5064.o +obj-$(CONFIG_AD5504) += ad5504.o +obj-$(CONFIG_AD5446) += ad5446.o +obj-$(CONFIG_AD5764) += ad5764.o +obj-$(CONFIG_AD5791) += ad5791.o +obj-$(CONFIG_AD5686) += ad5686.o +obj-$(CONFIG_MAX517) += max517.o diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c new file mode 100644 index 000000000000..276af02520af --- /dev/null +++ b/drivers/iio/dac/ad5064.c @@ -0,0 +1,538 @@ +/* + * AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5648, + * AD5666, AD5668 Digital to analog converters driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define AD5064_MAX_DAC_CHANNELS 8 +#define AD5064_MAX_VREFS 4 + +#define AD5064_ADDR(x) ((x) << 20) +#define AD5064_CMD(x) ((x) << 24) + +#define AD5064_ADDR_DAC(chan) (chan) +#define AD5064_ADDR_ALL_DAC 0xF + +#define AD5064_CMD_WRITE_INPUT_N 0x0 +#define AD5064_CMD_UPDATE_DAC_N 0x1 +#define AD5064_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2 +#define AD5064_CMD_WRITE_INPUT_N_UPDATE_N 0x3 +#define AD5064_CMD_POWERDOWN_DAC 0x4 +#define AD5064_CMD_CLEAR 0x5 +#define AD5064_CMD_LDAC_MASK 0x6 +#define AD5064_CMD_RESET 0x7 +#define AD5064_CMD_CONFIG 0x8 + +#define AD5064_CONFIG_DAISY_CHAIN_ENABLE BIT(1) +#define AD5064_CONFIG_INT_VREF_ENABLE BIT(0) + +#define AD5064_LDAC_PWRDN_NONE 0x0 +#define AD5064_LDAC_PWRDN_1K 0x1 +#define AD5064_LDAC_PWRDN_100K 0x2 +#define AD5064_LDAC_PWRDN_3STATE 0x3 + +/** + * struct ad5064_chip_info - chip specific information + * @shared_vref: whether the vref supply is shared between channels + * @internal_vref: internal reference voltage. 0 if the chip has no internal + * vref. + * @channel: channel specification + * @num_channels: number of channels + */ + +struct ad5064_chip_info { + bool shared_vref; + unsigned long internal_vref; + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +/** + * struct ad5064_state - driver instance specific data + * @spi: spi_device + * @chip_info: chip model specific constants, available modes etc + * @vref_reg: vref supply regulators + * @pwr_down: whether channel is powered down + * @pwr_down_mode: channel's current power down mode + * @dac_cache: current DAC raw value (chip does not support readback) + * @use_internal_vref: set to true if the internal reference voltage should be + * used. + * @data: spi transfer buffers + */ + +struct ad5064_state { + struct spi_device *spi; + const struct ad5064_chip_info *chip_info; + struct regulator_bulk_data vref_reg[AD5064_MAX_VREFS]; + bool pwr_down[AD5064_MAX_DAC_CHANNELS]; + u8 pwr_down_mode[AD5064_MAX_DAC_CHANNELS]; + unsigned int dac_cache[AD5064_MAX_DAC_CHANNELS]; + bool use_internal_vref; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be32 data ____cacheline_aligned; +}; + +enum ad5064_type { + ID_AD5024, + ID_AD5025, + ID_AD5044, + ID_AD5045, + ID_AD5064, + ID_AD5064_1, + ID_AD5065, + ID_AD5628_1, + ID_AD5628_2, + ID_AD5648_1, + ID_AD5648_2, + ID_AD5666_1, + ID_AD5666_2, + ID_AD5668_1, + ID_AD5668_2, +}; + +static int ad5064_spi_write(struct ad5064_state *st, unsigned int cmd, + unsigned int addr, unsigned int val, unsigned int shift) +{ + val <<= shift; + + st->data = cpu_to_be32(AD5064_CMD(cmd) | AD5064_ADDR(addr) | val); + + return spi_write(st->spi, &st->data, sizeof(st->data)); +} + +static int ad5064_sync_powerdown_mode(struct ad5064_state *st, + unsigned int channel) +{ + unsigned int val; + int ret; + + val = (0x1 << channel); + + if (st->pwr_down[channel]) + val |= st->pwr_down_mode[channel] << 8; + + ret = ad5064_spi_write(st, AD5064_CMD_POWERDOWN_DAC, 0, val, 0); + + return ret; +} + +static const char * const ad5064_powerdown_modes[] = { + "1kohm_to_gnd", + "100kohm_to_gnd", + "three_state", +}; + +static int ad5064_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5064_state *st = iio_priv(indio_dev); + + return st->pwr_down_mode[chan->channel] - 1; +} + +static int ad5064_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct ad5064_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&indio_dev->mlock); + st->pwr_down_mode[chan->channel] = mode + 1; + + ret = ad5064_sync_powerdown_mode(st, chan->channel); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static const struct iio_enum ad5064_powerdown_mode_enum = { + .items = ad5064_powerdown_modes, + .num_items = ARRAY_SIZE(ad5064_powerdown_modes), + .get = ad5064_get_powerdown_mode, + .set = ad5064_set_powerdown_mode, +}; + +static ssize_t ad5064_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct ad5064_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->pwr_down[chan->channel]); +} + +static ssize_t ad5064_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + struct ad5064_state *st = iio_priv(indio_dev); + bool pwr_down; + int ret; + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + st->pwr_down[chan->channel] = pwr_down; + + ret = ad5064_sync_powerdown_mode(st, chan->channel); + mutex_unlock(&indio_dev->mlock); + return ret ? ret : len; +} + +static int ad5064_get_vref(struct ad5064_state *st, + struct iio_chan_spec const *chan) +{ + unsigned int i; + + if (st->use_internal_vref) + return st->chip_info->internal_vref; + + i = st->chip_info->shared_vref ? 0 : chan->channel; + return regulator_get_voltage(st->vref_reg[i].consumer); +} + +static int ad5064_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad5064_state *st = iio_priv(indio_dev); + int scale_uv; + + switch (m) { + case IIO_CHAN_INFO_RAW: + *val = st->dac_cache[chan->channel]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = ad5064_get_vref(st, chan); + if (scale_uv < 0) + return scale_uv; + + scale_uv = (scale_uv * 100) >> chan->scan_type.realbits; + *val = scale_uv / 100000; + *val2 = (scale_uv % 100000) * 10; + return IIO_VAL_INT_PLUS_MICRO; + default: + break; + } + return -EINVAL; +} + +static int ad5064_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + struct ad5064_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val > (1 << chan->scan_type.realbits) || val < 0) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + ret = ad5064_spi_write(st, AD5064_CMD_WRITE_INPUT_N_UPDATE_N, + chan->address, val, chan->scan_type.shift); + if (ret == 0) + st->dac_cache[chan->channel] = val; + mutex_unlock(&indio_dev->mlock); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct iio_info ad5064_info = { + .read_raw = ad5064_read_raw, + .write_raw = ad5064_write_raw, + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec_ext_info ad5064_ext_info[] = { + { + .name = "powerdown", + .read = ad5064_read_dac_powerdown, + .write = ad5064_write_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", false, &ad5064_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", &ad5064_powerdown_mode_enum), + { }, +}; + +#define AD5064_CHANNEL(chan, bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ + .address = AD5064_ADDR_DAC(chan), \ + .scan_type = IIO_ST('u', (bits), 16, 20 - (bits)), \ + .ext_info = ad5064_ext_info, \ +} + +#define DECLARE_AD5064_CHANNELS(name, bits) \ +const struct iio_chan_spec name[] = { \ + AD5064_CHANNEL(0, bits), \ + AD5064_CHANNEL(1, bits), \ + AD5064_CHANNEL(2, bits), \ + AD5064_CHANNEL(3, bits), \ + AD5064_CHANNEL(4, bits), \ + AD5064_CHANNEL(5, bits), \ + AD5064_CHANNEL(6, bits), \ + AD5064_CHANNEL(7, bits), \ +} + +static DECLARE_AD5064_CHANNELS(ad5024_channels, 12); +static DECLARE_AD5064_CHANNELS(ad5044_channels, 14); +static DECLARE_AD5064_CHANNELS(ad5064_channels, 16); + +static const struct ad5064_chip_info ad5064_chip_info_tbl[] = { + [ID_AD5024] = { + .shared_vref = false, + .channels = ad5024_channels, + .num_channels = 4, + }, + [ID_AD5025] = { + .shared_vref = false, + .channels = ad5024_channels, + .num_channels = 2, + }, + [ID_AD5044] = { + .shared_vref = false, + .channels = ad5044_channels, + .num_channels = 4, + }, + [ID_AD5045] = { + .shared_vref = false, + .channels = ad5044_channels, + .num_channels = 2, + }, + [ID_AD5064] = { + .shared_vref = false, + .channels = ad5064_channels, + .num_channels = 4, + }, + [ID_AD5064_1] = { + .shared_vref = true, + .channels = ad5064_channels, + .num_channels = 4, + }, + [ID_AD5065] = { + .shared_vref = false, + .channels = ad5064_channels, + .num_channels = 2, + }, + [ID_AD5628_1] = { + .shared_vref = true, + .internal_vref = 2500000, + .channels = ad5024_channels, + .num_channels = 8, + }, + [ID_AD5628_2] = { + .shared_vref = true, + .internal_vref = 5000000, + .channels = ad5024_channels, + .num_channels = 8, + }, + [ID_AD5648_1] = { + .shared_vref = true, + .internal_vref = 2500000, + .channels = ad5044_channels, + .num_channels = 8, + }, + [ID_AD5648_2] = { + .shared_vref = true, + .internal_vref = 5000000, + .channels = ad5044_channels, + .num_channels = 8, + }, + [ID_AD5666_1] = { + .shared_vref = true, + .internal_vref = 2500000, + .channels = ad5064_channels, + .num_channels = 4, + }, + [ID_AD5666_2] = { + .shared_vref = true, + .internal_vref = 5000000, + .channels = ad5064_channels, + .num_channels = 4, + }, + [ID_AD5668_1] = { + .shared_vref = true, + .internal_vref = 2500000, + .channels = ad5064_channels, + .num_channels = 8, + }, + [ID_AD5668_2] = { + .shared_vref = true, + .internal_vref = 5000000, + .channels = ad5064_channels, + .num_channels = 8, + }, +}; + +static inline unsigned int ad5064_num_vref(struct ad5064_state *st) +{ + return st->chip_info->shared_vref ? 1 : st->chip_info->num_channels; +} + +static const char * const ad5064_vref_names[] = { + "vrefA", + "vrefB", + "vrefC", + "vrefD", +}; + +static const char * const ad5064_vref_name(struct ad5064_state *st, + unsigned int vref) +{ + return st->chip_info->shared_vref ? "vref" : ad5064_vref_names[vref]; +} + +static int __devinit ad5064_probe(struct spi_device *spi) +{ + enum ad5064_type type = spi_get_device_id(spi)->driver_data; + struct iio_dev *indio_dev; + struct ad5064_state *st; + unsigned int i; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->chip_info = &ad5064_chip_info_tbl[type]; + st->spi = spi; + + for (i = 0; i < ad5064_num_vref(st); ++i) + st->vref_reg[i].supply = ad5064_vref_name(st, i); + + ret = regulator_bulk_get(&st->spi->dev, ad5064_num_vref(st), + st->vref_reg); + if (ret) { + if (!st->chip_info->internal_vref) + goto error_free; + st->use_internal_vref = true; + ret = ad5064_spi_write(st, AD5064_CMD_CONFIG, 0, + AD5064_CONFIG_INT_VREF_ENABLE, 0); + if (ret) { + dev_err(&spi->dev, "Failed to enable internal vref: %d\n", + ret); + goto error_free; + } + } else { + ret = regulator_bulk_enable(ad5064_num_vref(st), st->vref_reg); + if (ret) + goto error_free_reg; + } + + for (i = 0; i < st->chip_info->num_channels; ++i) { + st->pwr_down_mode[i] = AD5064_LDAC_PWRDN_1K; + st->dac_cache[i] = 0x8000; + } + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad5064_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_disable_reg; + + return 0; + +error_disable_reg: + if (!st->use_internal_vref) + regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg); +error_free_reg: + if (!st->use_internal_vref) + regulator_bulk_free(ad5064_num_vref(st), st->vref_reg); +error_free: + iio_device_free(indio_dev); + + return ret; +} + + +static int __devexit ad5064_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5064_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + if (!st->use_internal_vref) { + regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg); + regulator_bulk_free(ad5064_num_vref(st), st->vref_reg); + } + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5064_id[] = { + {"ad5024", ID_AD5024}, + {"ad5025", ID_AD5025}, + {"ad5044", ID_AD5044}, + {"ad5045", ID_AD5045}, + {"ad5064", ID_AD5064}, + {"ad5064-1", ID_AD5064_1}, + {"ad5065", ID_AD5065}, + {"ad5628-1", ID_AD5628_1}, + {"ad5628-2", ID_AD5628_2}, + {"ad5648-1", ID_AD5648_1}, + {"ad5648-2", ID_AD5648_2}, + {"ad5666-1", ID_AD5666_1}, + {"ad5666-2", ID_AD5666_2}, + {"ad5668-1", ID_AD5668_1}, + {"ad5668-2", ID_AD5668_2}, + {"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */ + {} +}; +MODULE_DEVICE_TABLE(spi, ad5064_id); + +static struct spi_driver ad5064_driver = { + .driver = { + .name = "ad5064", + .owner = THIS_MODULE, + }, + .probe = ad5064_probe, + .remove = __devexit_p(ad5064_remove), + .id_table = ad5064_id, +}; +module_spi_driver(ad5064_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD5024/25/44/45/64/64-1/65, AD5628/48/66/68 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c new file mode 100644 index 000000000000..8fce84fe70b1 --- /dev/null +++ b/drivers/iio/dac/ad5360.c @@ -0,0 +1,570 @@ +/* + * Analog devices AD5360, AD5361, AD5362, AD5363, AD5370, AD5371, AD5373 + * multi-channel Digital to Analog Converters driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define AD5360_CMD(x) ((x) << 22) +#define AD5360_ADDR(x) ((x) << 16) + +#define AD5360_READBACK_TYPE(x) ((x) << 13) +#define AD5360_READBACK_ADDR(x) ((x) << 7) + +#define AD5360_CHAN_ADDR(chan) ((chan) + 0x8) + +#define AD5360_CMD_WRITE_DATA 0x3 +#define AD5360_CMD_WRITE_OFFSET 0x2 +#define AD5360_CMD_WRITE_GAIN 0x1 +#define AD5360_CMD_SPECIAL_FUNCTION 0x0 + +/* Special function register addresses */ +#define AD5360_REG_SF_NOP 0x0 +#define AD5360_REG_SF_CTRL 0x1 +#define AD5360_REG_SF_OFS(x) (0x2 + (x)) +#define AD5360_REG_SF_READBACK 0x5 + +#define AD5360_SF_CTRL_PWR_DOWN BIT(0) + +#define AD5360_READBACK_X1A 0x0 +#define AD5360_READBACK_X1B 0x1 +#define AD5360_READBACK_OFFSET 0x2 +#define AD5360_READBACK_GAIN 0x3 +#define AD5360_READBACK_SF 0x4 + + +/** + * struct ad5360_chip_info - chip specific information + * @channel_template: channel specification template + * @num_channels: number of channels + * @channels_per_group: number of channels per group + * @num_vrefs: number of vref supplies for the chip +*/ + +struct ad5360_chip_info { + struct iio_chan_spec channel_template; + unsigned int num_channels; + unsigned int channels_per_group; + unsigned int num_vrefs; +}; + +/** + * struct ad5360_state - driver instance specific data + * @spi: spi_device + * @chip_info: chip model specific constants, available modes etc + * @vref_reg: vref supply regulators + * @ctrl: control register cache + * @data: spi transfer buffers + */ + +struct ad5360_state { + struct spi_device *spi; + const struct ad5360_chip_info *chip_info; + struct regulator_bulk_data vref_reg[3]; + unsigned int ctrl; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + union { + __be32 d32; + u8 d8[4]; + } data[2] ____cacheline_aligned; +}; + +enum ad5360_type { + ID_AD5360, + ID_AD5361, + ID_AD5362, + ID_AD5363, + ID_AD5370, + ID_AD5371, + ID_AD5372, + ID_AD5373, +}; + +#define AD5360_CHANNEL(bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \ + .scan_type = IIO_ST('u', (bits), 16, 16 - (bits)) \ +} + +static const struct ad5360_chip_info ad5360_chip_info_tbl[] = { + [ID_AD5360] = { + .channel_template = AD5360_CHANNEL(16), + .num_channels = 16, + .channels_per_group = 8, + .num_vrefs = 2, + }, + [ID_AD5361] = { + .channel_template = AD5360_CHANNEL(14), + .num_channels = 16, + .channels_per_group = 8, + .num_vrefs = 2, + }, + [ID_AD5362] = { + .channel_template = AD5360_CHANNEL(16), + .num_channels = 8, + .channels_per_group = 4, + .num_vrefs = 2, + }, + [ID_AD5363] = { + .channel_template = AD5360_CHANNEL(14), + .num_channels = 8, + .channels_per_group = 4, + .num_vrefs = 2, + }, + [ID_AD5370] = { + .channel_template = AD5360_CHANNEL(16), + .num_channels = 40, + .channels_per_group = 8, + .num_vrefs = 2, + }, + [ID_AD5371] = { + .channel_template = AD5360_CHANNEL(14), + .num_channels = 40, + .channels_per_group = 8, + .num_vrefs = 3, + }, + [ID_AD5372] = { + .channel_template = AD5360_CHANNEL(16), + .num_channels = 32, + .channels_per_group = 8, + .num_vrefs = 2, + }, + [ID_AD5373] = { + .channel_template = AD5360_CHANNEL(14), + .num_channels = 32, + .channels_per_group = 8, + .num_vrefs = 2, + }, +}; + +static unsigned int ad5360_get_channel_vref_index(struct ad5360_state *st, + unsigned int channel) +{ + unsigned int i; + + /* The first groups have their own vref, while the remaining groups + * share the last vref */ + i = channel / st->chip_info->channels_per_group; + if (i >= st->chip_info->num_vrefs) + i = st->chip_info->num_vrefs - 1; + + return i; +} + +static int ad5360_get_channel_vref(struct ad5360_state *st, + unsigned int channel) +{ + unsigned int i = ad5360_get_channel_vref_index(st, channel); + + return regulator_get_voltage(st->vref_reg[i].consumer); +} + + +static int ad5360_write_unlocked(struct iio_dev *indio_dev, + unsigned int cmd, unsigned int addr, unsigned int val, + unsigned int shift) +{ + struct ad5360_state *st = iio_priv(indio_dev); + + val <<= shift; + val |= AD5360_CMD(cmd) | AD5360_ADDR(addr); + st->data[0].d32 = cpu_to_be32(val); + + return spi_write(st->spi, &st->data[0].d8[1], 3); +} + +static int ad5360_write(struct iio_dev *indio_dev, unsigned int cmd, + unsigned int addr, unsigned int val, unsigned int shift) +{ + int ret; + + mutex_lock(&indio_dev->mlock); + ret = ad5360_write_unlocked(indio_dev, cmd, addr, val, shift); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5360_read(struct iio_dev *indio_dev, unsigned int type, + unsigned int addr) +{ + struct ad5360_state *st = iio_priv(indio_dev); + struct spi_message m; + int ret; + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[1], + .len = 3, + .cs_change = 1, + }, { + .rx_buf = &st->data[1].d8[1], + .len = 3, + }, + }; + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + mutex_lock(&indio_dev->mlock); + + st->data[0].d32 = cpu_to_be32(AD5360_CMD(AD5360_CMD_SPECIAL_FUNCTION) | + AD5360_ADDR(AD5360_REG_SF_READBACK) | + AD5360_READBACK_TYPE(type) | + AD5360_READBACK_ADDR(addr)); + + ret = spi_sync(st->spi, &m); + if (ret >= 0) + ret = be32_to_cpu(st->data[1].d32) & 0xffff; + + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t ad5360_read_dac_powerdown(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad5360_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", (bool)(st->ctrl & AD5360_SF_CTRL_PWR_DOWN)); +} + +static int ad5360_update_ctrl(struct iio_dev *indio_dev, unsigned int set, + unsigned int clr) +{ + struct ad5360_state *st = iio_priv(indio_dev); + unsigned int ret; + + mutex_lock(&indio_dev->mlock); + + st->ctrl |= set; + st->ctrl &= ~clr; + + ret = ad5360_write_unlocked(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, + AD5360_REG_SF_CTRL, st->ctrl, 0); + + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static ssize_t ad5360_write_dac_powerdown(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + bool pwr_down; + int ret; + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + if (pwr_down) + ret = ad5360_update_ctrl(indio_dev, AD5360_SF_CTRL_PWR_DOWN, 0); + else + ret = ad5360_update_ctrl(indio_dev, 0, AD5360_SF_CTRL_PWR_DOWN); + + return ret ? ret : len; +} + +static IIO_DEVICE_ATTR(out_voltage_powerdown, + S_IRUGO | S_IWUSR, + ad5360_read_dac_powerdown, + ad5360_write_dac_powerdown, 0); + +static struct attribute *ad5360_attributes[] = { + &iio_dev_attr_out_voltage_powerdown.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad5360_attribute_group = { + .attrs = ad5360_attributes, +}; + +static int ad5360_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad5360_state *st = iio_priv(indio_dev); + int max_val = (1 << chan->scan_type.realbits); + unsigned int ofs_index; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val >= max_val || val < 0) + return -EINVAL; + + return ad5360_write(indio_dev, AD5360_CMD_WRITE_DATA, + chan->address, val, chan->scan_type.shift); + + case IIO_CHAN_INFO_CALIBBIAS: + if (val >= max_val || val < 0) + return -EINVAL; + + return ad5360_write(indio_dev, AD5360_CMD_WRITE_OFFSET, + chan->address, val, chan->scan_type.shift); + + case IIO_CHAN_INFO_CALIBSCALE: + if (val >= max_val || val < 0) + return -EINVAL; + + return ad5360_write(indio_dev, AD5360_CMD_WRITE_GAIN, + chan->address, val, chan->scan_type.shift); + + case IIO_CHAN_INFO_OFFSET: + if (val <= -max_val || val > 0) + return -EINVAL; + + val = -val; + + /* offset is supposed to have the same scale as raw, but it + * is always 14bits wide, so on a chip where the raw value has + * more bits, we need to shift offset. */ + val >>= (chan->scan_type.realbits - 14); + + /* There is one DAC offset register per vref. Changing one + * channels offset will also change the offset for all other + * channels which share the same vref supply. */ + ofs_index = ad5360_get_channel_vref_index(st, chan->channel); + return ad5360_write(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, + AD5360_REG_SF_OFS(ofs_index), val, 0); + default: + break; + } + + return -EINVAL; +} + +static int ad5360_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad5360_state *st = iio_priv(indio_dev); + unsigned int ofs_index; + int scale_uv; + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ad5360_read(indio_dev, AD5360_READBACK_X1A, + chan->address); + if (ret < 0) + return ret; + *val = ret >> chan->scan_type.shift; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* vout = 4 * vref * dac_code */ + scale_uv = ad5360_get_channel_vref(st, chan->channel) * 4 * 100; + if (scale_uv < 0) + return scale_uv; + + scale_uv >>= (chan->scan_type.realbits); + *val = scale_uv / 100000; + *val2 = (scale_uv % 100000) * 10; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + ret = ad5360_read(indio_dev, AD5360_READBACK_OFFSET, + chan->address); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + ret = ad5360_read(indio_dev, AD5360_READBACK_GAIN, + chan->address); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + ofs_index = ad5360_get_channel_vref_index(st, chan->channel); + ret = ad5360_read(indio_dev, AD5360_READBACK_SF, + AD5360_REG_SF_OFS(ofs_index)); + if (ret < 0) + return ret; + + ret <<= (chan->scan_type.realbits - 14); + *val = -ret; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static const struct iio_info ad5360_info = { + .read_raw = ad5360_read_raw, + .write_raw = ad5360_write_raw, + .attrs = &ad5360_attribute_group, + .driver_module = THIS_MODULE, +}; + +static const char * const ad5360_vref_name[] = { + "vref0", "vref1", "vref2" +}; + +static int __devinit ad5360_alloc_channels(struct iio_dev *indio_dev) +{ + struct ad5360_state *st = iio_priv(indio_dev); + struct iio_chan_spec *channels; + unsigned int i; + + channels = kcalloc(st->chip_info->num_channels, + sizeof(struct iio_chan_spec), GFP_KERNEL); + + if (!channels) + return -ENOMEM; + + for (i = 0; i < st->chip_info->num_channels; ++i) { + channels[i] = st->chip_info->channel_template; + channels[i].channel = i; + channels[i].address = AD5360_CHAN_ADDR(i); + } + + indio_dev->channels = channels; + + return 0; +} + +static int __devinit ad5360_probe(struct spi_device *spi) +{ + enum ad5360_type type = spi_get_device_id(spi)->driver_data; + struct iio_dev *indio_dev; + struct ad5360_state *st; + unsigned int i; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + dev_err(&spi->dev, "Failed to allocate iio device\n"); + return -ENOMEM; + } + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->chip_info = &ad5360_chip_info_tbl[type]; + st->spi = spi; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad5360_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = st->chip_info->num_channels; + + ret = ad5360_alloc_channels(indio_dev); + if (ret) { + dev_err(&spi->dev, "Failed to allocate channel spec: %d\n", ret); + goto error_free; + } + + for (i = 0; i < st->chip_info->num_vrefs; ++i) + st->vref_reg[i].supply = ad5360_vref_name[i]; + + ret = regulator_bulk_get(&st->spi->dev, st->chip_info->num_vrefs, + st->vref_reg); + if (ret) { + dev_err(&spi->dev, "Failed to request vref regulators: %d\n", ret); + goto error_free_channels; + } + + ret = regulator_bulk_enable(st->chip_info->num_vrefs, st->vref_reg); + if (ret) { + dev_err(&spi->dev, "Failed to enable vref regulators: %d\n", ret); + goto error_free_reg; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); + goto error_disable_reg; + } + + return 0; + +error_disable_reg: + regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg); +error_free_reg: + regulator_bulk_free(st->chip_info->num_vrefs, st->vref_reg); +error_free_channels: + kfree(indio_dev->channels); +error_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad5360_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5360_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + kfree(indio_dev->channels); + + regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg); + regulator_bulk_free(st->chip_info->num_vrefs, st->vref_reg); + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5360_ids[] = { + { "ad5360", ID_AD5360 }, + { "ad5361", ID_AD5361 }, + { "ad5362", ID_AD5362 }, + { "ad5363", ID_AD5363 }, + { "ad5370", ID_AD5370 }, + { "ad5371", ID_AD5371 }, + { "ad5372", ID_AD5372 }, + { "ad5373", ID_AD5373 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5360_ids); + +static struct spi_driver ad5360_driver = { + .driver = { + .name = "ad5360", + .owner = THIS_MODULE, + }, + .probe = ad5360_probe, + .remove = __devexit_p(ad5360_remove), + .id_table = ad5360_ids, +}; +module_spi_driver(ad5360_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD5360/61/62/63/70/71/72/73 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c new file mode 100644 index 000000000000..5dfb4451728f --- /dev/null +++ b/drivers/iio/dac/ad5380.c @@ -0,0 +1,657 @@ +/* + * Analog devices AD5380, AD5381, AD5382, AD5383, AD5390, AD5391, AD5392 + * multi-channel Digital to Analog Converters driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define AD5380_REG_DATA(x) (((x) << 2) | 3) +#define AD5380_REG_OFFSET(x) (((x) << 2) | 2) +#define AD5380_REG_GAIN(x) (((x) << 2) | 1) +#define AD5380_REG_SF_PWR_DOWN (8 << 2) +#define AD5380_REG_SF_PWR_UP (9 << 2) +#define AD5380_REG_SF_CTRL (12 << 2) + +#define AD5380_CTRL_PWR_DOWN_MODE_OFFSET 13 +#define AD5380_CTRL_INT_VREF_2V5 BIT(12) +#define AD5380_CTRL_INT_VREF_EN BIT(10) + +/** + * struct ad5380_chip_info - chip specific information + * @channel_template: channel specification template + * @num_channels: number of channels + * @int_vref: internal vref in uV +*/ + +struct ad5380_chip_info { + struct iio_chan_spec channel_template; + unsigned int num_channels; + unsigned int int_vref; +}; + +/** + * struct ad5380_state - driver instance specific data + * @regmap: regmap instance used by the device + * @chip_info: chip model specific constants, available modes etc + * @vref_reg: vref supply regulator + * @vref: actual reference voltage used in uA + * @pwr_down: whether the chip is currently in power down mode + */ + +struct ad5380_state { + struct regmap *regmap; + const struct ad5380_chip_info *chip_info; + struct regulator *vref_reg; + int vref; + bool pwr_down; +}; + +enum ad5380_type { + ID_AD5380_3, + ID_AD5380_5, + ID_AD5381_3, + ID_AD5381_5, + ID_AD5382_3, + ID_AD5382_5, + ID_AD5383_3, + ID_AD5383_5, + ID_AD5390_3, + ID_AD5390_5, + ID_AD5391_3, + ID_AD5391_5, + ID_AD5392_3, + ID_AD5392_5, +}; + +static ssize_t ad5380_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct ad5380_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->pwr_down); +} + +static ssize_t ad5380_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + struct ad5380_state *st = iio_priv(indio_dev); + bool pwr_down; + int ret; + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + + if (pwr_down) + ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_DOWN, 0); + else + ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_UP, 0); + + st->pwr_down = pwr_down; + + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static const char * const ad5380_powerdown_modes[] = { + "100kohm_to_gnd", + "three_state", +}; + +static int ad5380_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5380_state *st = iio_priv(indio_dev); + unsigned int mode; + int ret; + + ret = regmap_read(st->regmap, AD5380_REG_SF_CTRL, &mode); + if (ret) + return ret; + + mode = (mode >> AD5380_CTRL_PWR_DOWN_MODE_OFFSET) & 1; + + return mode; +} + +static int ad5380_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct ad5380_state *st = iio_priv(indio_dev); + int ret; + + ret = regmap_update_bits(st->regmap, AD5380_REG_SF_CTRL, + 1 << AD5380_CTRL_PWR_DOWN_MODE_OFFSET, + mode << AD5380_CTRL_PWR_DOWN_MODE_OFFSET); + + return ret; +} + +static const struct iio_enum ad5380_powerdown_mode_enum = { + .items = ad5380_powerdown_modes, + .num_items = ARRAY_SIZE(ad5380_powerdown_modes), + .get = ad5380_get_powerdown_mode, + .set = ad5380_set_powerdown_mode, +}; + +static unsigned int ad5380_info_to_reg(struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case 0: + return AD5380_REG_DATA(chan->address); + case IIO_CHAN_INFO_CALIBBIAS: + return AD5380_REG_OFFSET(chan->address); + case IIO_CHAN_INFO_CALIBSCALE: + return AD5380_REG_GAIN(chan->address); + default: + break; + } + + return 0; +} + +static int ad5380_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long info) +{ + const unsigned int max_val = (1 << chan->scan_type.realbits); + struct ad5380_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_CALIBSCALE: + if (val >= max_val || val < 0) + return -EINVAL; + + return regmap_write(st->regmap, + ad5380_info_to_reg(chan, info), + val << chan->scan_type.shift); + case IIO_CHAN_INFO_CALIBBIAS: + val += (1 << chan->scan_type.realbits) / 2; + if (val >= max_val || val < 0) + return -EINVAL; + + return regmap_write(st->regmap, + AD5380_REG_OFFSET(chan->address), + val << chan->scan_type.shift); + default: + break; + } + return -EINVAL; +} + +static int ad5380_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long info) +{ + struct ad5380_state *st = iio_priv(indio_dev); + unsigned long scale_uv; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_CALIBSCALE: + ret = regmap_read(st->regmap, ad5380_info_to_reg(chan, info), + val); + if (ret) + return ret; + *val >>= chan->scan_type.shift; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + ret = regmap_read(st->regmap, AD5380_REG_OFFSET(chan->address), + val); + if (ret) + return ret; + *val >>= chan->scan_type.shift; + val -= (1 << chan->scan_type.realbits) / 2; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = ((2 * st->vref) >> chan->scan_type.realbits) * 100; + *val = scale_uv / 100000; + *val2 = (scale_uv % 100000) * 10; + return IIO_VAL_INT_PLUS_MICRO; + default: + break; + } + + return -EINVAL; +} + +static const struct iio_info ad5380_info = { + .read_raw = ad5380_read_raw, + .write_raw = ad5380_write_raw, + .driver_module = THIS_MODULE, +}; + +static struct iio_chan_spec_ext_info ad5380_ext_info[] = { + { + .name = "powerdown", + .read = ad5380_read_dac_powerdown, + .write = ad5380_write_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", true, &ad5380_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", &ad5380_powerdown_mode_enum), + { }, +}; + +#define AD5380_CHANNEL(_bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT | \ + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \ + .scan_type = IIO_ST('u', (_bits), 16, 14 - (_bits)), \ + .ext_info = ad5380_ext_info, \ +} + +static const struct ad5380_chip_info ad5380_chip_info_tbl[] = { + [ID_AD5380_3] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 40, + .int_vref = 1250000, + }, + [ID_AD5380_5] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 40, + .int_vref = 2500000, + }, + [ID_AD5381_3] = { + .channel_template = AD5380_CHANNEL(12), + .num_channels = 16, + .int_vref = 1250000, + }, + [ID_AD5381_5] = { + .channel_template = AD5380_CHANNEL(12), + .num_channels = 16, + .int_vref = 2500000, + }, + [ID_AD5382_3] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 32, + .int_vref = 1250000, + }, + [ID_AD5382_5] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 32, + .int_vref = 2500000, + }, + [ID_AD5383_3] = { + .channel_template = AD5380_CHANNEL(12), + .num_channels = 32, + .int_vref = 1250000, + }, + [ID_AD5383_5] = { + .channel_template = AD5380_CHANNEL(12), + .num_channels = 32, + .int_vref = 2500000, + }, + [ID_AD5390_3] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 16, + .int_vref = 1250000, + }, + [ID_AD5390_5] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 16, + .int_vref = 2500000, + }, + [ID_AD5391_3] = { + .channel_template = AD5380_CHANNEL(12), + .num_channels = 16, + .int_vref = 1250000, + }, + [ID_AD5391_5] = { + .channel_template = AD5380_CHANNEL(12), + .num_channels = 16, + .int_vref = 2500000, + }, + [ID_AD5392_3] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 8, + .int_vref = 1250000, + }, + [ID_AD5392_5] = { + .channel_template = AD5380_CHANNEL(14), + .num_channels = 8, + .int_vref = 2500000, + }, +}; + +static int __devinit ad5380_alloc_channels(struct iio_dev *indio_dev) +{ + struct ad5380_state *st = iio_priv(indio_dev); + struct iio_chan_spec *channels; + unsigned int i; + + channels = kcalloc(st->chip_info->num_channels, + sizeof(struct iio_chan_spec), GFP_KERNEL); + + if (!channels) + return -ENOMEM; + + for (i = 0; i < st->chip_info->num_channels; ++i) { + channels[i] = st->chip_info->channel_template; + channels[i].channel = i; + channels[i].address = i; + } + + indio_dev->channels = channels; + + return 0; +} + +static int __devinit ad5380_probe(struct device *dev, struct regmap *regmap, + enum ad5380_type type, const char *name) +{ + struct iio_dev *indio_dev; + struct ad5380_state *st; + unsigned int ctrl = 0; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + dev_err(dev, "Failed to allocate iio device\n"); + ret = -ENOMEM; + goto error_regmap_exit; + } + + st = iio_priv(indio_dev); + dev_set_drvdata(dev, indio_dev); + + st->chip_info = &ad5380_chip_info_tbl[type]; + st->regmap = regmap; + + indio_dev->dev.parent = dev; + indio_dev->name = name; + indio_dev->info = &ad5380_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = st->chip_info->num_channels; + + ret = ad5380_alloc_channels(indio_dev); + if (ret) { + dev_err(dev, "Failed to allocate channel spec: %d\n", ret); + goto error_free; + } + + if (st->chip_info->int_vref == 2500000) + ctrl |= AD5380_CTRL_INT_VREF_2V5; + + st->vref_reg = regulator_get(dev, "vref"); + if (!IS_ERR(st->vref_reg)) { + ret = regulator_enable(st->vref_reg); + if (ret) { + dev_err(dev, "Failed to enable vref regulators: %d\n", + ret); + goto error_free_reg; + } + + st->vref = regulator_get_voltage(st->vref_reg); + } else { + st->vref = st->chip_info->int_vref; + ctrl |= AD5380_CTRL_INT_VREF_EN; + } + + ret = regmap_write(st->regmap, AD5380_REG_SF_CTRL, ctrl); + if (ret) { + dev_err(dev, "Failed to write to device: %d\n", ret); + goto error_disable_reg; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "Failed to register iio device: %d\n", ret); + goto error_disable_reg; + } + + return 0; + +error_disable_reg: + if (!IS_ERR(st->vref_reg)) + regulator_disable(st->vref_reg); +error_free_reg: + if (!IS_ERR(st->vref_reg)) + regulator_put(st->vref_reg); + + kfree(indio_dev->channels); +error_free: + iio_device_free(indio_dev); +error_regmap_exit: + regmap_exit(regmap); + + return ret; +} + +static int __devexit ad5380_remove(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad5380_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + kfree(indio_dev->channels); + + if (!IS_ERR(st->vref_reg)) { + regulator_disable(st->vref_reg); + regulator_put(st->vref_reg); + } + + regmap_exit(st->regmap); + iio_device_free(indio_dev); + + return 0; +} + +static bool ad5380_reg_false(struct device *dev, unsigned int reg) +{ + return false; +} + +static const struct regmap_config ad5380_regmap_config = { + .reg_bits = 10, + .val_bits = 14, + + .max_register = AD5380_REG_DATA(40), + .cache_type = REGCACHE_RBTREE, + + .volatile_reg = ad5380_reg_false, + .readable_reg = ad5380_reg_false, +}; + +#if IS_ENABLED(CONFIG_SPI_MASTER) + +static int __devinit ad5380_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap *regmap; + + regmap = regmap_init_spi(spi, &ad5380_regmap_config); + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return ad5380_probe(&spi->dev, regmap, id->driver_data, id->name); +} + +static int __devexit ad5380_spi_remove(struct spi_device *spi) +{ + return ad5380_remove(&spi->dev); +} + +static const struct spi_device_id ad5380_spi_ids[] = { + { "ad5380-3", ID_AD5380_3 }, + { "ad5380-5", ID_AD5380_5 }, + { "ad5381-3", ID_AD5381_3 }, + { "ad5381-5", ID_AD5381_5 }, + { "ad5382-3", ID_AD5382_3 }, + { "ad5382-5", ID_AD5382_5 }, + { "ad5383-3", ID_AD5383_3 }, + { "ad5383-5", ID_AD5383_5 }, + { "ad5384-3", ID_AD5380_3 }, + { "ad5384-5", ID_AD5380_5 }, + { "ad5390-3", ID_AD5390_3 }, + { "ad5390-5", ID_AD5390_5 }, + { "ad5391-3", ID_AD5391_3 }, + { "ad5391-5", ID_AD5391_5 }, + { "ad5392-3", ID_AD5392_3 }, + { "ad5392-5", ID_AD5392_5 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad5380_spi_ids); + +static struct spi_driver ad5380_spi_driver = { + .driver = { + .name = "ad5380", + .owner = THIS_MODULE, + }, + .probe = ad5380_spi_probe, + .remove = __devexit_p(ad5380_spi_remove), + .id_table = ad5380_spi_ids, +}; + +static inline int ad5380_spi_register_driver(void) +{ + return spi_register_driver(&ad5380_spi_driver); +} + +static inline void ad5380_spi_unregister_driver(void) +{ + spi_unregister_driver(&ad5380_spi_driver); +} + +#else + +static inline int ad5380_spi_register_driver(void) +{ + return 0; +} + +static inline void ad5380_spi_unregister_driver(void) +{ +} + +#endif + +#if IS_ENABLED(CONFIG_I2C) + +static int __devinit ad5380_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + + regmap = regmap_init_i2c(i2c, &ad5380_regmap_config); + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return ad5380_probe(&i2c->dev, regmap, id->driver_data, id->name); +} + +static int __devexit ad5380_i2c_remove(struct i2c_client *i2c) +{ + return ad5380_remove(&i2c->dev); +} + +static const struct i2c_device_id ad5380_i2c_ids[] = { + { "ad5380-3", ID_AD5380_3 }, + { "ad5380-5", ID_AD5380_5 }, + { "ad5381-3", ID_AD5381_3 }, + { "ad5381-5", ID_AD5381_5 }, + { "ad5382-3", ID_AD5382_3 }, + { "ad5382-5", ID_AD5382_5 }, + { "ad5383-3", ID_AD5383_3 }, + { "ad5383-5", ID_AD5383_5 }, + { "ad5384-3", ID_AD5380_3 }, + { "ad5384-5", ID_AD5380_5 }, + { "ad5390-3", ID_AD5390_3 }, + { "ad5390-5", ID_AD5390_5 }, + { "ad5391-3", ID_AD5391_3 }, + { "ad5391-5", ID_AD5391_5 }, + { "ad5392-3", ID_AD5392_3 }, + { "ad5392-5", ID_AD5392_5 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad5380_i2c_ids); + +static struct i2c_driver ad5380_i2c_driver = { + .driver = { + .name = "ad5380", + .owner = THIS_MODULE, + }, + .probe = ad5380_i2c_probe, + .remove = __devexit_p(ad5380_i2c_remove), + .id_table = ad5380_i2c_ids, +}; + +static inline int ad5380_i2c_register_driver(void) +{ + return i2c_add_driver(&ad5380_i2c_driver); +} + +static inline void ad5380_i2c_unregister_driver(void) +{ + i2c_del_driver(&ad5380_i2c_driver); +} + +#else + +static inline int ad5380_i2c_register_driver(void) +{ + return 0; +} + +static inline void ad5380_i2c_unregister_driver(void) +{ +} + +#endif + +static int __init ad5380_spi_init(void) +{ + int ret; + + ret = ad5380_spi_register_driver(); + if (ret) + return ret; + + ret = ad5380_i2c_register_driver(); + if (ret) { + ad5380_spi_unregister_driver(); + return ret; + } + + return 0; +} +module_init(ad5380_spi_init); + +static void __exit ad5380_spi_exit(void) +{ + ad5380_i2c_unregister_driver(); + ad5380_spi_unregister_driver(); + +} +module_exit(ad5380_spi_exit); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD5380/81/82/83/84/90/91/92 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5421.c b/drivers/iio/dac/ad5421.c new file mode 100644 index 000000000000..cdbc5bf25c31 --- /dev/null +++ b/drivers/iio/dac/ad5421.c @@ -0,0 +1,544 @@ +/* + * AD5421 Digital to analog converters driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#define AD5421_REG_DAC_DATA 0x1 +#define AD5421_REG_CTRL 0x2 +#define AD5421_REG_OFFSET 0x3 +#define AD5421_REG_GAIN 0x4 +/* load dac and fault shared the same register number. Writing to it will cause + * a dac load command, reading from it will return the fault status register */ +#define AD5421_REG_LOAD_DAC 0x5 +#define AD5421_REG_FAULT 0x5 +#define AD5421_REG_FORCE_ALARM_CURRENT 0x6 +#define AD5421_REG_RESET 0x7 +#define AD5421_REG_START_CONVERSION 0x8 +#define AD5421_REG_NOOP 0x9 + +#define AD5421_CTRL_WATCHDOG_DISABLE BIT(12) +#define AD5421_CTRL_AUTO_FAULT_READBACK BIT(11) +#define AD5421_CTRL_MIN_CURRENT BIT(9) +#define AD5421_CTRL_ADC_SOURCE_TEMP BIT(8) +#define AD5421_CTRL_ADC_ENABLE BIT(7) +#define AD5421_CTRL_PWR_DOWN_INT_VREF BIT(6) + +#define AD5421_FAULT_SPI BIT(15) +#define AD5421_FAULT_PEC BIT(14) +#define AD5421_FAULT_OVER_CURRENT BIT(13) +#define AD5421_FAULT_UNDER_CURRENT BIT(12) +#define AD5421_FAULT_TEMP_OVER_140 BIT(11) +#define AD5421_FAULT_TEMP_OVER_100 BIT(10) +#define AD5421_FAULT_UNDER_VOLTAGE_6V BIT(9) +#define AD5421_FAULT_UNDER_VOLTAGE_12V BIT(8) + +/* These bits will cause the fault pin to go high */ +#define AD5421_FAULT_TRIGGER_IRQ \ + (AD5421_FAULT_SPI | AD5421_FAULT_PEC | AD5421_FAULT_OVER_CURRENT | \ + AD5421_FAULT_UNDER_CURRENT | AD5421_FAULT_TEMP_OVER_140) + +/** + * struct ad5421_state - driver instance specific data + * @spi: spi_device + * @ctrl: control register cache + * @current_range: current range which the device is configured for + * @data: spi transfer buffers + * @fault_mask: software masking of events + */ +struct ad5421_state { + struct spi_device *spi; + unsigned int ctrl; + enum ad5421_current_range current_range; + unsigned int fault_mask; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + union { + u32 d32; + u8 d8[4]; + } data[2] ____cacheline_aligned; +}; + +static const struct iio_chan_spec ad5421_channels[] = { + { + .type = IIO_CURRENT, + .indexed = 1, + .output = 1, + .channel = 0, + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT | + IIO_CHAN_INFO_OFFSET_SHARED_BIT | + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, + .scan_type = IIO_ST('u', 16, 16, 0), + .event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | + IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + }, + { + .type = IIO_TEMP, + .channel = -1, + .event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + }, +}; + +static int ad5421_write_unlocked(struct iio_dev *indio_dev, + unsigned int reg, unsigned int val) +{ + struct ad5421_state *st = iio_priv(indio_dev); + + st->data[0].d32 = cpu_to_be32((reg << 16) | val); + + return spi_write(st->spi, &st->data[0].d8[1], 3); +} + +static int ad5421_write(struct iio_dev *indio_dev, unsigned int reg, + unsigned int val) +{ + int ret; + + mutex_lock(&indio_dev->mlock); + ret = ad5421_write_unlocked(indio_dev, reg, val); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5421_read(struct iio_dev *indio_dev, unsigned int reg) +{ + struct ad5421_state *st = iio_priv(indio_dev); + struct spi_message m; + int ret; + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[1], + .len = 3, + .cs_change = 1, + }, { + .rx_buf = &st->data[1].d8[1], + .len = 3, + }, + }; + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + mutex_lock(&indio_dev->mlock); + + st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16)); + + ret = spi_sync(st->spi, &m); + if (ret >= 0) + ret = be32_to_cpu(st->data[1].d32) & 0xffff; + + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5421_update_ctrl(struct iio_dev *indio_dev, unsigned int set, + unsigned int clr) +{ + struct ad5421_state *st = iio_priv(indio_dev); + unsigned int ret; + + mutex_lock(&indio_dev->mlock); + + st->ctrl &= ~clr; + st->ctrl |= set; + + ret = ad5421_write_unlocked(indio_dev, AD5421_REG_CTRL, st->ctrl); + + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static irqreturn_t ad5421_fault_handler(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct ad5421_state *st = iio_priv(indio_dev); + unsigned int fault; + unsigned int old_fault = 0; + unsigned int events; + + fault = ad5421_read(indio_dev, AD5421_REG_FAULT); + if (!fault) + return IRQ_NONE; + + /* If we had a fault, this might mean that the DAC has lost its state + * and has been reset. Make sure that the control register actually + * contains what we expect it to contain. Otherwise the watchdog might + * be enabled and we get watchdog timeout faults, which will render the + * DAC unusable. */ + ad5421_update_ctrl(indio_dev, 0, 0); + + + /* The fault pin stays high as long as a fault condition is present and + * it is not possible to mask fault conditions. For certain fault + * conditions for example like over-temperature it takes some time + * until the fault condition disappears. If we would exit the interrupt + * handler immediately after handling the event it would be entered + * again instantly. Thus we fall back to polling in case we detect that + * a interrupt condition is still present. + */ + do { + /* 0xffff is a invalid value for the register and will only be + * read if there has been a communication error */ + if (fault == 0xffff) + fault = 0; + + /* we are only interested in new events */ + events = (old_fault ^ fault) & fault; + events &= st->fault_mask; + + if (events & AD5421_FAULT_OVER_CURRENT) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_CURRENT, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns()); + } + + if (events & AD5421_FAULT_UNDER_CURRENT) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_CURRENT, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns()); + } + + if (events & AD5421_FAULT_TEMP_OVER_140) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, + 0, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_RISING), + iio_get_time_ns()); + } + + old_fault = fault; + fault = ad5421_read(indio_dev, AD5421_REG_FAULT); + + /* still active? go to sleep for some time */ + if (fault & AD5421_FAULT_TRIGGER_IRQ) + msleep(1000); + + } while (fault & AD5421_FAULT_TRIGGER_IRQ); + + + return IRQ_HANDLED; +} + +static void ad5421_get_current_min_max(struct ad5421_state *st, + unsigned int *min, unsigned int *max) +{ + /* The current range is configured using external pins, which are + * usually hard-wired and not run-time switchable. */ + switch (st->current_range) { + case AD5421_CURRENT_RANGE_4mA_20mA: + *min = 4000; + *max = 20000; + break; + case AD5421_CURRENT_RANGE_3mA8_21mA: + *min = 3800; + *max = 21000; + break; + case AD5421_CURRENT_RANGE_3mA2_24mA: + *min = 3200; + *max = 24000; + break; + default: + *min = 0; + *max = 1; + break; + } +} + +static inline unsigned int ad5421_get_offset(struct ad5421_state *st) +{ + unsigned int min, max; + + ad5421_get_current_min_max(st, &min, &max); + return (min * (1 << 16)) / (max - min); +} + +static inline unsigned int ad5421_get_scale(struct ad5421_state *st) +{ + unsigned int min, max; + + ad5421_get_current_min_max(st, &min, &max); + return ((max - min) * 1000) / (1 << 16); +} + +static int ad5421_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long m) +{ + struct ad5421_state *st = iio_priv(indio_dev); + int ret; + + if (chan->type != IIO_CURRENT) + return -EINVAL; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = ad5421_get_scale(st); + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + *val = ad5421_get_offset(st); + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + ret = ad5421_read(indio_dev, AD5421_REG_OFFSET); + if (ret < 0) + return ret; + *val = ret - 32768; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + ret = ad5421_read(indio_dev, AD5421_REG_GAIN); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int ad5421_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + const unsigned int max_val = 1 << 16; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val >= max_val || val < 0) + return -EINVAL; + + return ad5421_write(indio_dev, AD5421_REG_DAC_DATA, val); + case IIO_CHAN_INFO_CALIBBIAS: + val += 32768; + if (val >= max_val || val < 0) + return -EINVAL; + + return ad5421_write(indio_dev, AD5421_REG_OFFSET, val); + case IIO_CHAN_INFO_CALIBSCALE: + if (val >= max_val || val < 0) + return -EINVAL; + + return ad5421_write(indio_dev, AD5421_REG_GAIN, val); + default: + break; + } + + return -EINVAL; +} + +static int ad5421_write_event_config(struct iio_dev *indio_dev, + u64 event_code, int state) +{ + struct ad5421_state *st = iio_priv(indio_dev); + unsigned int mask; + + switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { + case IIO_CURRENT: + if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == + IIO_EV_DIR_RISING) + mask = AD5421_FAULT_OVER_CURRENT; + else + mask = AD5421_FAULT_UNDER_CURRENT; + break; + case IIO_TEMP: + mask = AD5421_FAULT_TEMP_OVER_140; + break; + default: + return -EINVAL; + } + + mutex_lock(&indio_dev->mlock); + if (state) + st->fault_mask |= mask; + else + st->fault_mask &= ~mask; + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static int ad5421_read_event_config(struct iio_dev *indio_dev, + u64 event_code) +{ + struct ad5421_state *st = iio_priv(indio_dev); + unsigned int mask; + + switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { + case IIO_CURRENT: + if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == + IIO_EV_DIR_RISING) + mask = AD5421_FAULT_OVER_CURRENT; + else + mask = AD5421_FAULT_UNDER_CURRENT; + break; + case IIO_TEMP: + mask = AD5421_FAULT_TEMP_OVER_140; + break; + default: + return -EINVAL; + } + + return (bool)(st->fault_mask & mask); +} + +static int ad5421_read_event_value(struct iio_dev *indio_dev, u64 event_code, + int *val) +{ + int ret; + + switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { + case IIO_CURRENT: + ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA); + if (ret < 0) + return ret; + *val = ret; + break; + case IIO_TEMP: + *val = 140000; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct iio_info ad5421_info = { + .read_raw = ad5421_read_raw, + .write_raw = ad5421_write_raw, + .read_event_config = ad5421_read_event_config, + .write_event_config = ad5421_write_event_config, + .read_event_value = ad5421_read_event_value, + .driver_module = THIS_MODULE, +}; + +static int __devinit ad5421_probe(struct spi_device *spi) +{ + struct ad5421_platform_data *pdata = dev_get_platdata(&spi->dev); + struct iio_dev *indio_dev; + struct ad5421_state *st; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + dev_err(&spi->dev, "Failed to allocate iio device\n"); + return -ENOMEM; + } + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = "ad5421"; + indio_dev->info = &ad5421_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad5421_channels; + indio_dev->num_channels = ARRAY_SIZE(ad5421_channels); + + st->ctrl = AD5421_CTRL_WATCHDOG_DISABLE | + AD5421_CTRL_AUTO_FAULT_READBACK; + + if (pdata) { + st->current_range = pdata->current_range; + if (pdata->external_vref) + st->ctrl |= AD5421_CTRL_PWR_DOWN_INT_VREF; + } else { + st->current_range = AD5421_CURRENT_RANGE_4mA_20mA; + } + + /* write initial ctrl register value */ + ad5421_update_ctrl(indio_dev, 0, 0); + + if (spi->irq) { + ret = request_threaded_irq(spi->irq, + NULL, + ad5421_fault_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "ad5421 fault", + indio_dev); + if (ret) + goto error_free; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); + goto error_free_irq; + } + + return 0; + +error_free_irq: + if (spi->irq) + free_irq(spi->irq, indio_dev); +error_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad5421_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + + iio_device_unregister(indio_dev); + if (spi->irq) + free_irq(spi->irq, indio_dev); + iio_device_free(indio_dev); + + return 0; +} + +static struct spi_driver ad5421_driver = { + .driver = { + .name = "ad5421", + .owner = THIS_MODULE, + }, + .probe = ad5421_probe, + .remove = __devexit_p(ad5421_remove), +}; +module_spi_driver(ad5421_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD5421 DAC"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:ad5421"); diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c new file mode 100644 index 000000000000..49f557fc673b --- /dev/null +++ b/drivers/iio/dac/ad5446.c @@ -0,0 +1,381 @@ +/* + * AD5446 SPI DAC driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ad5446.h" + +static int ad5446_write(struct ad5446_state *st, unsigned val) +{ + __be16 data = cpu_to_be16(val); + return spi_write(st->spi, &data, sizeof(data)); +} + +static int ad5660_write(struct ad5446_state *st, unsigned val) +{ + uint8_t data[3]; + + data[0] = (val >> 16) & 0xFF; + data[1] = (val >> 8) & 0xFF; + data[2] = val & 0xFF; + + return spi_write(st->spi, data, sizeof(data)); +} + +static const char * const ad5446_powerdown_modes[] = { + "1kohm_to_gnd", "100kohm_to_gnd", "three_state" +}; + +static int ad5446_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct ad5446_state *st = iio_priv(indio_dev); + + st->pwr_down_mode = mode + 1; + + return 0; +} + +static int ad5446_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5446_state *st = iio_priv(indio_dev); + + return st->pwr_down_mode - 1; +} + +static const struct iio_enum ad5446_powerdown_mode_enum = { + .items = ad5446_powerdown_modes, + .num_items = ARRAY_SIZE(ad5446_powerdown_modes), + .get = ad5446_get_powerdown_mode, + .set = ad5446_set_powerdown_mode, +}; + +static ssize_t ad5446_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad5446_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->pwr_down); +} + +static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad5446_state *st = iio_priv(indio_dev); + unsigned int shift; + unsigned int val; + bool powerdown; + int ret; + + ret = strtobool(buf, &powerdown); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + st->pwr_down = powerdown; + + if (st->pwr_down) { + shift = chan->scan_type.realbits + chan->scan_type.shift; + val = st->pwr_down_mode << shift; + } else { + val = st->cached_val; + } + + ret = st->chip_info->write(st, val); + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static const struct iio_chan_spec_ext_info ad5064_ext_info_powerdown[] = { + { + .name = "powerdown", + .read = ad5446_read_dac_powerdown, + .write = ad5446_write_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", false, &ad5446_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", &ad5446_powerdown_mode_enum), + { }, +}; + +#define _AD5446_CHANNEL(bits, storage, shift, ext) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = 0, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT, \ + .scan_type = IIO_ST('u', (bits), (storage), (shift)), \ + .ext_info = (ext), \ +} + +#define AD5446_CHANNEL(bits, storage, shift) \ + _AD5446_CHANNEL(bits, storage, shift, NULL) + +#define AD5446_CHANNEL_POWERDOWN(bits, storage, shift) \ + _AD5446_CHANNEL(bits, storage, shift, ad5064_ext_info_powerdown) + +static const struct ad5446_chip_info ad5446_chip_info_tbl[] = { + [ID_AD5444] = { + .channel = AD5446_CHANNEL(12, 16, 2), + .write = ad5446_write, + }, + [ID_AD5446] = { + .channel = AD5446_CHANNEL(14, 16, 0), + .write = ad5446_write, + }, + [ID_AD5541A] = { + .channel = AD5446_CHANNEL(16, 16, 0), + .write = ad5446_write, + }, + [ID_AD5512A] = { + .channel = AD5446_CHANNEL(12, 16, 4), + .write = ad5446_write, + }, + [ID_AD5553] = { + .channel = AD5446_CHANNEL(14, 16, 0), + .write = ad5446_write, + }, + [ID_AD5601] = { + .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6), + .write = ad5446_write, + }, + [ID_AD5611] = { + .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 4), + .write = ad5446_write, + }, + [ID_AD5621] = { + .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), + .write = ad5446_write, + }, + [ID_AD5620_2500] = { + .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), + .int_vref_mv = 2500, + .write = ad5446_write, + }, + [ID_AD5620_1250] = { + .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), + .int_vref_mv = 1250, + .write = ad5446_write, + }, + [ID_AD5640_2500] = { + .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0), + .int_vref_mv = 2500, + .write = ad5446_write, + }, + [ID_AD5640_1250] = { + .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0), + .int_vref_mv = 1250, + .write = ad5446_write, + }, + [ID_AD5660_2500] = { + .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), + .int_vref_mv = 2500, + .write = ad5660_write, + }, + [ID_AD5660_1250] = { + .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), + .int_vref_mv = 1250, + .write = ad5660_write, + }, + [ID_AD5662] = { + .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), + .write = ad5660_write, + }, +}; + +static int ad5446_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad5446_state *st = iio_priv(indio_dev); + unsigned long scale_uv; + + switch (m) { + case IIO_CHAN_INFO_RAW: + *val = st->cached_val; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; + *val = scale_uv / 1000; + *val2 = (scale_uv % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + + } + return -EINVAL; +} + +static int ad5446_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad5446_state *st = iio_priv(indio_dev); + int ret = 0; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val >= (1 << chan->scan_type.realbits) || val < 0) + return -EINVAL; + + val <<= chan->scan_type.shift; + mutex_lock(&indio_dev->mlock); + st->cached_val = val; + if (!st->pwr_down) + ret = st->chip_info->write(st, val); + mutex_unlock(&indio_dev->mlock); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct iio_info ad5446_info = { + .read_raw = ad5446_read_raw, + .write_raw = ad5446_write_raw, + .driver_module = THIS_MODULE, +}; + +static int __devinit ad5446_probe(struct spi_device *spi) +{ + struct ad5446_state *st; + struct iio_dev *indio_dev; + struct regulator *reg; + int ret, voltage_uv = 0; + + reg = regulator_get(&spi->dev, "vcc"); + if (!IS_ERR(reg)) { + ret = regulator_enable(reg); + if (ret) + goto error_put_reg; + + voltage_uv = regulator_get_voltage(reg); + } + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_disable_reg; + } + st = iio_priv(indio_dev); + st->chip_info = + &ad5446_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + spi_set_drvdata(spi, indio_dev); + st->reg = reg; + st->spi = spi; + + /* Establish that the iio_dev is a child of the spi device */ + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad5446_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &st->chip_info->channel; + indio_dev->num_channels = 1; + + st->pwr_down_mode = MODE_PWRDWN_1k; + + if (st->chip_info->int_vref_mv) + st->vref_mv = st->chip_info->int_vref_mv; + else if (voltage_uv) + st->vref_mv = voltage_uv / 1000; + else + dev_warn(&spi->dev, "reference voltage unspecified\n"); + + ret = iio_device_register(indio_dev); + if (ret) + goto error_free_device; + + return 0; + +error_free_device: + iio_device_free(indio_dev); +error_disable_reg: + if (!IS_ERR(reg)) + regulator_disable(reg); +error_put_reg: + if (!IS_ERR(reg)) + regulator_put(reg); + + return ret; +} + +static int ad5446_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5446_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (!IS_ERR(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5446_id[] = { + {"ad5444", ID_AD5444}, + {"ad5446", ID_AD5446}, + {"ad5512a", ID_AD5512A}, + {"ad5541a", ID_AD5541A}, + {"ad5542a", ID_AD5541A}, /* ad5541a and ad5542a are compatible */ + {"ad5543", ID_AD5541A}, /* ad5541a and ad5543 are compatible */ + {"ad5553", ID_AD5553}, + {"ad5601", ID_AD5601}, + {"ad5611", ID_AD5611}, + {"ad5621", ID_AD5621}, + {"ad5620-2500", ID_AD5620_2500}, /* AD5620/40/60: */ + {"ad5620-1250", ID_AD5620_1250}, /* part numbers may look differently */ + {"ad5640-2500", ID_AD5640_2500}, + {"ad5640-1250", ID_AD5640_1250}, + {"ad5660-2500", ID_AD5660_2500}, + {"ad5660-1250", ID_AD5660_1250}, + {"ad5662", ID_AD5662}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5446_id); + +static struct spi_driver ad5446_driver = { + .driver = { + .name = "ad5446", + .owner = THIS_MODULE, + }, + .probe = ad5446_probe, + .remove = __devexit_p(ad5446_remove), + .id_table = ad5446_id, +}; +module_spi_driver(ad5446_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5446.h b/drivers/iio/dac/ad5446.h new file mode 100644 index 000000000000..dfd68ce7427e --- /dev/null +++ b/drivers/iio/dac/ad5446.h @@ -0,0 +1,89 @@ +/* + * AD5446 SPI DAC driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ +#ifndef IIO_DAC_AD5446_H_ +#define IIO_DAC_AD5446_H_ + +/* DAC Control Bits */ + +#define AD5446_LOAD (0x0 << 14) /* Load and update */ +#define AD5446_SDO_DIS (0x1 << 14) /* Disable SDO */ +#define AD5446_NOP (0x2 << 14) /* No operation */ +#define AD5446_CLK_RISING (0x3 << 14) /* Clock data on rising edge */ + +#define AD5620_LOAD (0x0 << 14) /* Load and update Norm Operation*/ +#define AD5620_PWRDWN_1k (0x1 << 14) /* Power-down: 1kOhm to GND */ +#define AD5620_PWRDWN_100k (0x2 << 14) /* Power-down: 100kOhm to GND */ +#define AD5620_PWRDWN_TRISTATE (0x3 << 14) /* Power-down: Three-state */ + +#define AD5660_LOAD (0x0 << 16) /* Load and update Norm Operation*/ +#define AD5660_PWRDWN_1k (0x1 << 16) /* Power-down: 1kOhm to GND */ +#define AD5660_PWRDWN_100k (0x2 << 16) /* Power-down: 100kOhm to GND */ +#define AD5660_PWRDWN_TRISTATE (0x3 << 16) /* Power-down: Three-state */ + +#define MODE_PWRDWN_1k 0x1 +#define MODE_PWRDWN_100k 0x2 +#define MODE_PWRDWN_TRISTATE 0x3 + +/** + * struct ad5446_state - driver instance specific data + * @spi: spi_device + * @chip_info: chip model specific constants, available modes etc + * @reg: supply regulator + * @vref_mv: actual reference voltage used + */ + +struct ad5446_state { + struct spi_device *spi; + const struct ad5446_chip_info *chip_info; + struct regulator *reg; + unsigned short vref_mv; + unsigned cached_val; + unsigned pwr_down_mode; + unsigned pwr_down; +}; + +/** + * struct ad5446_chip_info - chip specific information + * @channel: channel spec for the DAC + * @int_vref_mv: AD5620/40/60: the internal reference voltage + * @write: chip specific helper function to write to the register + */ + +struct ad5446_chip_info { + struct iio_chan_spec channel; + u16 int_vref_mv; + int (*write)(struct ad5446_state *st, unsigned val); +}; + +/** + * ad5446_supported_device_ids: + * The AD5620/40/60 parts are available in different fixed internal reference + * voltage options. The actual part numbers may look differently + * (and a bit cryptic), however this style is used to make clear which + * parts are supported here. + */ + +enum ad5446_supported_device_ids { + ID_AD5444, + ID_AD5446, + ID_AD5541A, + ID_AD5512A, + ID_AD5553, + ID_AD5601, + ID_AD5611, + ID_AD5621, + ID_AD5620_2500, + ID_AD5620_1250, + ID_AD5640_2500, + ID_AD5640_1250, + ID_AD5660_2500, + ID_AD5660_1250, + ID_AD5662, +}; + +#endif /* IIO_DAC_AD5446_H_ */ diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c new file mode 100644 index 000000000000..242bdc7d0044 --- /dev/null +++ b/drivers/iio/dac/ad5504.c @@ -0,0 +1,393 @@ +/* + * AD5504, AD5501 High Voltage Digital to Analog Converter + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define AD5505_BITS 12 +#define AD5504_RES_MASK ((1 << (AD5505_BITS)) - 1) + +#define AD5504_CMD_READ (1 << 15) +#define AD5504_CMD_WRITE (0 << 15) +#define AD5504_ADDR(addr) ((addr) << 12) + +/* Registers */ +#define AD5504_ADDR_NOOP 0 +#define AD5504_ADDR_DAC(x) ((x) + 1) +#define AD5504_ADDR_ALL_DAC 5 +#define AD5504_ADDR_CTRL 7 + +/* Control Register */ +#define AD5504_DAC_PWR(ch) ((ch) << 2) +#define AD5504_DAC_PWRDWN_MODE(mode) ((mode) << 6) +#define AD5504_DAC_PWRDN_20K 0 +#define AD5504_DAC_PWRDN_3STATE 1 + +/** + * struct ad5446_state - driver instance specific data + * @us: spi_device + * @reg: supply regulator + * @vref_mv: actual reference voltage used + * @pwr_down_mask power down mask + * @pwr_down_mode current power down mode + */ + +struct ad5504_state { + struct spi_device *spi; + struct regulator *reg; + unsigned short vref_mv; + unsigned pwr_down_mask; + unsigned pwr_down_mode; +}; + +/** + * ad5504_supported_device_ids: + */ + +enum ad5504_supported_device_ids { + ID_AD5504, + ID_AD5501, +}; + +static int ad5504_spi_write(struct spi_device *spi, u8 addr, u16 val) +{ + u16 tmp = cpu_to_be16(AD5504_CMD_WRITE | + AD5504_ADDR(addr) | + (val & AD5504_RES_MASK)); + + return spi_write(spi, (u8 *)&tmp, 2); +} + +static int ad5504_spi_read(struct spi_device *spi, u8 addr) +{ + u16 tmp = cpu_to_be16(AD5504_CMD_READ | AD5504_ADDR(addr)); + u16 val; + int ret; + struct spi_transfer t = { + .tx_buf = &tmp, + .rx_buf = &val, + .len = 2, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + ret = spi_sync(spi, &m); + + if (ret < 0) + return ret; + + return be16_to_cpu(val) & AD5504_RES_MASK; +} + +static int ad5504_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad5504_state *st = iio_priv(indio_dev); + unsigned long scale_uv; + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ad5504_spi_read(st->spi, chan->address); + if (ret < 0) + return ret; + + *val = ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; + *val = scale_uv / 1000; + *val2 = (scale_uv % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + + } + return -EINVAL; +} + +static int ad5504_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad5504_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val >= (1 << chan->scan_type.realbits) || val < 0) + return -EINVAL; + + return ad5504_spi_write(st->spi, chan->address, val); + default: + ret = -EINVAL; + } + + return -EINVAL; +} + +static const char * const ad5504_powerdown_modes[] = { + "20kohm_to_gnd", + "three_state", +}; + +static int ad5504_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5504_state *st = iio_priv(indio_dev); + + return st->pwr_down_mode; +} + +static int ad5504_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct ad5504_state *st = iio_priv(indio_dev); + + st->pwr_down_mode = mode; + + return 0; +} + +static const struct iio_enum ad5504_powerdown_mode_enum = { + .items = ad5504_powerdown_modes, + .num_items = ARRAY_SIZE(ad5504_powerdown_modes), + .get = ad5504_get_powerdown_mode, + .set = ad5504_set_powerdown_mode, +}; + +static ssize_t ad5504_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct ad5504_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", + !(st->pwr_down_mask & (1 << chan->channel))); +} + +static ssize_t ad5504_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + bool pwr_down; + int ret; + struct ad5504_state *st = iio_priv(indio_dev); + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + if (pwr_down) + st->pwr_down_mask |= (1 << chan->channel); + else + st->pwr_down_mask &= ~(1 << chan->channel); + + ret = ad5504_spi_write(st->spi, AD5504_ADDR_CTRL, + AD5504_DAC_PWRDWN_MODE(st->pwr_down_mode) | + AD5504_DAC_PWR(st->pwr_down_mask)); + + /* writes to the CTRL register must be followed by a NOOP */ + ad5504_spi_write(st->spi, AD5504_ADDR_NOOP, 0); + + return ret ? ret : len; +} + +static IIO_CONST_ATTR(temp0_thresh_rising_value, "110000"); +static IIO_CONST_ATTR(temp0_thresh_rising_en, "1"); + +static struct attribute *ad5504_ev_attributes[] = { + &iio_const_attr_temp0_thresh_rising_value.dev_attr.attr, + &iio_const_attr_temp0_thresh_rising_en.dev_attr.attr, + NULL, +}; + +static struct attribute_group ad5504_ev_attribute_group = { + .attrs = ad5504_ev_attributes, + .name = "events", +}; + +static irqreturn_t ad5504_event_handler(int irq, void *private) +{ + iio_push_event(private, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns()); + + return IRQ_HANDLED; +} + +static const struct iio_info ad5504_info = { + .write_raw = ad5504_write_raw, + .read_raw = ad5504_read_raw, + .event_attrs = &ad5504_ev_attribute_group, + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec_ext_info ad5504_ext_info[] = { + { + .name = "powerdown", + .read = ad5504_read_dac_powerdown, + .write = ad5504_write_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", true, &ad5504_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", &ad5504_powerdown_mode_enum), + { }, +}; + +#define AD5504_CHANNEL(_chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (_chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT, \ + .address = AD5504_ADDR_DAC(_chan), \ + .scan_type = IIO_ST('u', 12, 16, 0), \ + .ext_info = ad5504_ext_info, \ +} + +static const struct iio_chan_spec ad5504_channels[] = { + AD5504_CHANNEL(0), + AD5504_CHANNEL(1), + AD5504_CHANNEL(2), + AD5504_CHANNEL(3), +}; + +static int __devinit ad5504_probe(struct spi_device *spi) +{ + struct ad5504_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev; + struct ad5504_state *st; + struct regulator *reg; + int ret, voltage_uv = 0; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + reg = regulator_get(&spi->dev, "vcc"); + if (!IS_ERR(reg)) { + ret = regulator_enable(reg); + if (ret) + goto error_put_reg; + + voltage_uv = regulator_get_voltage(reg); + } + + spi_set_drvdata(spi, indio_dev); + st = iio_priv(indio_dev); + if (voltage_uv) + st->vref_mv = voltage_uv / 1000; + else if (pdata) + st->vref_mv = pdata->vref_mv; + else + dev_warn(&spi->dev, "reference voltage unspecified\n"); + + st->reg = reg; + st->spi = spi; + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(st->spi)->name; + indio_dev->info = &ad5504_info; + if (spi_get_device_id(st->spi)->driver_data == ID_AD5501) + indio_dev->num_channels = 1; + else + indio_dev->num_channels = 4; + indio_dev->channels = ad5504_channels; + indio_dev->modes = INDIO_DIRECT_MODE; + + if (spi->irq) { + ret = request_threaded_irq(spi->irq, + NULL, + &ad5504_event_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + spi_get_device_id(st->spi)->name, + indio_dev); + if (ret) + goto error_disable_reg; + } + + ret = iio_device_register(indio_dev); + if (ret) + goto error_free_irq; + + return 0; + +error_free_irq: + if (spi->irq) + free_irq(spi->irq, indio_dev); +error_disable_reg: + if (!IS_ERR(reg)) + regulator_disable(reg); +error_put_reg: + if (!IS_ERR(reg)) + regulator_put(reg); + + iio_device_free(indio_dev); +error_ret: + return ret; +} + +static int __devexit ad5504_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5504_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (spi->irq) + free_irq(spi->irq, indio_dev); + + if (!IS_ERR(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5504_id[] = { + {"ad5504", ID_AD5504}, + {"ad5501", ID_AD5501}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5504_id); + +static struct spi_driver ad5504_driver = { + .driver = { + .name = "ad5504", + .owner = THIS_MODULE, + }, + .probe = ad5504_probe, + .remove = __devexit_p(ad5504_remove), + .id_table = ad5504_id, +}; +module_spi_driver(ad5504_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD5501/AD5501 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5624r.h b/drivers/iio/dac/ad5624r.h new file mode 100644 index 000000000000..5dca3028cdfd --- /dev/null +++ b/drivers/iio/dac/ad5624r.h @@ -0,0 +1,79 @@ +/* + * AD5624R SPI DAC driver + * + * Copyright 2010-2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ +#ifndef SPI_AD5624R_H_ +#define SPI_AD5624R_H_ + +#define AD5624R_DAC_CHANNELS 4 + +#define AD5624R_ADDR_DAC0 0x0 +#define AD5624R_ADDR_DAC1 0x1 +#define AD5624R_ADDR_DAC2 0x2 +#define AD5624R_ADDR_DAC3 0x3 +#define AD5624R_ADDR_ALL_DAC 0x7 + +#define AD5624R_CMD_WRITE_INPUT_N 0x0 +#define AD5624R_CMD_UPDATE_DAC_N 0x1 +#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2 +#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_N 0x3 +#define AD5624R_CMD_POWERDOWN_DAC 0x4 +#define AD5624R_CMD_RESET 0x5 +#define AD5624R_CMD_LDAC_SETUP 0x6 +#define AD5624R_CMD_INTERNAL_REFER_SETUP 0x7 + +#define AD5624R_LDAC_PWRDN_NONE 0x0 +#define AD5624R_LDAC_PWRDN_1K 0x1 +#define AD5624R_LDAC_PWRDN_100K 0x2 +#define AD5624R_LDAC_PWRDN_3STATE 0x3 + +/** + * struct ad5624r_chip_info - chip specific information + * @channels: channel spec for the DAC + * @int_vref_mv: AD5620/40/60: the internal reference voltage + */ + +struct ad5624r_chip_info { + const struct iio_chan_spec *channels; + u16 int_vref_mv; +}; + +/** + * struct ad5446_state - driver instance specific data + * @indio_dev: the industrial I/O device + * @us: spi_device + * @chip_info: chip model specific constants, available modes etc + * @reg: supply regulator + * @vref_mv: actual reference voltage used + * @pwr_down_mask power down mask + * @pwr_down_mode current power down mode + */ + +struct ad5624r_state { + struct spi_device *us; + const struct ad5624r_chip_info *chip_info; + struct regulator *reg; + unsigned short vref_mv; + unsigned pwr_down_mask; + unsigned pwr_down_mode; +}; + +/** + * ad5624r_supported_device_ids: + * The AD5624/44/64 parts are available in different + * fixed internal reference voltage options. + */ + +enum ad5624r_supported_device_ids { + ID_AD5624R3, + ID_AD5644R3, + ID_AD5664R3, + ID_AD5624R5, + ID_AD5644R5, + ID_AD5664R5, +}; + +#endif /* SPI_AD5624R_H_ */ diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c new file mode 100644 index 000000000000..6a7d6a48cc6d --- /dev/null +++ b/drivers/iio/dac/ad5624r_spi.c @@ -0,0 +1,324 @@ +/* + * AD5624R, AD5644R, AD5664R Digital to analog convertors spi driver + * + * Copyright 2010-2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ad5624r.h" + +static int ad5624r_spi_write(struct spi_device *spi, + u8 cmd, u8 addr, u16 val, u8 len) +{ + u32 data; + u8 msg[3]; + + /* + * The input shift register is 24 bits wide. The first two bits are + * don't care bits. The next three are the command bits, C2 to C0, + * followed by the 3-bit DAC address, A2 to A0, and then the + * 16-, 14-, 12-bit data-word. The data-word comprises the 16-, + * 14-, 12-bit input code followed by 0, 2, or 4 don't care bits, + * for the AD5664R, AD5644R, and AD5624R, respectively. + */ + data = (0 << 22) | (cmd << 19) | (addr << 16) | (val << (16 - len)); + msg[0] = data >> 16; + msg[1] = data >> 8; + msg[2] = data; + + return spi_write(spi, msg, 3); +} + +static int ad5624r_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad5624r_state *st = iio_priv(indio_dev); + unsigned long scale_uv; + + switch (m) { + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; + *val = scale_uv / 1000; + *val2 = (scale_uv % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + + } + return -EINVAL; +} + +static int ad5624r_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad5624r_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val >= (1 << chan->scan_type.realbits) || val < 0) + return -EINVAL; + + return ad5624r_spi_write(st->us, + AD5624R_CMD_WRITE_INPUT_N_UPDATE_N, + chan->address, val, + chan->scan_type.shift); + default: + ret = -EINVAL; + } + + return -EINVAL; +} + +static const char * const ad5624r_powerdown_modes[] = { + "1kohm_to_gnd", + "100kohm_to_gnd", + "three_state" +}; + +static int ad5624r_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5624r_state *st = iio_priv(indio_dev); + + return st->pwr_down_mode; +} + +static int ad5624r_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct ad5624r_state *st = iio_priv(indio_dev); + + st->pwr_down_mode = mode; + + return 0; +} + +static const struct iio_enum ad5624r_powerdown_mode_enum = { + .items = ad5624r_powerdown_modes, + .num_items = ARRAY_SIZE(ad5624r_powerdown_modes), + .get = ad5624r_get_powerdown_mode, + .set = ad5624r_set_powerdown_mode, +}; + +static ssize_t ad5624r_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct ad5624r_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", + !!(st->pwr_down_mask & (1 << chan->channel))); +} + +static ssize_t ad5624r_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + bool pwr_down; + int ret; + struct ad5624r_state *st = iio_priv(indio_dev); + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + if (pwr_down) + st->pwr_down_mask |= (1 << chan->channel); + else + st->pwr_down_mask &= ~(1 << chan->channel); + + ret = ad5624r_spi_write(st->us, AD5624R_CMD_POWERDOWN_DAC, 0, + (st->pwr_down_mode << 4) | + st->pwr_down_mask, 16); + + return ret ? ret : len; +} + +static const struct iio_info ad5624r_info = { + .write_raw = ad5624r_write_raw, + .read_raw = ad5624r_read_raw, + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = { + { + .name = "powerdown", + .read = ad5624r_read_dac_powerdown, + .write = ad5624r_write_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", true, &ad5624r_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", &ad5624r_powerdown_mode_enum), + { }, +}; + +#define AD5624R_CHANNEL(_chan, _bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (_chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT, \ + .address = (_chan), \ + .scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)), \ + .ext_info = ad5624r_ext_info, \ +} + +#define DECLARE_AD5624R_CHANNELS(_name, _bits) \ + const struct iio_chan_spec _name##_channels[] = { \ + AD5624R_CHANNEL(0, _bits), \ + AD5624R_CHANNEL(1, _bits), \ + AD5624R_CHANNEL(2, _bits), \ + AD5624R_CHANNEL(3, _bits), \ +} + +static DECLARE_AD5624R_CHANNELS(ad5624r, 12); +static DECLARE_AD5624R_CHANNELS(ad5644r, 14); +static DECLARE_AD5624R_CHANNELS(ad5664r, 16); + +static const struct ad5624r_chip_info ad5624r_chip_info_tbl[] = { + [ID_AD5624R3] = { + .channels = ad5624r_channels, + .int_vref_mv = 1250, + }, + [ID_AD5624R5] = { + .channels = ad5624r_channels, + .int_vref_mv = 2500, + }, + [ID_AD5644R3] = { + .channels = ad5644r_channels, + .int_vref_mv = 1250, + }, + [ID_AD5644R5] = { + .channels = ad5644r_channels, + .int_vref_mv = 2500, + }, + [ID_AD5664R3] = { + .channels = ad5664r_channels, + .int_vref_mv = 1250, + }, + [ID_AD5664R5] = { + .channels = ad5664r_channels, + .int_vref_mv = 2500, + }, +}; + +static int __devinit ad5624r_probe(struct spi_device *spi) +{ + struct ad5624r_state *st; + struct iio_dev *indio_dev; + int ret, voltage_uv = 0; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + st = iio_priv(indio_dev); + st->reg = regulator_get(&spi->dev, "vcc"); + if (!IS_ERR(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + + voltage_uv = regulator_get_voltage(st->reg); + } + + spi_set_drvdata(spi, indio_dev); + st->chip_info = + &ad5624r_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + if (voltage_uv) + st->vref_mv = voltage_uv / 1000; + else + st->vref_mv = st->chip_info->int_vref_mv; + + st->us = spi; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad5624r_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = AD5624R_DAC_CHANNELS; + + ret = ad5624r_spi_write(spi, AD5624R_CMD_INTERNAL_REFER_SETUP, 0, + !!voltage_uv, 16); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_disable_reg; + + return 0; + +error_disable_reg: + if (!IS_ERR(st->reg)) + regulator_disable(st->reg); +error_put_reg: + if (!IS_ERR(st->reg)) + regulator_put(st->reg); + iio_device_free(indio_dev); +error_ret: + + return ret; +} + +static int __devexit ad5624r_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5624r_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (!IS_ERR(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5624r_id[] = { + {"ad5624r3", ID_AD5624R3}, + {"ad5644r3", ID_AD5644R3}, + {"ad5664r3", ID_AD5664R3}, + {"ad5624r5", ID_AD5624R5}, + {"ad5644r5", ID_AD5644R5}, + {"ad5664r5", ID_AD5664R5}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5624r_id); + +static struct spi_driver ad5624r_driver = { + .driver = { + .name = "ad5624r", + .owner = THIS_MODULE, + }, + .probe = ad5624r_probe, + .remove = __devexit_p(ad5624r_remove), + .id_table = ad5624r_id, +}; +module_spi_driver(ad5624r_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices AD5624/44/64R DAC spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c new file mode 100644 index 000000000000..6948d75e1036 --- /dev/null +++ b/drivers/iio/dac/ad5686.c @@ -0,0 +1,418 @@ +/* + * AD5686R, AD5685R, AD5684R Digital to analog converters driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define AD5686_DAC_CHANNELS 4 + +#define AD5686_ADDR(x) ((x) << 16) +#define AD5686_CMD(x) ((x) << 20) + +#define AD5686_ADDR_DAC(chan) (0x1 << (chan)) +#define AD5686_ADDR_ALL_DAC 0xF + +#define AD5686_CMD_NOOP 0x0 +#define AD5686_CMD_WRITE_INPUT_N 0x1 +#define AD5686_CMD_UPDATE_DAC_N 0x2 +#define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3 +#define AD5686_CMD_POWERDOWN_DAC 0x4 +#define AD5686_CMD_LDAC_MASK 0x5 +#define AD5686_CMD_RESET 0x6 +#define AD5686_CMD_INTERNAL_REFER_SETUP 0x7 +#define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8 +#define AD5686_CMD_READBACK_ENABLE 0x9 + +#define AD5686_LDAC_PWRDN_NONE 0x0 +#define AD5686_LDAC_PWRDN_1K 0x1 +#define AD5686_LDAC_PWRDN_100K 0x2 +#define AD5686_LDAC_PWRDN_3STATE 0x3 + +/** + * struct ad5686_chip_info - chip specific information + * @int_vref_mv: AD5620/40/60: the internal reference voltage + * @channel: channel specification +*/ + +struct ad5686_chip_info { + u16 int_vref_mv; + struct iio_chan_spec channel[AD5686_DAC_CHANNELS]; +}; + +/** + * struct ad5446_state - driver instance specific data + * @spi: spi_device + * @chip_info: chip model specific constants, available modes etc + * @reg: supply regulator + * @vref_mv: actual reference voltage used + * @pwr_down_mask: power down mask + * @pwr_down_mode: current power down mode + * @data: spi transfer buffers + */ + +struct ad5686_state { + struct spi_device *spi; + const struct ad5686_chip_info *chip_info; + struct regulator *reg; + unsigned short vref_mv; + unsigned pwr_down_mask; + unsigned pwr_down_mode; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + + union { + u32 d32; + u8 d8[4]; + } data[3] ____cacheline_aligned; +}; + +/** + * ad5686_supported_device_ids: + */ + +enum ad5686_supported_device_ids { + ID_AD5684, + ID_AD5685, + ID_AD5686, +}; +static int ad5686_spi_write(struct ad5686_state *st, + u8 cmd, u8 addr, u16 val, u8 shift) +{ + val <<= shift; + + st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | + AD5686_ADDR(addr) | + val); + + return spi_write(st->spi, &st->data[0].d8[1], 3); +} + +static int ad5686_spi_read(struct ad5686_state *st, u8 addr) +{ + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[1], + .len = 3, + .cs_change = 1, + }, { + .tx_buf = &st->data[1].d8[1], + .rx_buf = &st->data[2].d8[1], + .len = 3, + }, + }; + struct spi_message m; + int ret; + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_READBACK_ENABLE) | + AD5686_ADDR(addr)); + st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP)); + + ret = spi_sync(st->spi, &m); + if (ret < 0) + return ret; + + return be32_to_cpu(st->data[2].d32); +} + +static const char * const ad5686_powerdown_modes[] = { + "1kohm_to_gnd", + "100kohm_to_gnd", + "three_state" +}; + +static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5686_state *st = iio_priv(indio_dev); + + return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1; +} + +static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct ad5686_state *st = iio_priv(indio_dev); + + st->pwr_down_mode &= ~(0x3 << (chan->channel * 2)); + st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2)); + + return 0; +} + +static const struct iio_enum ad5686_powerdown_mode_enum = { + .items = ad5686_powerdown_modes, + .num_items = ARRAY_SIZE(ad5686_powerdown_modes), + .get = ad5686_get_powerdown_mode, + .set = ad5686_set_powerdown_mode, +}; + +static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct ad5686_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", !!(st->pwr_down_mask & + (0x3 << (chan->channel * 2)))); +} + +static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + bool readin; + int ret; + struct ad5686_state *st = iio_priv(indio_dev); + + ret = strtobool(buf, &readin); + if (ret) + return ret; + + if (readin == true) + st->pwr_down_mask |= (0x3 << (chan->channel * 2)); + else + st->pwr_down_mask &= ~(0x3 << (chan->channel * 2)); + + ret = ad5686_spi_write(st, AD5686_CMD_POWERDOWN_DAC, 0, + st->pwr_down_mask & st->pwr_down_mode, 0); + + return ret ? ret : len; +} + +static int ad5686_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad5686_state *st = iio_priv(indio_dev); + unsigned long scale_uv; + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + ret = ad5686_spi_read(st, chan->address); + mutex_unlock(&indio_dev->mlock); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_mv * 100000) + >> (chan->scan_type.realbits); + *val = scale_uv / 100000; + *val2 = (scale_uv % 100000) * 10; + return IIO_VAL_INT_PLUS_MICRO; + + } + return -EINVAL; +} + +static int ad5686_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad5686_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val > (1 << chan->scan_type.realbits) || val < 0) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + ret = ad5686_spi_write(st, + AD5686_CMD_WRITE_INPUT_N_UPDATE_N, + chan->address, + val, + chan->scan_type.shift); + mutex_unlock(&indio_dev->mlock); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct iio_info ad5686_info = { + .read_raw = ad5686_read_raw, + .write_raw = ad5686_write_raw, + .driver_module = THIS_MODULE, +}; + +static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { + { + .name = "powerdown", + .read = ad5686_read_dac_powerdown, + .write = ad5686_write_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", false, &ad5686_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", &ad5686_powerdown_mode_enum), + { }, +}; + +#define AD5868_CHANNEL(chan, bits, shift) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = chan, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT, \ + .address = AD5686_ADDR_DAC(chan), \ + .scan_type = IIO_ST('u', bits, 16, shift), \ + .ext_info = ad5686_ext_info, \ +} + +static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { + [ID_AD5684] = { + .channel[0] = AD5868_CHANNEL(0, 12, 4), + .channel[1] = AD5868_CHANNEL(1, 12, 4), + .channel[2] = AD5868_CHANNEL(2, 12, 4), + .channel[3] = AD5868_CHANNEL(3, 12, 4), + .int_vref_mv = 2500, + }, + [ID_AD5685] = { + .channel[0] = AD5868_CHANNEL(0, 14, 2), + .channel[1] = AD5868_CHANNEL(1, 14, 2), + .channel[2] = AD5868_CHANNEL(2, 14, 2), + .channel[3] = AD5868_CHANNEL(3, 14, 2), + .int_vref_mv = 2500, + }, + [ID_AD5686] = { + .channel[0] = AD5868_CHANNEL(0, 16, 0), + .channel[1] = AD5868_CHANNEL(1, 16, 0), + .channel[2] = AD5868_CHANNEL(2, 16, 0), + .channel[3] = AD5868_CHANNEL(3, 16, 0), + .int_vref_mv = 2500, + }, +}; + + +static int __devinit ad5686_probe(struct spi_device *spi) +{ + struct ad5686_state *st; + struct iio_dev *indio_dev; + int ret, regdone = 0, voltage_uv = 0; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->reg = regulator_get(&spi->dev, "vcc"); + if (!IS_ERR(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + + voltage_uv = regulator_get_voltage(st->reg); + } + + st->chip_info = + &ad5686_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + if (voltage_uv) + st->vref_mv = voltage_uv / 1000; + else + st->vref_mv = st->chip_info->int_vref_mv; + + st->spi = spi; + + /* Set all the power down mode for all channels to 1K pulldown */ + st->pwr_down_mode = 0x55; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad5686_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channel; + indio_dev->num_channels = AD5686_DAC_CHANNELS; + + regdone = 1; + ret = ad5686_spi_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0, + !!voltage_uv, 0); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_disable_reg; + + return 0; + +error_disable_reg: + if (!IS_ERR(st->reg)) + regulator_disable(st->reg); +error_put_reg: + if (!IS_ERR(st->reg)) + regulator_put(st->reg); + + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad5686_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5686_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (!IS_ERR(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5686_id[] = { + {"ad5684", ID_AD5684}, + {"ad5685", ID_AD5685}, + {"ad5686", ID_AD5686}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5686_id); + +static struct spi_driver ad5686_driver = { + .driver = { + .name = "ad5686", + .owner = THIS_MODULE, + }, + .probe = ad5686_probe, + .remove = __devexit_p(ad5686_remove), + .id_table = ad5686_id, +}; +module_spi_driver(ad5686_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5764.c b/drivers/iio/dac/ad5764.c new file mode 100644 index 000000000000..ffce30447445 --- /dev/null +++ b/drivers/iio/dac/ad5764.c @@ -0,0 +1,382 @@ +/* + * Analog devices AD5764, AD5764R, AD5744, AD5744R quad-channel + * Digital to Analog Converters driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define AD5764_REG_SF_NOP 0x0 +#define AD5764_REG_SF_CONFIG 0x1 +#define AD5764_REG_SF_CLEAR 0x4 +#define AD5764_REG_SF_LOAD 0x5 +#define AD5764_REG_DATA(x) ((2 << 3) | (x)) +#define AD5764_REG_COARSE_GAIN(x) ((3 << 3) | (x)) +#define AD5764_REG_FINE_GAIN(x) ((4 << 3) | (x)) +#define AD5764_REG_OFFSET(x) ((5 << 3) | (x)) + +#define AD5764_NUM_CHANNELS 4 + +/** + * struct ad5764_chip_info - chip specific information + * @int_vref: Value of the internal reference voltage in uV - 0 if external + * reference voltage is used + * @channel channel specification +*/ + +struct ad5764_chip_info { + unsigned long int_vref; + const struct iio_chan_spec *channels; +}; + +/** + * struct ad5764_state - driver instance specific data + * @spi: spi_device + * @chip_info: chip info + * @vref_reg: vref supply regulators + * @data: spi transfer buffers + */ + +struct ad5764_state { + struct spi_device *spi; + const struct ad5764_chip_info *chip_info; + struct regulator_bulk_data vref_reg[2]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + union { + __be32 d32; + u8 d8[4]; + } data[2] ____cacheline_aligned; +}; + +enum ad5764_type { + ID_AD5744, + ID_AD5744R, + ID_AD5764, + ID_AD5764R, +}; + +#define AD5764_CHANNEL(_chan, _bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (_chan), \ + .address = (_chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_OFFSET_SHARED_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \ + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \ + .scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)) \ +} + +#define DECLARE_AD5764_CHANNELS(_name, _bits) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD5764_CHANNEL(0, (_bits)), \ + AD5764_CHANNEL(1, (_bits)), \ + AD5764_CHANNEL(2, (_bits)), \ + AD5764_CHANNEL(3, (_bits)), \ +}; + +static DECLARE_AD5764_CHANNELS(ad5764, 16); +static DECLARE_AD5764_CHANNELS(ad5744, 14); + +static const struct ad5764_chip_info ad5764_chip_infos[] = { + [ID_AD5744] = { + .int_vref = 0, + .channels = ad5744_channels, + }, + [ID_AD5744R] = { + .int_vref = 5000000, + .channels = ad5744_channels, + }, + [ID_AD5764] = { + .int_vref = 0, + .channels = ad5764_channels, + }, + [ID_AD5764R] = { + .int_vref = 5000000, + .channels = ad5764_channels, + }, +}; + +static int ad5764_write(struct iio_dev *indio_dev, unsigned int reg, + unsigned int val) +{ + struct ad5764_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&indio_dev->mlock); + st->data[0].d32 = cpu_to_be32((reg << 16) | val); + + ret = spi_write(st->spi, &st->data[0].d8[1], 3); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg, + unsigned int *val) +{ + struct ad5764_state *st = iio_priv(indio_dev); + struct spi_message m; + int ret; + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[1], + .len = 3, + .cs_change = 1, + }, { + .rx_buf = &st->data[1].d8[1], + .len = 3, + }, + }; + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + mutex_lock(&indio_dev->mlock); + + st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16)); + + ret = spi_sync(st->spi, &m); + if (ret >= 0) + *val = be32_to_cpu(st->data[1].d32) & 0xffff; + + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad5764_chan_info_to_reg(struct iio_chan_spec const *chan, long info) +{ + switch (info) { + case 0: + return AD5764_REG_DATA(chan->address); + case IIO_CHAN_INFO_CALIBBIAS: + return AD5764_REG_OFFSET(chan->address); + case IIO_CHAN_INFO_CALIBSCALE: + return AD5764_REG_FINE_GAIN(chan->address); + default: + break; + } + + return 0; +} + +static int ad5764_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long info) +{ + const int max_val = (1 << chan->scan_type.realbits); + unsigned int reg; + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (val >= max_val || val < 0) + return -EINVAL; + val <<= chan->scan_type.shift; + break; + case IIO_CHAN_INFO_CALIBBIAS: + if (val >= 128 || val < -128) + return -EINVAL; + break; + case IIO_CHAN_INFO_CALIBSCALE: + if (val >= 32 || val < -32) + return -EINVAL; + break; + default: + return -EINVAL; + } + + reg = ad5764_chan_info_to_reg(chan, info); + return ad5764_write(indio_dev, reg, (u16)val); +} + +static int ad5764_get_channel_vref(struct ad5764_state *st, + unsigned int channel) +{ + if (st->chip_info->int_vref) + return st->chip_info->int_vref; + else + return regulator_get_voltage(st->vref_reg[channel / 2].consumer); +} + +static int ad5764_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long info) +{ + struct ad5764_state *st = iio_priv(indio_dev); + unsigned long scale_uv; + unsigned int reg; + int vref; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + reg = AD5764_REG_DATA(chan->address); + ret = ad5764_read(indio_dev, reg, val); + if (ret < 0) + return ret; + *val >>= chan->scan_type.shift; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + reg = AD5764_REG_OFFSET(chan->address); + ret = ad5764_read(indio_dev, reg, val); + if (ret < 0) + return ret; + *val = sign_extend32(*val, 7); + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + reg = AD5764_REG_FINE_GAIN(chan->address); + ret = ad5764_read(indio_dev, reg, val); + if (ret < 0) + return ret; + *val = sign_extend32(*val, 5); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* vout = 4 * vref + ((dac_code / 65535) - 0.5) */ + vref = ad5764_get_channel_vref(st, chan->channel); + if (vref < 0) + return vref; + + scale_uv = (vref * 4 * 100) >> chan->scan_type.realbits; + *val = scale_uv / 100000; + *val2 = (scale_uv % 100000) * 10; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + *val = -(1 << chan->scan_type.realbits) / 2; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static const struct iio_info ad5764_info = { + .read_raw = ad5764_read_raw, + .write_raw = ad5764_write_raw, + .driver_module = THIS_MODULE, +}; + +static int __devinit ad5764_probe(struct spi_device *spi) +{ + enum ad5764_type type = spi_get_device_id(spi)->driver_data; + struct iio_dev *indio_dev; + struct ad5764_state *st; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + dev_err(&spi->dev, "Failed to allocate iio device\n"); + return -ENOMEM; + } + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + st->chip_info = &ad5764_chip_infos[type]; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad5764_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = AD5764_NUM_CHANNELS; + indio_dev->channels = st->chip_info->channels; + + if (st->chip_info->int_vref == 0) { + st->vref_reg[0].supply = "vrefAB"; + st->vref_reg[1].supply = "vrefCD"; + + ret = regulator_bulk_get(&st->spi->dev, + ARRAY_SIZE(st->vref_reg), st->vref_reg); + if (ret) { + dev_err(&spi->dev, "Failed to request vref regulators: %d\n", + ret); + goto error_free; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(st->vref_reg), + st->vref_reg); + if (ret) { + dev_err(&spi->dev, "Failed to enable vref regulators: %d\n", + ret); + goto error_free_reg; + } + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); + goto error_disable_reg; + } + + return 0; + +error_disable_reg: + if (st->chip_info->int_vref == 0) + regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg); +error_free_reg: + if (st->chip_info->int_vref == 0) + regulator_bulk_free(ARRAY_SIZE(st->vref_reg), st->vref_reg); +error_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad5764_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5764_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + if (st->chip_info->int_vref == 0) { + regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg); + regulator_bulk_free(ARRAY_SIZE(st->vref_reg), st->vref_reg); + } + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5764_ids[] = { + { "ad5744", ID_AD5744 }, + { "ad5744r", ID_AD5744R }, + { "ad5764", ID_AD5764 }, + { "ad5764r", ID_AD5764R }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad5764_ids); + +static struct spi_driver ad5764_driver = { + .driver = { + .name = "ad5764", + .owner = THIS_MODULE, + }, + .probe = ad5764_probe, + .remove = __devexit_p(ad5764_remove), + .id_table = ad5764_ids, +}; +module_spi_driver(ad5764_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD5744/AD5744R/AD5764/AD5764R DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c new file mode 100644 index 000000000000..2bd2e37280ff --- /dev/null +++ b/drivers/iio/dac/ad5791.c @@ -0,0 +1,485 @@ +/* + * AD5760, AD5780, AD5781, AD5790, AD5791 Voltage Output Digital to Analog + * Converter + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define AD5791_RES_MASK(x) ((1 << (x)) - 1) +#define AD5791_DAC_MASK AD5791_RES_MASK(20) +#define AD5791_DAC_MSB (1 << 19) + +#define AD5791_CMD_READ (1 << 23) +#define AD5791_CMD_WRITE (0 << 23) +#define AD5791_ADDR(addr) ((addr) << 20) + +/* Registers */ +#define AD5791_ADDR_NOOP 0 +#define AD5791_ADDR_DAC0 1 +#define AD5791_ADDR_CTRL 2 +#define AD5791_ADDR_CLRCODE 3 +#define AD5791_ADDR_SW_CTRL 4 + +/* Control Register */ +#define AD5791_CTRL_RBUF (1 << 1) +#define AD5791_CTRL_OPGND (1 << 2) +#define AD5791_CTRL_DACTRI (1 << 3) +#define AD5791_CTRL_BIN2SC (1 << 4) +#define AD5791_CTRL_SDODIS (1 << 5) +#define AD5761_CTRL_LINCOMP(x) ((x) << 6) + +#define AD5791_LINCOMP_0_10 0 +#define AD5791_LINCOMP_10_12 1 +#define AD5791_LINCOMP_12_16 2 +#define AD5791_LINCOMP_16_19 3 +#define AD5791_LINCOMP_19_20 12 + +#define AD5780_LINCOMP_0_10 0 +#define AD5780_LINCOMP_10_20 12 + +/* Software Control Register */ +#define AD5791_SWCTRL_LDAC (1 << 0) +#define AD5791_SWCTRL_CLR (1 << 1) +#define AD5791_SWCTRL_RESET (1 << 2) + +#define AD5791_DAC_PWRDN_6K 0 +#define AD5791_DAC_PWRDN_3STATE 1 + +/** + * struct ad5791_chip_info - chip specific information + * @get_lin_comp: function pointer to the device specific function + */ + +struct ad5791_chip_info { + int (*get_lin_comp) (unsigned int span); +}; + +/** + * struct ad5791_state - driver instance specific data + * @us: spi_device + * @reg_vdd: positive supply regulator + * @reg_vss: negative supply regulator + * @chip_info: chip model specific constants + * @vref_mv: actual reference voltage used + * @vref_neg_mv: voltage of the negative supply + * @pwr_down_mode current power down mode + */ + +struct ad5791_state { + struct spi_device *spi; + struct regulator *reg_vdd; + struct regulator *reg_vss; + const struct ad5791_chip_info *chip_info; + unsigned short vref_mv; + unsigned int vref_neg_mv; + unsigned ctrl; + unsigned pwr_down_mode; + bool pwr_down; +}; + +/** + * ad5791_supported_device_ids: + */ + +enum ad5791_supported_device_ids { + ID_AD5760, + ID_AD5780, + ID_AD5781, + ID_AD5791, +}; + +static int ad5791_spi_write(struct spi_device *spi, u8 addr, u32 val) +{ + union { + u32 d32; + u8 d8[4]; + } data; + + data.d32 = cpu_to_be32(AD5791_CMD_WRITE | + AD5791_ADDR(addr) | + (val & AD5791_DAC_MASK)); + + return spi_write(spi, &data.d8[1], 3); +} + +static int ad5791_spi_read(struct spi_device *spi, u8 addr, u32 *val) +{ + union { + u32 d32; + u8 d8[4]; + } data[3]; + int ret; + struct spi_message msg; + struct spi_transfer xfers[] = { + { + .tx_buf = &data[0].d8[1], + .bits_per_word = 8, + .len = 3, + .cs_change = 1, + }, { + .tx_buf = &data[1].d8[1], + .rx_buf = &data[2].d8[1], + .bits_per_word = 8, + .len = 3, + }, + }; + + data[0].d32 = cpu_to_be32(AD5791_CMD_READ | + AD5791_ADDR(addr)); + data[1].d32 = cpu_to_be32(AD5791_ADDR(AD5791_ADDR_NOOP)); + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(spi, &msg); + + *val = be32_to_cpu(data[2].d32); + + return ret; +} + +static const char * const ad5791_powerdown_modes[] = { + "6kohm_to_gnd", + "three_state", +}; + +static int ad5791_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5791_state *st = iio_priv(indio_dev); + + return st->pwr_down_mode; +} + +static int ad5791_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int mode) +{ + struct ad5791_state *st = iio_priv(indio_dev); + + st->pwr_down_mode = mode; + + return 0; +} + +static const struct iio_enum ad5791_powerdown_mode_enum = { + .items = ad5791_powerdown_modes, + .num_items = ARRAY_SIZE(ad5791_powerdown_modes), + .get = ad5791_get_powerdown_mode, + .set = ad5791_set_powerdown_mode, +}; + +static ssize_t ad5791_read_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct ad5791_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->pwr_down); +} + +static ssize_t ad5791_write_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + bool pwr_down; + int ret; + struct ad5791_state *st = iio_priv(indio_dev); + + ret = strtobool(buf, &pwr_down); + if (ret) + return ret; + + if (!pwr_down) { + st->ctrl &= ~(AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI); + } else { + if (st->pwr_down_mode == AD5791_DAC_PWRDN_6K) + st->ctrl |= AD5791_CTRL_OPGND; + else if (st->pwr_down_mode == AD5791_DAC_PWRDN_3STATE) + st->ctrl |= AD5791_CTRL_DACTRI; + } + st->pwr_down = pwr_down; + + ret = ad5791_spi_write(st->spi, AD5791_ADDR_CTRL, st->ctrl); + + return ret ? ret : len; +} + +static int ad5791_get_lin_comp(unsigned int span) +{ + if (span <= 10000) + return AD5791_LINCOMP_0_10; + else if (span <= 12000) + return AD5791_LINCOMP_10_12; + else if (span <= 16000) + return AD5791_LINCOMP_12_16; + else if (span <= 19000) + return AD5791_LINCOMP_16_19; + else + return AD5791_LINCOMP_19_20; +} + +static int ad5780_get_lin_comp(unsigned int span) +{ + if (span <= 10000) + return AD5780_LINCOMP_0_10; + else + return AD5780_LINCOMP_10_20; +} +static const struct ad5791_chip_info ad5791_chip_info_tbl[] = { + [ID_AD5760] = { + .get_lin_comp = ad5780_get_lin_comp, + }, + [ID_AD5780] = { + .get_lin_comp = ad5780_get_lin_comp, + }, + [ID_AD5781] = { + .get_lin_comp = ad5791_get_lin_comp, + }, + [ID_AD5791] = { + .get_lin_comp = ad5791_get_lin_comp, + }, +}; + +static int ad5791_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad5791_state *st = iio_priv(indio_dev); + u64 val64; + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ad5791_spi_read(st->spi, chan->address, val); + if (ret) + return ret; + *val &= AD5791_DAC_MASK; + *val >>= chan->scan_type.shift; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = (((u64)st->vref_mv) * 1000000ULL) >> chan->scan_type.realbits; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + val64 = (((u64)st->vref_neg_mv) << chan->scan_type.realbits); + do_div(val64, st->vref_mv); + *val = -val64; + return IIO_VAL_INT; + default: + return -EINVAL; + } + +}; + +static const struct iio_chan_spec_ext_info ad5791_ext_info[] = { + { + .name = "powerdown", + .shared = true, + .read = ad5791_read_dac_powerdown, + .write = ad5791_write_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", true, &ad5791_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", &ad5791_powerdown_mode_enum), + { }, +}; + +#define AD5791_CHAN(bits, shift) { \ + .type = IIO_VOLTAGE, \ + .output = 1, \ + .indexed = 1, \ + .address = AD5791_ADDR_DAC0, \ + .channel = 0, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT | \ + IIO_CHAN_INFO_OFFSET_SHARED_BIT, \ + .scan_type = IIO_ST('u', bits, 24, shift), \ + .ext_info = ad5791_ext_info, \ +} + +static const struct iio_chan_spec ad5791_channels[] = { + [ID_AD5760] = AD5791_CHAN(16, 4), + [ID_AD5780] = AD5791_CHAN(18, 2), + [ID_AD5781] = AD5791_CHAN(18, 2), + [ID_AD5791] = AD5791_CHAN(20, 0) +}; + +static int ad5791_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad5791_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + val &= AD5791_RES_MASK(chan->scan_type.realbits); + val <<= chan->scan_type.shift; + + return ad5791_spi_write(st->spi, chan->address, val); + + default: + return -EINVAL; + } +} + +static const struct iio_info ad5791_info = { + .read_raw = &ad5791_read_raw, + .write_raw = &ad5791_write_raw, + .driver_module = THIS_MODULE, +}; + +static int __devinit ad5791_probe(struct spi_device *spi) +{ + struct ad5791_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev; + struct ad5791_state *st; + int ret, pos_voltage_uv = 0, neg_voltage_uv = 0; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + st = iio_priv(indio_dev); + st->reg_vdd = regulator_get(&spi->dev, "vdd"); + if (!IS_ERR(st->reg_vdd)) { + ret = regulator_enable(st->reg_vdd); + if (ret) + goto error_put_reg_pos; + + pos_voltage_uv = regulator_get_voltage(st->reg_vdd); + } + + st->reg_vss = regulator_get(&spi->dev, "vss"); + if (!IS_ERR(st->reg_vss)) { + ret = regulator_enable(st->reg_vss); + if (ret) + goto error_put_reg_neg; + + neg_voltage_uv = regulator_get_voltage(st->reg_vss); + } + + st->pwr_down = true; + st->spi = spi; + + if (!IS_ERR(st->reg_vss) && !IS_ERR(st->reg_vdd)) { + st->vref_mv = (pos_voltage_uv + neg_voltage_uv) / 1000; + st->vref_neg_mv = neg_voltage_uv / 1000; + } else if (pdata) { + st->vref_mv = pdata->vref_pos_mv + pdata->vref_neg_mv; + st->vref_neg_mv = pdata->vref_neg_mv; + } else { + dev_warn(&spi->dev, "reference voltage unspecified\n"); + } + + ret = ad5791_spi_write(spi, AD5791_ADDR_SW_CTRL, AD5791_SWCTRL_RESET); + if (ret) + goto error_disable_reg_neg; + + st->chip_info = &ad5791_chip_info_tbl[spi_get_device_id(spi) + ->driver_data]; + + + st->ctrl = AD5761_CTRL_LINCOMP(st->chip_info->get_lin_comp(st->vref_mv)) + | ((pdata && pdata->use_rbuf_gain2) ? 0 : AD5791_CTRL_RBUF) | + AD5791_CTRL_BIN2SC; + + ret = ad5791_spi_write(spi, AD5791_ADDR_CTRL, st->ctrl | + AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI); + if (ret) + goto error_disable_reg_neg; + + spi_set_drvdata(spi, indio_dev); + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &ad5791_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels + = &ad5791_channels[spi_get_device_id(spi)->driver_data]; + indio_dev->num_channels = 1; + indio_dev->name = spi_get_device_id(st->spi)->name; + ret = iio_device_register(indio_dev); + if (ret) + goto error_disable_reg_neg; + + return 0; + +error_disable_reg_neg: + if (!IS_ERR(st->reg_vss)) + regulator_disable(st->reg_vss); +error_put_reg_neg: + if (!IS_ERR(st->reg_vss)) + regulator_put(st->reg_vss); + + if (!IS_ERR(st->reg_vdd)) + regulator_disable(st->reg_vdd); +error_put_reg_pos: + if (!IS_ERR(st->reg_vdd)) + regulator_put(st->reg_vdd); + iio_device_free(indio_dev); +error_ret: + + return ret; +} + +static int __devexit ad5791_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad5791_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (!IS_ERR(st->reg_vdd)) { + regulator_disable(st->reg_vdd); + regulator_put(st->reg_vdd); + } + + if (!IS_ERR(st->reg_vss)) { + regulator_disable(st->reg_vss); + regulator_put(st->reg_vss); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad5791_id[] = { + {"ad5760", ID_AD5760}, + {"ad5780", ID_AD5780}, + {"ad5781", ID_AD5781}, + {"ad5790", ID_AD5791}, + {"ad5791", ID_AD5791}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5791_id); + +static struct spi_driver ad5791_driver = { + .driver = { + .name = "ad5791", + .owner = THIS_MODULE, + }, + .probe = ad5791_probe, + .remove = __devexit_p(ad5791_remove), + .id_table = ad5791_id, +}; +module_spi_driver(ad5791_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c new file mode 100644 index 000000000000..92c77c82026c --- /dev/null +++ b/drivers/iio/dac/max517.c @@ -0,0 +1,243 @@ +/* + * max517.c - Support for Maxim MAX517, MAX518 and MAX519 + * + * Copyright (C) 2010, 2011 Roland Stigge + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MAX517_DRV_NAME "max517" + +/* Commands */ +#define COMMAND_CHANNEL0 0x00 +#define COMMAND_CHANNEL1 0x01 /* for MAX518 and MAX519 */ +#define COMMAND_PD 0x08 /* Power Down */ + +enum max517_device_ids { + ID_MAX517, + ID_MAX518, + ID_MAX519, +}; + +struct max517_data { + struct iio_dev *indio_dev; + struct i2c_client *client; + unsigned short vref_mv[2]; +}; + +/* + * channel: bit 0: channel 1 + * bit 1: channel 2 + * (this way, it's possible to set both channels at once) + */ +static int max517_set_value(struct iio_dev *indio_dev, + long val, int channel) +{ + struct max517_data *data = iio_priv(indio_dev); + struct i2c_client *client = data->client; + u8 outbuf[2]; + int res; + + if (val < 0 || val > 255) + return -EINVAL; + + outbuf[0] = channel; + outbuf[1] = val; + + res = i2c_master_send(client, outbuf, 2); + if (res < 0) + return res; + else if (res != 2) + return -EIO; + else + return 0; +} + +static int max517_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct max517_data *data = iio_priv(indio_dev); + unsigned int scale_uv; + + switch (m) { + case IIO_CHAN_INFO_SCALE: + /* Corresponds to Vref / 2^(bits) */ + scale_uv = (data->vref_mv[chan->channel] * 1000) >> 8; + *val = scale_uv / 1000000; + *val2 = scale_uv % 1000000; + return IIO_VAL_INT_PLUS_MICRO; + default: + break; + } + return -EINVAL; +} + +static int max517_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = max517_set_value(indio_dev, val, chan->channel); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int max517_suspend(struct device *dev) +{ + u8 outbuf = COMMAND_PD; + + return i2c_master_send(to_i2c_client(dev), &outbuf, 1); +} + +static int max517_resume(struct device *dev) +{ + u8 outbuf = 0; + + return i2c_master_send(to_i2c_client(dev), &outbuf, 1); +} + +static SIMPLE_DEV_PM_OPS(max517_pm_ops, max517_suspend, max517_resume); +#define MAX517_PM_OPS (&max517_pm_ops) +#else +#define MAX517_PM_OPS NULL +#endif + +static const struct iio_info max517_info = { + .read_raw = max517_read_raw, + .write_raw = max517_write_raw, + .driver_module = THIS_MODULE, +}; + +#define MAX517_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ + .scan_type = IIO_ST('u', 8, 8, 0), \ +} + +static const struct iio_chan_spec max517_channels[] = { + MAX517_CHANNEL(0), + MAX517_CHANNEL(1) +}; + +static int max517_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max517_data *data; + struct iio_dev *indio_dev; + struct max517_platform_data *platform_data = client->dev.platform_data; + int err; + + indio_dev = iio_device_alloc(sizeof(*data)); + if (indio_dev == NULL) { + err = -ENOMEM; + goto exit; + } + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + /* establish that the iio_dev is a child of the i2c device */ + indio_dev->dev.parent = &client->dev; + + /* reduced channel set for MAX517 */ + if (id->driver_data == ID_MAX517) + indio_dev->num_channels = 1; + else + indio_dev->num_channels = 2; + indio_dev->channels = max517_channels; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &max517_info; + + /* + * Reference voltage on MAX518 and default is 5V, else take vref_mv + * from platform_data + */ + if (id->driver_data == ID_MAX518 || !platform_data) { + data->vref_mv[0] = data->vref_mv[1] = 5000; /* mV */ + } else { + data->vref_mv[0] = platform_data->vref_mv[0]; + data->vref_mv[1] = platform_data->vref_mv[1]; + } + + err = iio_device_register(indio_dev); + if (err) + goto exit_free_device; + + dev_info(&client->dev, "DAC registered\n"); + + return 0; + +exit_free_device: + iio_device_free(indio_dev); +exit: + return err; +} + +static int max517_remove(struct i2c_client *client) +{ + iio_device_unregister(i2c_get_clientdata(client)); + iio_device_free(i2c_get_clientdata(client)); + + return 0; +} + +static const struct i2c_device_id max517_id[] = { + { "max517", ID_MAX517 }, + { "max518", ID_MAX518 }, + { "max519", ID_MAX519 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max517_id); + +static struct i2c_driver max517_driver = { + .driver = { + .name = MAX517_DRV_NAME, + .pm = MAX517_PM_OPS, + }, + .probe = max517_probe, + .remove = max517_remove, + .id_table = max517_id, +}; +module_i2c_driver(max517_driver); + +MODULE_AUTHOR("Roland Stigge "); +MODULE_DESCRIPTION("MAX517/MAX518/MAX519 8-bit DAC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index 3c8e5ec26ac1..04cd6ec1f70f 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -29,7 +29,6 @@ source "drivers/staging/iio/accel/Kconfig" source "drivers/staging/iio/adc/Kconfig" source "drivers/staging/iio/addac/Kconfig" source "drivers/staging/iio/cdc/Kconfig" -source "drivers/staging/iio/dac/Kconfig" source "drivers/staging/iio/frequency/Kconfig" source "drivers/staging/iio/gyro/Kconfig" source "drivers/staging/iio/impedance-analyzer/Kconfig" diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index 6a46d5afb380..fa6937d92ee3 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -17,7 +17,6 @@ obj-y += accel/ obj-y += adc/ obj-y += addac/ obj-y += cdc/ -obj-y += dac/ obj-y += frequency/ obj-y += gyro/ obj-y += impedance-analyzer/ diff --git a/drivers/staging/iio/dac/Kconfig b/drivers/staging/iio/dac/Kconfig deleted file mode 100644 index a626f03871ec..000000000000 --- a/drivers/staging/iio/dac/Kconfig +++ /dev/null @@ -1,121 +0,0 @@ -# -# DAC drivers -# -menu "Digital to analog converters" - -config AD5064 - tristate "Analog Devices AD5064/64-1/65/44/45/24/25, AD5628/48/66/68 DAC driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5024, AD5025, AD5044, - AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5648, AD5666, AD5668 Digital - to Analog Converter. - - To compile this driver as a module, choose M here: the - module will be called ad5064. - -config AD5360 - tristate "Analog Devices Analog Devices AD5360/61/62/63/70/71/73 DAC driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5360, AD5361, - AD5362, AD5363, AD5370, AD5371, AD5373 multi-channel - Digital to Analog Converters (DAC). - - To compile this driver as module choose M here: the module will be called - ad5360. - -config AD5380 - tristate "Analog Devices AD5380/81/82/83/84/90/91/92 DAC driver" - depends on (SPI_MASTER || I2C) - select REGMAP_I2C if I2C - select REGMAP_SPI if SPI_MASTER - help - Say yes here to build support for Analog Devices AD5380, AD5381, - AD5382, AD5383, AD5384, AD5390, AD5391, AD5392 multi-channel - Digital to Analog Converters (DAC). - - To compile this driver as module choose M here: the module will be called - ad5380. - -config AD5421 - tristate "Analog Devices AD5421 DAC driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5421 loop-powered - digital-to-analog convertors (DAC). - - To compile this driver as module choose M here: the module will be called - ad5421. - -config AD5624R_SPI - tristate "Analog Devices AD5624/44/64R DAC spi driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5624R, AD5644R and - AD5664R converters (DAC). This driver uses the common SPI interface. - -config AD5446 - tristate "Analog Devices AD5446 and similar single channel DACs driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5444, AD5446, - AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5611, AD5620, - AD5621, AD5640, AD5660, AD5662 DACs. - - To compile this driver as a module, choose M here: the - module will be called ad5446. - -config AD5504 - tristate "Analog Devices AD5504/AD5501 DAC SPI driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5504, AD5501, - High Voltage Digital to Analog Converter. - - To compile this driver as a module, choose M here: the - module will be called ad5504. - -config AD5764 - tristate "Analog Devices AD5764/64R/44/44R DAC driver" - depends on SPI_MASTER - help - Say yes here to build support for Analog Devices AD5764, AD5764R, AD5744, - AD5744R Digital to Analog Converter. - - To compile this driver as a module, choose M here: the - module will be called ad5764. - -config AD5791 - tristate "Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC SPI driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5760, AD5780, - AD5781, AD5790, AD5791 High Resolution Voltage Output Digital to - Analog Converter. - - To compile this driver as a module, choose M here: the - module will be called ad5791. - -config AD5686 - tristate "Analog Devices AD5686R/AD5685R/AD5684R DAC SPI driver" - depends on SPI - help - Say yes here to build support for Analog Devices AD5686R, AD5685R, - AD5684R, AD5791 Voltage Output Digital to - Analog Converter. - - To compile this driver as a module, choose M here: the - module will be called ad5686. - -config MAX517 - tristate "Maxim MAX517/518/519 DAC driver" - depends on I2C && EXPERIMENTAL - help - If you say yes here you get support for the Maxim chips MAX517, - MAX518 and MAX519 (I2C 8-Bit DACs with rail-to-rail outputs). - - This driver can also be built as a module. If so, the module - will be called max517. - -endmenu diff --git a/drivers/staging/iio/dac/Makefile b/drivers/staging/iio/dac/Makefile deleted file mode 100644 index 8ab1d264aab7..000000000000 --- a/drivers/staging/iio/dac/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# -# Makefile for industrial I/O DAC drivers -# - -obj-$(CONFIG_AD5360) += ad5360.o -obj-$(CONFIG_AD5380) += ad5380.o -obj-$(CONFIG_AD5421) += ad5421.o -obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o -obj-$(CONFIG_AD5064) += ad5064.o -obj-$(CONFIG_AD5504) += ad5504.o -obj-$(CONFIG_AD5446) += ad5446.o -obj-$(CONFIG_AD5764) += ad5764.o -obj-$(CONFIG_AD5791) += ad5791.o -obj-$(CONFIG_AD5686) += ad5686.o -obj-$(CONFIG_MAX517) += max517.o diff --git a/drivers/staging/iio/dac/ad5064.c b/drivers/staging/iio/dac/ad5064.c deleted file mode 100644 index 276af02520af..000000000000 --- a/drivers/staging/iio/dac/ad5064.c +++ /dev/null @@ -1,538 +0,0 @@ -/* - * AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5648, - * AD5666, AD5668 Digital to analog converters driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define AD5064_MAX_DAC_CHANNELS 8 -#define AD5064_MAX_VREFS 4 - -#define AD5064_ADDR(x) ((x) << 20) -#define AD5064_CMD(x) ((x) << 24) - -#define AD5064_ADDR_DAC(chan) (chan) -#define AD5064_ADDR_ALL_DAC 0xF - -#define AD5064_CMD_WRITE_INPUT_N 0x0 -#define AD5064_CMD_UPDATE_DAC_N 0x1 -#define AD5064_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2 -#define AD5064_CMD_WRITE_INPUT_N_UPDATE_N 0x3 -#define AD5064_CMD_POWERDOWN_DAC 0x4 -#define AD5064_CMD_CLEAR 0x5 -#define AD5064_CMD_LDAC_MASK 0x6 -#define AD5064_CMD_RESET 0x7 -#define AD5064_CMD_CONFIG 0x8 - -#define AD5064_CONFIG_DAISY_CHAIN_ENABLE BIT(1) -#define AD5064_CONFIG_INT_VREF_ENABLE BIT(0) - -#define AD5064_LDAC_PWRDN_NONE 0x0 -#define AD5064_LDAC_PWRDN_1K 0x1 -#define AD5064_LDAC_PWRDN_100K 0x2 -#define AD5064_LDAC_PWRDN_3STATE 0x3 - -/** - * struct ad5064_chip_info - chip specific information - * @shared_vref: whether the vref supply is shared between channels - * @internal_vref: internal reference voltage. 0 if the chip has no internal - * vref. - * @channel: channel specification - * @num_channels: number of channels - */ - -struct ad5064_chip_info { - bool shared_vref; - unsigned long internal_vref; - const struct iio_chan_spec *channels; - unsigned int num_channels; -}; - -/** - * struct ad5064_state - driver instance specific data - * @spi: spi_device - * @chip_info: chip model specific constants, available modes etc - * @vref_reg: vref supply regulators - * @pwr_down: whether channel is powered down - * @pwr_down_mode: channel's current power down mode - * @dac_cache: current DAC raw value (chip does not support readback) - * @use_internal_vref: set to true if the internal reference voltage should be - * used. - * @data: spi transfer buffers - */ - -struct ad5064_state { - struct spi_device *spi; - const struct ad5064_chip_info *chip_info; - struct regulator_bulk_data vref_reg[AD5064_MAX_VREFS]; - bool pwr_down[AD5064_MAX_DAC_CHANNELS]; - u8 pwr_down_mode[AD5064_MAX_DAC_CHANNELS]; - unsigned int dac_cache[AD5064_MAX_DAC_CHANNELS]; - bool use_internal_vref; - - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - */ - __be32 data ____cacheline_aligned; -}; - -enum ad5064_type { - ID_AD5024, - ID_AD5025, - ID_AD5044, - ID_AD5045, - ID_AD5064, - ID_AD5064_1, - ID_AD5065, - ID_AD5628_1, - ID_AD5628_2, - ID_AD5648_1, - ID_AD5648_2, - ID_AD5666_1, - ID_AD5666_2, - ID_AD5668_1, - ID_AD5668_2, -}; - -static int ad5064_spi_write(struct ad5064_state *st, unsigned int cmd, - unsigned int addr, unsigned int val, unsigned int shift) -{ - val <<= shift; - - st->data = cpu_to_be32(AD5064_CMD(cmd) | AD5064_ADDR(addr) | val); - - return spi_write(st->spi, &st->data, sizeof(st->data)); -} - -static int ad5064_sync_powerdown_mode(struct ad5064_state *st, - unsigned int channel) -{ - unsigned int val; - int ret; - - val = (0x1 << channel); - - if (st->pwr_down[channel]) - val |= st->pwr_down_mode[channel] << 8; - - ret = ad5064_spi_write(st, AD5064_CMD_POWERDOWN_DAC, 0, val, 0); - - return ret; -} - -static const char * const ad5064_powerdown_modes[] = { - "1kohm_to_gnd", - "100kohm_to_gnd", - "three_state", -}; - -static int ad5064_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct ad5064_state *st = iio_priv(indio_dev); - - return st->pwr_down_mode[chan->channel] - 1; -} - -static int ad5064_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) -{ - struct ad5064_state *st = iio_priv(indio_dev); - int ret; - - mutex_lock(&indio_dev->mlock); - st->pwr_down_mode[chan->channel] = mode + 1; - - ret = ad5064_sync_powerdown_mode(st, chan->channel); - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static const struct iio_enum ad5064_powerdown_mode_enum = { - .items = ad5064_powerdown_modes, - .num_items = ARRAY_SIZE(ad5064_powerdown_modes), - .get = ad5064_get_powerdown_mode, - .set = ad5064_set_powerdown_mode, -}; - -static ssize_t ad5064_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, char *buf) -{ - struct ad5064_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", st->pwr_down[chan->channel]); -} - -static ssize_t ad5064_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, - size_t len) -{ - struct ad5064_state *st = iio_priv(indio_dev); - bool pwr_down; - int ret; - - ret = strtobool(buf, &pwr_down); - if (ret) - return ret; - - mutex_lock(&indio_dev->mlock); - st->pwr_down[chan->channel] = pwr_down; - - ret = ad5064_sync_powerdown_mode(st, chan->channel); - mutex_unlock(&indio_dev->mlock); - return ret ? ret : len; -} - -static int ad5064_get_vref(struct ad5064_state *st, - struct iio_chan_spec const *chan) -{ - unsigned int i; - - if (st->use_internal_vref) - return st->chip_info->internal_vref; - - i = st->chip_info->shared_vref ? 0 : chan->channel; - return regulator_get_voltage(st->vref_reg[i].consumer); -} - -static int ad5064_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad5064_state *st = iio_priv(indio_dev); - int scale_uv; - - switch (m) { - case IIO_CHAN_INFO_RAW: - *val = st->dac_cache[chan->channel]; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - scale_uv = ad5064_get_vref(st, chan); - if (scale_uv < 0) - return scale_uv; - - scale_uv = (scale_uv * 100) >> chan->scan_type.realbits; - *val = scale_uv / 100000; - *val2 = (scale_uv % 100000) * 10; - return IIO_VAL_INT_PLUS_MICRO; - default: - break; - } - return -EINVAL; -} - -static int ad5064_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, long mask) -{ - struct ad5064_state *st = iio_priv(indio_dev); - int ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (val > (1 << chan->scan_type.realbits) || val < 0) - return -EINVAL; - - mutex_lock(&indio_dev->mlock); - ret = ad5064_spi_write(st, AD5064_CMD_WRITE_INPUT_N_UPDATE_N, - chan->address, val, chan->scan_type.shift); - if (ret == 0) - st->dac_cache[chan->channel] = val; - mutex_unlock(&indio_dev->mlock); - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static const struct iio_info ad5064_info = { - .read_raw = ad5064_read_raw, - .write_raw = ad5064_write_raw, - .driver_module = THIS_MODULE, -}; - -static const struct iio_chan_spec_ext_info ad5064_ext_info[] = { - { - .name = "powerdown", - .read = ad5064_read_dac_powerdown, - .write = ad5064_write_dac_powerdown, - }, - IIO_ENUM("powerdown_mode", false, &ad5064_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5064_powerdown_mode_enum), - { }, -}; - -#define AD5064_CHANNEL(chan, bits) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .channel = (chan), \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ - .address = AD5064_ADDR_DAC(chan), \ - .scan_type = IIO_ST('u', (bits), 16, 20 - (bits)), \ - .ext_info = ad5064_ext_info, \ -} - -#define DECLARE_AD5064_CHANNELS(name, bits) \ -const struct iio_chan_spec name[] = { \ - AD5064_CHANNEL(0, bits), \ - AD5064_CHANNEL(1, bits), \ - AD5064_CHANNEL(2, bits), \ - AD5064_CHANNEL(3, bits), \ - AD5064_CHANNEL(4, bits), \ - AD5064_CHANNEL(5, bits), \ - AD5064_CHANNEL(6, bits), \ - AD5064_CHANNEL(7, bits), \ -} - -static DECLARE_AD5064_CHANNELS(ad5024_channels, 12); -static DECLARE_AD5064_CHANNELS(ad5044_channels, 14); -static DECLARE_AD5064_CHANNELS(ad5064_channels, 16); - -static const struct ad5064_chip_info ad5064_chip_info_tbl[] = { - [ID_AD5024] = { - .shared_vref = false, - .channels = ad5024_channels, - .num_channels = 4, - }, - [ID_AD5025] = { - .shared_vref = false, - .channels = ad5024_channels, - .num_channels = 2, - }, - [ID_AD5044] = { - .shared_vref = false, - .channels = ad5044_channels, - .num_channels = 4, - }, - [ID_AD5045] = { - .shared_vref = false, - .channels = ad5044_channels, - .num_channels = 2, - }, - [ID_AD5064] = { - .shared_vref = false, - .channels = ad5064_channels, - .num_channels = 4, - }, - [ID_AD5064_1] = { - .shared_vref = true, - .channels = ad5064_channels, - .num_channels = 4, - }, - [ID_AD5065] = { - .shared_vref = false, - .channels = ad5064_channels, - .num_channels = 2, - }, - [ID_AD5628_1] = { - .shared_vref = true, - .internal_vref = 2500000, - .channels = ad5024_channels, - .num_channels = 8, - }, - [ID_AD5628_2] = { - .shared_vref = true, - .internal_vref = 5000000, - .channels = ad5024_channels, - .num_channels = 8, - }, - [ID_AD5648_1] = { - .shared_vref = true, - .internal_vref = 2500000, - .channels = ad5044_channels, - .num_channels = 8, - }, - [ID_AD5648_2] = { - .shared_vref = true, - .internal_vref = 5000000, - .channels = ad5044_channels, - .num_channels = 8, - }, - [ID_AD5666_1] = { - .shared_vref = true, - .internal_vref = 2500000, - .channels = ad5064_channels, - .num_channels = 4, - }, - [ID_AD5666_2] = { - .shared_vref = true, - .internal_vref = 5000000, - .channels = ad5064_channels, - .num_channels = 4, - }, - [ID_AD5668_1] = { - .shared_vref = true, - .internal_vref = 2500000, - .channels = ad5064_channels, - .num_channels = 8, - }, - [ID_AD5668_2] = { - .shared_vref = true, - .internal_vref = 5000000, - .channels = ad5064_channels, - .num_channels = 8, - }, -}; - -static inline unsigned int ad5064_num_vref(struct ad5064_state *st) -{ - return st->chip_info->shared_vref ? 1 : st->chip_info->num_channels; -} - -static const char * const ad5064_vref_names[] = { - "vrefA", - "vrefB", - "vrefC", - "vrefD", -}; - -static const char * const ad5064_vref_name(struct ad5064_state *st, - unsigned int vref) -{ - return st->chip_info->shared_vref ? "vref" : ad5064_vref_names[vref]; -} - -static int __devinit ad5064_probe(struct spi_device *spi) -{ - enum ad5064_type type = spi_get_device_id(spi)->driver_data; - struct iio_dev *indio_dev; - struct ad5064_state *st; - unsigned int i; - int ret; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) - return -ENOMEM; - - st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); - - st->chip_info = &ad5064_chip_info_tbl[type]; - st->spi = spi; - - for (i = 0; i < ad5064_num_vref(st); ++i) - st->vref_reg[i].supply = ad5064_vref_name(st, i); - - ret = regulator_bulk_get(&st->spi->dev, ad5064_num_vref(st), - st->vref_reg); - if (ret) { - if (!st->chip_info->internal_vref) - goto error_free; - st->use_internal_vref = true; - ret = ad5064_spi_write(st, AD5064_CMD_CONFIG, 0, - AD5064_CONFIG_INT_VREF_ENABLE, 0); - if (ret) { - dev_err(&spi->dev, "Failed to enable internal vref: %d\n", - ret); - goto error_free; - } - } else { - ret = regulator_bulk_enable(ad5064_num_vref(st), st->vref_reg); - if (ret) - goto error_free_reg; - } - - for (i = 0; i < st->chip_info->num_channels; ++i) { - st->pwr_down_mode[i] = AD5064_LDAC_PWRDN_1K; - st->dac_cache[i] = 0x8000; - } - - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->info = &ad5064_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = st->chip_info->channels; - indio_dev->num_channels = st->chip_info->num_channels; - - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg; - - return 0; - -error_disable_reg: - if (!st->use_internal_vref) - regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg); -error_free_reg: - if (!st->use_internal_vref) - regulator_bulk_free(ad5064_num_vref(st), st->vref_reg); -error_free: - iio_device_free(indio_dev); - - return ret; -} - - -static int __devexit ad5064_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5064_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - if (!st->use_internal_vref) { - regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg); - regulator_bulk_free(ad5064_num_vref(st), st->vref_reg); - } - - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5064_id[] = { - {"ad5024", ID_AD5024}, - {"ad5025", ID_AD5025}, - {"ad5044", ID_AD5044}, - {"ad5045", ID_AD5045}, - {"ad5064", ID_AD5064}, - {"ad5064-1", ID_AD5064_1}, - {"ad5065", ID_AD5065}, - {"ad5628-1", ID_AD5628_1}, - {"ad5628-2", ID_AD5628_2}, - {"ad5648-1", ID_AD5648_1}, - {"ad5648-2", ID_AD5648_2}, - {"ad5666-1", ID_AD5666_1}, - {"ad5666-2", ID_AD5666_2}, - {"ad5668-1", ID_AD5668_1}, - {"ad5668-2", ID_AD5668_2}, - {"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */ - {} -}; -MODULE_DEVICE_TABLE(spi, ad5064_id); - -static struct spi_driver ad5064_driver = { - .driver = { - .name = "ad5064", - .owner = THIS_MODULE, - }, - .probe = ad5064_probe, - .remove = __devexit_p(ad5064_remove), - .id_table = ad5064_id, -}; -module_spi_driver(ad5064_driver); - -MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_DESCRIPTION("Analog Devices AD5024/25/44/45/64/64-1/65, AD5628/48/66/68 DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5360.c b/drivers/staging/iio/dac/ad5360.c deleted file mode 100644 index 8fce84fe70b1..000000000000 --- a/drivers/staging/iio/dac/ad5360.c +++ /dev/null @@ -1,570 +0,0 @@ -/* - * Analog devices AD5360, AD5361, AD5362, AD5363, AD5370, AD5371, AD5373 - * multi-channel Digital to Analog Converters driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define AD5360_CMD(x) ((x) << 22) -#define AD5360_ADDR(x) ((x) << 16) - -#define AD5360_READBACK_TYPE(x) ((x) << 13) -#define AD5360_READBACK_ADDR(x) ((x) << 7) - -#define AD5360_CHAN_ADDR(chan) ((chan) + 0x8) - -#define AD5360_CMD_WRITE_DATA 0x3 -#define AD5360_CMD_WRITE_OFFSET 0x2 -#define AD5360_CMD_WRITE_GAIN 0x1 -#define AD5360_CMD_SPECIAL_FUNCTION 0x0 - -/* Special function register addresses */ -#define AD5360_REG_SF_NOP 0x0 -#define AD5360_REG_SF_CTRL 0x1 -#define AD5360_REG_SF_OFS(x) (0x2 + (x)) -#define AD5360_REG_SF_READBACK 0x5 - -#define AD5360_SF_CTRL_PWR_DOWN BIT(0) - -#define AD5360_READBACK_X1A 0x0 -#define AD5360_READBACK_X1B 0x1 -#define AD5360_READBACK_OFFSET 0x2 -#define AD5360_READBACK_GAIN 0x3 -#define AD5360_READBACK_SF 0x4 - - -/** - * struct ad5360_chip_info - chip specific information - * @channel_template: channel specification template - * @num_channels: number of channels - * @channels_per_group: number of channels per group - * @num_vrefs: number of vref supplies for the chip -*/ - -struct ad5360_chip_info { - struct iio_chan_spec channel_template; - unsigned int num_channels; - unsigned int channels_per_group; - unsigned int num_vrefs; -}; - -/** - * struct ad5360_state - driver instance specific data - * @spi: spi_device - * @chip_info: chip model specific constants, available modes etc - * @vref_reg: vref supply regulators - * @ctrl: control register cache - * @data: spi transfer buffers - */ - -struct ad5360_state { - struct spi_device *spi; - const struct ad5360_chip_info *chip_info; - struct regulator_bulk_data vref_reg[3]; - unsigned int ctrl; - - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - */ - union { - __be32 d32; - u8 d8[4]; - } data[2] ____cacheline_aligned; -}; - -enum ad5360_type { - ID_AD5360, - ID_AD5361, - ID_AD5362, - ID_AD5363, - ID_AD5370, - ID_AD5371, - ID_AD5372, - ID_AD5373, -}; - -#define AD5360_CHANNEL(bits) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \ - IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \ - IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \ - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \ - .scan_type = IIO_ST('u', (bits), 16, 16 - (bits)) \ -} - -static const struct ad5360_chip_info ad5360_chip_info_tbl[] = { - [ID_AD5360] = { - .channel_template = AD5360_CHANNEL(16), - .num_channels = 16, - .channels_per_group = 8, - .num_vrefs = 2, - }, - [ID_AD5361] = { - .channel_template = AD5360_CHANNEL(14), - .num_channels = 16, - .channels_per_group = 8, - .num_vrefs = 2, - }, - [ID_AD5362] = { - .channel_template = AD5360_CHANNEL(16), - .num_channels = 8, - .channels_per_group = 4, - .num_vrefs = 2, - }, - [ID_AD5363] = { - .channel_template = AD5360_CHANNEL(14), - .num_channels = 8, - .channels_per_group = 4, - .num_vrefs = 2, - }, - [ID_AD5370] = { - .channel_template = AD5360_CHANNEL(16), - .num_channels = 40, - .channels_per_group = 8, - .num_vrefs = 2, - }, - [ID_AD5371] = { - .channel_template = AD5360_CHANNEL(14), - .num_channels = 40, - .channels_per_group = 8, - .num_vrefs = 3, - }, - [ID_AD5372] = { - .channel_template = AD5360_CHANNEL(16), - .num_channels = 32, - .channels_per_group = 8, - .num_vrefs = 2, - }, - [ID_AD5373] = { - .channel_template = AD5360_CHANNEL(14), - .num_channels = 32, - .channels_per_group = 8, - .num_vrefs = 2, - }, -}; - -static unsigned int ad5360_get_channel_vref_index(struct ad5360_state *st, - unsigned int channel) -{ - unsigned int i; - - /* The first groups have their own vref, while the remaining groups - * share the last vref */ - i = channel / st->chip_info->channels_per_group; - if (i >= st->chip_info->num_vrefs) - i = st->chip_info->num_vrefs - 1; - - return i; -} - -static int ad5360_get_channel_vref(struct ad5360_state *st, - unsigned int channel) -{ - unsigned int i = ad5360_get_channel_vref_index(st, channel); - - return regulator_get_voltage(st->vref_reg[i].consumer); -} - - -static int ad5360_write_unlocked(struct iio_dev *indio_dev, - unsigned int cmd, unsigned int addr, unsigned int val, - unsigned int shift) -{ - struct ad5360_state *st = iio_priv(indio_dev); - - val <<= shift; - val |= AD5360_CMD(cmd) | AD5360_ADDR(addr); - st->data[0].d32 = cpu_to_be32(val); - - return spi_write(st->spi, &st->data[0].d8[1], 3); -} - -static int ad5360_write(struct iio_dev *indio_dev, unsigned int cmd, - unsigned int addr, unsigned int val, unsigned int shift) -{ - int ret; - - mutex_lock(&indio_dev->mlock); - ret = ad5360_write_unlocked(indio_dev, cmd, addr, val, shift); - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static int ad5360_read(struct iio_dev *indio_dev, unsigned int type, - unsigned int addr) -{ - struct ad5360_state *st = iio_priv(indio_dev); - struct spi_message m; - int ret; - struct spi_transfer t[] = { - { - .tx_buf = &st->data[0].d8[1], - .len = 3, - .cs_change = 1, - }, { - .rx_buf = &st->data[1].d8[1], - .len = 3, - }, - }; - - spi_message_init(&m); - spi_message_add_tail(&t[0], &m); - spi_message_add_tail(&t[1], &m); - - mutex_lock(&indio_dev->mlock); - - st->data[0].d32 = cpu_to_be32(AD5360_CMD(AD5360_CMD_SPECIAL_FUNCTION) | - AD5360_ADDR(AD5360_REG_SF_READBACK) | - AD5360_READBACK_TYPE(type) | - AD5360_READBACK_ADDR(addr)); - - ret = spi_sync(st->spi, &m); - if (ret >= 0) - ret = be32_to_cpu(st->data[1].d32) & 0xffff; - - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static ssize_t ad5360_read_dac_powerdown(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad5360_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", (bool)(st->ctrl & AD5360_SF_CTRL_PWR_DOWN)); -} - -static int ad5360_update_ctrl(struct iio_dev *indio_dev, unsigned int set, - unsigned int clr) -{ - struct ad5360_state *st = iio_priv(indio_dev); - unsigned int ret; - - mutex_lock(&indio_dev->mlock); - - st->ctrl |= set; - st->ctrl &= ~clr; - - ret = ad5360_write_unlocked(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, - AD5360_REG_SF_CTRL, st->ctrl, 0); - - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static ssize_t ad5360_write_dac_powerdown(struct device *dev, - struct device_attribute *attr, const char *buf, size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - bool pwr_down; - int ret; - - ret = strtobool(buf, &pwr_down); - if (ret) - return ret; - - if (pwr_down) - ret = ad5360_update_ctrl(indio_dev, AD5360_SF_CTRL_PWR_DOWN, 0); - else - ret = ad5360_update_ctrl(indio_dev, 0, AD5360_SF_CTRL_PWR_DOWN); - - return ret ? ret : len; -} - -static IIO_DEVICE_ATTR(out_voltage_powerdown, - S_IRUGO | S_IWUSR, - ad5360_read_dac_powerdown, - ad5360_write_dac_powerdown, 0); - -static struct attribute *ad5360_attributes[] = { - &iio_dev_attr_out_voltage_powerdown.dev_attr.attr, - NULL, -}; - -static const struct attribute_group ad5360_attribute_group = { - .attrs = ad5360_attributes, -}; - -static int ad5360_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct ad5360_state *st = iio_priv(indio_dev); - int max_val = (1 << chan->scan_type.realbits); - unsigned int ofs_index; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (val >= max_val || val < 0) - return -EINVAL; - - return ad5360_write(indio_dev, AD5360_CMD_WRITE_DATA, - chan->address, val, chan->scan_type.shift); - - case IIO_CHAN_INFO_CALIBBIAS: - if (val >= max_val || val < 0) - return -EINVAL; - - return ad5360_write(indio_dev, AD5360_CMD_WRITE_OFFSET, - chan->address, val, chan->scan_type.shift); - - case IIO_CHAN_INFO_CALIBSCALE: - if (val >= max_val || val < 0) - return -EINVAL; - - return ad5360_write(indio_dev, AD5360_CMD_WRITE_GAIN, - chan->address, val, chan->scan_type.shift); - - case IIO_CHAN_INFO_OFFSET: - if (val <= -max_val || val > 0) - return -EINVAL; - - val = -val; - - /* offset is supposed to have the same scale as raw, but it - * is always 14bits wide, so on a chip where the raw value has - * more bits, we need to shift offset. */ - val >>= (chan->scan_type.realbits - 14); - - /* There is one DAC offset register per vref. Changing one - * channels offset will also change the offset for all other - * channels which share the same vref supply. */ - ofs_index = ad5360_get_channel_vref_index(st, chan->channel); - return ad5360_write(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, - AD5360_REG_SF_OFS(ofs_index), val, 0); - default: - break; - } - - return -EINVAL; -} - -static int ad5360_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad5360_state *st = iio_priv(indio_dev); - unsigned int ofs_index; - int scale_uv; - int ret; - - switch (m) { - case IIO_CHAN_INFO_RAW: - ret = ad5360_read(indio_dev, AD5360_READBACK_X1A, - chan->address); - if (ret < 0) - return ret; - *val = ret >> chan->scan_type.shift; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - /* vout = 4 * vref * dac_code */ - scale_uv = ad5360_get_channel_vref(st, chan->channel) * 4 * 100; - if (scale_uv < 0) - return scale_uv; - - scale_uv >>= (chan->scan_type.realbits); - *val = scale_uv / 100000; - *val2 = (scale_uv % 100000) * 10; - return IIO_VAL_INT_PLUS_MICRO; - case IIO_CHAN_INFO_CALIBBIAS: - ret = ad5360_read(indio_dev, AD5360_READBACK_OFFSET, - chan->address); - if (ret < 0) - return ret; - *val = ret; - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBSCALE: - ret = ad5360_read(indio_dev, AD5360_READBACK_GAIN, - chan->address); - if (ret < 0) - return ret; - *val = ret; - return IIO_VAL_INT; - case IIO_CHAN_INFO_OFFSET: - ofs_index = ad5360_get_channel_vref_index(st, chan->channel); - ret = ad5360_read(indio_dev, AD5360_READBACK_SF, - AD5360_REG_SF_OFS(ofs_index)); - if (ret < 0) - return ret; - - ret <<= (chan->scan_type.realbits - 14); - *val = -ret; - return IIO_VAL_INT; - } - - return -EINVAL; -} - -static const struct iio_info ad5360_info = { - .read_raw = ad5360_read_raw, - .write_raw = ad5360_write_raw, - .attrs = &ad5360_attribute_group, - .driver_module = THIS_MODULE, -}; - -static const char * const ad5360_vref_name[] = { - "vref0", "vref1", "vref2" -}; - -static int __devinit ad5360_alloc_channels(struct iio_dev *indio_dev) -{ - struct ad5360_state *st = iio_priv(indio_dev); - struct iio_chan_spec *channels; - unsigned int i; - - channels = kcalloc(st->chip_info->num_channels, - sizeof(struct iio_chan_spec), GFP_KERNEL); - - if (!channels) - return -ENOMEM; - - for (i = 0; i < st->chip_info->num_channels; ++i) { - channels[i] = st->chip_info->channel_template; - channels[i].channel = i; - channels[i].address = AD5360_CHAN_ADDR(i); - } - - indio_dev->channels = channels; - - return 0; -} - -static int __devinit ad5360_probe(struct spi_device *spi) -{ - enum ad5360_type type = spi_get_device_id(spi)->driver_data; - struct iio_dev *indio_dev; - struct ad5360_state *st; - unsigned int i; - int ret; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - dev_err(&spi->dev, "Failed to allocate iio device\n"); - return -ENOMEM; - } - - st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); - - st->chip_info = &ad5360_chip_info_tbl[type]; - st->spi = spi; - - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->info = &ad5360_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->num_channels = st->chip_info->num_channels; - - ret = ad5360_alloc_channels(indio_dev); - if (ret) { - dev_err(&spi->dev, "Failed to allocate channel spec: %d\n", ret); - goto error_free; - } - - for (i = 0; i < st->chip_info->num_vrefs; ++i) - st->vref_reg[i].supply = ad5360_vref_name[i]; - - ret = regulator_bulk_get(&st->spi->dev, st->chip_info->num_vrefs, - st->vref_reg); - if (ret) { - dev_err(&spi->dev, "Failed to request vref regulators: %d\n", ret); - goto error_free_channels; - } - - ret = regulator_bulk_enable(st->chip_info->num_vrefs, st->vref_reg); - if (ret) { - dev_err(&spi->dev, "Failed to enable vref regulators: %d\n", ret); - goto error_free_reg; - } - - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); - goto error_disable_reg; - } - - return 0; - -error_disable_reg: - regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg); -error_free_reg: - regulator_bulk_free(st->chip_info->num_vrefs, st->vref_reg); -error_free_channels: - kfree(indio_dev->channels); -error_free: - iio_device_free(indio_dev); - - return ret; -} - -static int __devexit ad5360_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5360_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - kfree(indio_dev->channels); - - regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg); - regulator_bulk_free(st->chip_info->num_vrefs, st->vref_reg); - - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5360_ids[] = { - { "ad5360", ID_AD5360 }, - { "ad5361", ID_AD5361 }, - { "ad5362", ID_AD5362 }, - { "ad5363", ID_AD5363 }, - { "ad5370", ID_AD5370 }, - { "ad5371", ID_AD5371 }, - { "ad5372", ID_AD5372 }, - { "ad5373", ID_AD5373 }, - {} -}; -MODULE_DEVICE_TABLE(spi, ad5360_ids); - -static struct spi_driver ad5360_driver = { - .driver = { - .name = "ad5360", - .owner = THIS_MODULE, - }, - .probe = ad5360_probe, - .remove = __devexit_p(ad5360_remove), - .id_table = ad5360_ids, -}; -module_spi_driver(ad5360_driver); - -MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_DESCRIPTION("Analog Devices AD5360/61/62/63/70/71/72/73 DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5380.c b/drivers/staging/iio/dac/ad5380.c deleted file mode 100644 index 5dfb4451728f..000000000000 --- a/drivers/staging/iio/dac/ad5380.c +++ /dev/null @@ -1,657 +0,0 @@ -/* - * Analog devices AD5380, AD5381, AD5382, AD5383, AD5390, AD5391, AD5392 - * multi-channel Digital to Analog Converters driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define AD5380_REG_DATA(x) (((x) << 2) | 3) -#define AD5380_REG_OFFSET(x) (((x) << 2) | 2) -#define AD5380_REG_GAIN(x) (((x) << 2) | 1) -#define AD5380_REG_SF_PWR_DOWN (8 << 2) -#define AD5380_REG_SF_PWR_UP (9 << 2) -#define AD5380_REG_SF_CTRL (12 << 2) - -#define AD5380_CTRL_PWR_DOWN_MODE_OFFSET 13 -#define AD5380_CTRL_INT_VREF_2V5 BIT(12) -#define AD5380_CTRL_INT_VREF_EN BIT(10) - -/** - * struct ad5380_chip_info - chip specific information - * @channel_template: channel specification template - * @num_channels: number of channels - * @int_vref: internal vref in uV -*/ - -struct ad5380_chip_info { - struct iio_chan_spec channel_template; - unsigned int num_channels; - unsigned int int_vref; -}; - -/** - * struct ad5380_state - driver instance specific data - * @regmap: regmap instance used by the device - * @chip_info: chip model specific constants, available modes etc - * @vref_reg: vref supply regulator - * @vref: actual reference voltage used in uA - * @pwr_down: whether the chip is currently in power down mode - */ - -struct ad5380_state { - struct regmap *regmap; - const struct ad5380_chip_info *chip_info; - struct regulator *vref_reg; - int vref; - bool pwr_down; -}; - -enum ad5380_type { - ID_AD5380_3, - ID_AD5380_5, - ID_AD5381_3, - ID_AD5381_5, - ID_AD5382_3, - ID_AD5382_5, - ID_AD5383_3, - ID_AD5383_5, - ID_AD5390_3, - ID_AD5390_5, - ID_AD5391_3, - ID_AD5391_5, - ID_AD5392_3, - ID_AD5392_5, -}; - -static ssize_t ad5380_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, char *buf) -{ - struct ad5380_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", st->pwr_down); -} - -static ssize_t ad5380_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, - size_t len) -{ - struct ad5380_state *st = iio_priv(indio_dev); - bool pwr_down; - int ret; - - ret = strtobool(buf, &pwr_down); - if (ret) - return ret; - - mutex_lock(&indio_dev->mlock); - - if (pwr_down) - ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_DOWN, 0); - else - ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_UP, 0); - - st->pwr_down = pwr_down; - - mutex_unlock(&indio_dev->mlock); - - return ret ? ret : len; -} - -static const char * const ad5380_powerdown_modes[] = { - "100kohm_to_gnd", - "three_state", -}; - -static int ad5380_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct ad5380_state *st = iio_priv(indio_dev); - unsigned int mode; - int ret; - - ret = regmap_read(st->regmap, AD5380_REG_SF_CTRL, &mode); - if (ret) - return ret; - - mode = (mode >> AD5380_CTRL_PWR_DOWN_MODE_OFFSET) & 1; - - return mode; -} - -static int ad5380_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) -{ - struct ad5380_state *st = iio_priv(indio_dev); - int ret; - - ret = regmap_update_bits(st->regmap, AD5380_REG_SF_CTRL, - 1 << AD5380_CTRL_PWR_DOWN_MODE_OFFSET, - mode << AD5380_CTRL_PWR_DOWN_MODE_OFFSET); - - return ret; -} - -static const struct iio_enum ad5380_powerdown_mode_enum = { - .items = ad5380_powerdown_modes, - .num_items = ARRAY_SIZE(ad5380_powerdown_modes), - .get = ad5380_get_powerdown_mode, - .set = ad5380_set_powerdown_mode, -}; - -static unsigned int ad5380_info_to_reg(struct iio_chan_spec const *chan, - long info) -{ - switch (info) { - case 0: - return AD5380_REG_DATA(chan->address); - case IIO_CHAN_INFO_CALIBBIAS: - return AD5380_REG_OFFSET(chan->address); - case IIO_CHAN_INFO_CALIBSCALE: - return AD5380_REG_GAIN(chan->address); - default: - break; - } - - return 0; -} - -static int ad5380_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, long info) -{ - const unsigned int max_val = (1 << chan->scan_type.realbits); - struct ad5380_state *st = iio_priv(indio_dev); - - switch (info) { - case IIO_CHAN_INFO_RAW: - case IIO_CHAN_INFO_CALIBSCALE: - if (val >= max_val || val < 0) - return -EINVAL; - - return regmap_write(st->regmap, - ad5380_info_to_reg(chan, info), - val << chan->scan_type.shift); - case IIO_CHAN_INFO_CALIBBIAS: - val += (1 << chan->scan_type.realbits) / 2; - if (val >= max_val || val < 0) - return -EINVAL; - - return regmap_write(st->regmap, - AD5380_REG_OFFSET(chan->address), - val << chan->scan_type.shift); - default: - break; - } - return -EINVAL; -} - -static int ad5380_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int *val, int *val2, long info) -{ - struct ad5380_state *st = iio_priv(indio_dev); - unsigned long scale_uv; - int ret; - - switch (info) { - case IIO_CHAN_INFO_RAW: - case IIO_CHAN_INFO_CALIBSCALE: - ret = regmap_read(st->regmap, ad5380_info_to_reg(chan, info), - val); - if (ret) - return ret; - *val >>= chan->scan_type.shift; - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBBIAS: - ret = regmap_read(st->regmap, AD5380_REG_OFFSET(chan->address), - val); - if (ret) - return ret; - *val >>= chan->scan_type.shift; - val -= (1 << chan->scan_type.realbits) / 2; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - scale_uv = ((2 * st->vref) >> chan->scan_type.realbits) * 100; - *val = scale_uv / 100000; - *val2 = (scale_uv % 100000) * 10; - return IIO_VAL_INT_PLUS_MICRO; - default: - break; - } - - return -EINVAL; -} - -static const struct iio_info ad5380_info = { - .read_raw = ad5380_read_raw, - .write_raw = ad5380_write_raw, - .driver_module = THIS_MODULE, -}; - -static struct iio_chan_spec_ext_info ad5380_ext_info[] = { - { - .name = "powerdown", - .read = ad5380_read_dac_powerdown, - .write = ad5380_write_dac_powerdown, - }, - IIO_ENUM("powerdown_mode", true, &ad5380_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5380_powerdown_mode_enum), - { }, -}; - -#define AD5380_CHANNEL(_bits) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT | \ - IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \ - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \ - .scan_type = IIO_ST('u', (_bits), 16, 14 - (_bits)), \ - .ext_info = ad5380_ext_info, \ -} - -static const struct ad5380_chip_info ad5380_chip_info_tbl[] = { - [ID_AD5380_3] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 40, - .int_vref = 1250000, - }, - [ID_AD5380_5] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 40, - .int_vref = 2500000, - }, - [ID_AD5381_3] = { - .channel_template = AD5380_CHANNEL(12), - .num_channels = 16, - .int_vref = 1250000, - }, - [ID_AD5381_5] = { - .channel_template = AD5380_CHANNEL(12), - .num_channels = 16, - .int_vref = 2500000, - }, - [ID_AD5382_3] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 32, - .int_vref = 1250000, - }, - [ID_AD5382_5] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 32, - .int_vref = 2500000, - }, - [ID_AD5383_3] = { - .channel_template = AD5380_CHANNEL(12), - .num_channels = 32, - .int_vref = 1250000, - }, - [ID_AD5383_5] = { - .channel_template = AD5380_CHANNEL(12), - .num_channels = 32, - .int_vref = 2500000, - }, - [ID_AD5390_3] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 16, - .int_vref = 1250000, - }, - [ID_AD5390_5] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 16, - .int_vref = 2500000, - }, - [ID_AD5391_3] = { - .channel_template = AD5380_CHANNEL(12), - .num_channels = 16, - .int_vref = 1250000, - }, - [ID_AD5391_5] = { - .channel_template = AD5380_CHANNEL(12), - .num_channels = 16, - .int_vref = 2500000, - }, - [ID_AD5392_3] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 8, - .int_vref = 1250000, - }, - [ID_AD5392_5] = { - .channel_template = AD5380_CHANNEL(14), - .num_channels = 8, - .int_vref = 2500000, - }, -}; - -static int __devinit ad5380_alloc_channels(struct iio_dev *indio_dev) -{ - struct ad5380_state *st = iio_priv(indio_dev); - struct iio_chan_spec *channels; - unsigned int i; - - channels = kcalloc(st->chip_info->num_channels, - sizeof(struct iio_chan_spec), GFP_KERNEL); - - if (!channels) - return -ENOMEM; - - for (i = 0; i < st->chip_info->num_channels; ++i) { - channels[i] = st->chip_info->channel_template; - channels[i].channel = i; - channels[i].address = i; - } - - indio_dev->channels = channels; - - return 0; -} - -static int __devinit ad5380_probe(struct device *dev, struct regmap *regmap, - enum ad5380_type type, const char *name) -{ - struct iio_dev *indio_dev; - struct ad5380_state *st; - unsigned int ctrl = 0; - int ret; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - dev_err(dev, "Failed to allocate iio device\n"); - ret = -ENOMEM; - goto error_regmap_exit; - } - - st = iio_priv(indio_dev); - dev_set_drvdata(dev, indio_dev); - - st->chip_info = &ad5380_chip_info_tbl[type]; - st->regmap = regmap; - - indio_dev->dev.parent = dev; - indio_dev->name = name; - indio_dev->info = &ad5380_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->num_channels = st->chip_info->num_channels; - - ret = ad5380_alloc_channels(indio_dev); - if (ret) { - dev_err(dev, "Failed to allocate channel spec: %d\n", ret); - goto error_free; - } - - if (st->chip_info->int_vref == 2500000) - ctrl |= AD5380_CTRL_INT_VREF_2V5; - - st->vref_reg = regulator_get(dev, "vref"); - if (!IS_ERR(st->vref_reg)) { - ret = regulator_enable(st->vref_reg); - if (ret) { - dev_err(dev, "Failed to enable vref regulators: %d\n", - ret); - goto error_free_reg; - } - - st->vref = regulator_get_voltage(st->vref_reg); - } else { - st->vref = st->chip_info->int_vref; - ctrl |= AD5380_CTRL_INT_VREF_EN; - } - - ret = regmap_write(st->regmap, AD5380_REG_SF_CTRL, ctrl); - if (ret) { - dev_err(dev, "Failed to write to device: %d\n", ret); - goto error_disable_reg; - } - - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(dev, "Failed to register iio device: %d\n", ret); - goto error_disable_reg; - } - - return 0; - -error_disable_reg: - if (!IS_ERR(st->vref_reg)) - regulator_disable(st->vref_reg); -error_free_reg: - if (!IS_ERR(st->vref_reg)) - regulator_put(st->vref_reg); - - kfree(indio_dev->channels); -error_free: - iio_device_free(indio_dev); -error_regmap_exit: - regmap_exit(regmap); - - return ret; -} - -static int __devexit ad5380_remove(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct ad5380_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - kfree(indio_dev->channels); - - if (!IS_ERR(st->vref_reg)) { - regulator_disable(st->vref_reg); - regulator_put(st->vref_reg); - } - - regmap_exit(st->regmap); - iio_device_free(indio_dev); - - return 0; -} - -static bool ad5380_reg_false(struct device *dev, unsigned int reg) -{ - return false; -} - -static const struct regmap_config ad5380_regmap_config = { - .reg_bits = 10, - .val_bits = 14, - - .max_register = AD5380_REG_DATA(40), - .cache_type = REGCACHE_RBTREE, - - .volatile_reg = ad5380_reg_false, - .readable_reg = ad5380_reg_false, -}; - -#if IS_ENABLED(CONFIG_SPI_MASTER) - -static int __devinit ad5380_spi_probe(struct spi_device *spi) -{ - const struct spi_device_id *id = spi_get_device_id(spi); - struct regmap *regmap; - - regmap = regmap_init_spi(spi, &ad5380_regmap_config); - - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - return ad5380_probe(&spi->dev, regmap, id->driver_data, id->name); -} - -static int __devexit ad5380_spi_remove(struct spi_device *spi) -{ - return ad5380_remove(&spi->dev); -} - -static const struct spi_device_id ad5380_spi_ids[] = { - { "ad5380-3", ID_AD5380_3 }, - { "ad5380-5", ID_AD5380_5 }, - { "ad5381-3", ID_AD5381_3 }, - { "ad5381-5", ID_AD5381_5 }, - { "ad5382-3", ID_AD5382_3 }, - { "ad5382-5", ID_AD5382_5 }, - { "ad5383-3", ID_AD5383_3 }, - { "ad5383-5", ID_AD5383_5 }, - { "ad5384-3", ID_AD5380_3 }, - { "ad5384-5", ID_AD5380_5 }, - { "ad5390-3", ID_AD5390_3 }, - { "ad5390-5", ID_AD5390_5 }, - { "ad5391-3", ID_AD5391_3 }, - { "ad5391-5", ID_AD5391_5 }, - { "ad5392-3", ID_AD5392_3 }, - { "ad5392-5", ID_AD5392_5 }, - { } -}; -MODULE_DEVICE_TABLE(spi, ad5380_spi_ids); - -static struct spi_driver ad5380_spi_driver = { - .driver = { - .name = "ad5380", - .owner = THIS_MODULE, - }, - .probe = ad5380_spi_probe, - .remove = __devexit_p(ad5380_spi_remove), - .id_table = ad5380_spi_ids, -}; - -static inline int ad5380_spi_register_driver(void) -{ - return spi_register_driver(&ad5380_spi_driver); -} - -static inline void ad5380_spi_unregister_driver(void) -{ - spi_unregister_driver(&ad5380_spi_driver); -} - -#else - -static inline int ad5380_spi_register_driver(void) -{ - return 0; -} - -static inline void ad5380_spi_unregister_driver(void) -{ -} - -#endif - -#if IS_ENABLED(CONFIG_I2C) - -static int __devinit ad5380_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - struct regmap *regmap; - - regmap = regmap_init_i2c(i2c, &ad5380_regmap_config); - - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - return ad5380_probe(&i2c->dev, regmap, id->driver_data, id->name); -} - -static int __devexit ad5380_i2c_remove(struct i2c_client *i2c) -{ - return ad5380_remove(&i2c->dev); -} - -static const struct i2c_device_id ad5380_i2c_ids[] = { - { "ad5380-3", ID_AD5380_3 }, - { "ad5380-5", ID_AD5380_5 }, - { "ad5381-3", ID_AD5381_3 }, - { "ad5381-5", ID_AD5381_5 }, - { "ad5382-3", ID_AD5382_3 }, - { "ad5382-5", ID_AD5382_5 }, - { "ad5383-3", ID_AD5383_3 }, - { "ad5383-5", ID_AD5383_5 }, - { "ad5384-3", ID_AD5380_3 }, - { "ad5384-5", ID_AD5380_5 }, - { "ad5390-3", ID_AD5390_3 }, - { "ad5390-5", ID_AD5390_5 }, - { "ad5391-3", ID_AD5391_3 }, - { "ad5391-5", ID_AD5391_5 }, - { "ad5392-3", ID_AD5392_3 }, - { "ad5392-5", ID_AD5392_5 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ad5380_i2c_ids); - -static struct i2c_driver ad5380_i2c_driver = { - .driver = { - .name = "ad5380", - .owner = THIS_MODULE, - }, - .probe = ad5380_i2c_probe, - .remove = __devexit_p(ad5380_i2c_remove), - .id_table = ad5380_i2c_ids, -}; - -static inline int ad5380_i2c_register_driver(void) -{ - return i2c_add_driver(&ad5380_i2c_driver); -} - -static inline void ad5380_i2c_unregister_driver(void) -{ - i2c_del_driver(&ad5380_i2c_driver); -} - -#else - -static inline int ad5380_i2c_register_driver(void) -{ - return 0; -} - -static inline void ad5380_i2c_unregister_driver(void) -{ -} - -#endif - -static int __init ad5380_spi_init(void) -{ - int ret; - - ret = ad5380_spi_register_driver(); - if (ret) - return ret; - - ret = ad5380_i2c_register_driver(); - if (ret) { - ad5380_spi_unregister_driver(); - return ret; - } - - return 0; -} -module_init(ad5380_spi_init); - -static void __exit ad5380_spi_exit(void) -{ - ad5380_i2c_unregister_driver(); - ad5380_spi_unregister_driver(); - -} -module_exit(ad5380_spi_exit); - -MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_DESCRIPTION("Analog Devices AD5380/81/82/83/84/90/91/92 DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5421.c b/drivers/staging/iio/dac/ad5421.c deleted file mode 100644 index ea2f83b4e357..000000000000 --- a/drivers/staging/iio/dac/ad5421.c +++ /dev/null @@ -1,544 +0,0 @@ -/* - * AD5421 Digital to analog converters driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "ad5421.h" - - -#define AD5421_REG_DAC_DATA 0x1 -#define AD5421_REG_CTRL 0x2 -#define AD5421_REG_OFFSET 0x3 -#define AD5421_REG_GAIN 0x4 -/* load dac and fault shared the same register number. Writing to it will cause - * a dac load command, reading from it will return the fault status register */ -#define AD5421_REG_LOAD_DAC 0x5 -#define AD5421_REG_FAULT 0x5 -#define AD5421_REG_FORCE_ALARM_CURRENT 0x6 -#define AD5421_REG_RESET 0x7 -#define AD5421_REG_START_CONVERSION 0x8 -#define AD5421_REG_NOOP 0x9 - -#define AD5421_CTRL_WATCHDOG_DISABLE BIT(12) -#define AD5421_CTRL_AUTO_FAULT_READBACK BIT(11) -#define AD5421_CTRL_MIN_CURRENT BIT(9) -#define AD5421_CTRL_ADC_SOURCE_TEMP BIT(8) -#define AD5421_CTRL_ADC_ENABLE BIT(7) -#define AD5421_CTRL_PWR_DOWN_INT_VREF BIT(6) - -#define AD5421_FAULT_SPI BIT(15) -#define AD5421_FAULT_PEC BIT(14) -#define AD5421_FAULT_OVER_CURRENT BIT(13) -#define AD5421_FAULT_UNDER_CURRENT BIT(12) -#define AD5421_FAULT_TEMP_OVER_140 BIT(11) -#define AD5421_FAULT_TEMP_OVER_100 BIT(10) -#define AD5421_FAULT_UNDER_VOLTAGE_6V BIT(9) -#define AD5421_FAULT_UNDER_VOLTAGE_12V BIT(8) - -/* These bits will cause the fault pin to go high */ -#define AD5421_FAULT_TRIGGER_IRQ \ - (AD5421_FAULT_SPI | AD5421_FAULT_PEC | AD5421_FAULT_OVER_CURRENT | \ - AD5421_FAULT_UNDER_CURRENT | AD5421_FAULT_TEMP_OVER_140) - -/** - * struct ad5421_state - driver instance specific data - * @spi: spi_device - * @ctrl: control register cache - * @current_range: current range which the device is configured for - * @data: spi transfer buffers - * @fault_mask: software masking of events - */ -struct ad5421_state { - struct spi_device *spi; - unsigned int ctrl; - enum ad5421_current_range current_range; - unsigned int fault_mask; - - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - */ - union { - u32 d32; - u8 d8[4]; - } data[2] ____cacheline_aligned; -}; - -static const struct iio_chan_spec ad5421_channels[] = { - { - .type = IIO_CURRENT, - .indexed = 1, - .output = 1, - .channel = 0, - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | - IIO_CHAN_INFO_SCALE_SHARED_BIT | - IIO_CHAN_INFO_OFFSET_SHARED_BIT | - IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, - .scan_type = IIO_ST('u', 16, 16, 0), - .event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | - IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), - }, - { - .type = IIO_TEMP, - .channel = -1, - .event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - }, -}; - -static int ad5421_write_unlocked(struct iio_dev *indio_dev, - unsigned int reg, unsigned int val) -{ - struct ad5421_state *st = iio_priv(indio_dev); - - st->data[0].d32 = cpu_to_be32((reg << 16) | val); - - return spi_write(st->spi, &st->data[0].d8[1], 3); -} - -static int ad5421_write(struct iio_dev *indio_dev, unsigned int reg, - unsigned int val) -{ - int ret; - - mutex_lock(&indio_dev->mlock); - ret = ad5421_write_unlocked(indio_dev, reg, val); - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static int ad5421_read(struct iio_dev *indio_dev, unsigned int reg) -{ - struct ad5421_state *st = iio_priv(indio_dev); - struct spi_message m; - int ret; - struct spi_transfer t[] = { - { - .tx_buf = &st->data[0].d8[1], - .len = 3, - .cs_change = 1, - }, { - .rx_buf = &st->data[1].d8[1], - .len = 3, - }, - }; - - spi_message_init(&m); - spi_message_add_tail(&t[0], &m); - spi_message_add_tail(&t[1], &m); - - mutex_lock(&indio_dev->mlock); - - st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16)); - - ret = spi_sync(st->spi, &m); - if (ret >= 0) - ret = be32_to_cpu(st->data[1].d32) & 0xffff; - - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static int ad5421_update_ctrl(struct iio_dev *indio_dev, unsigned int set, - unsigned int clr) -{ - struct ad5421_state *st = iio_priv(indio_dev); - unsigned int ret; - - mutex_lock(&indio_dev->mlock); - - st->ctrl &= ~clr; - st->ctrl |= set; - - ret = ad5421_write_unlocked(indio_dev, AD5421_REG_CTRL, st->ctrl); - - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static irqreturn_t ad5421_fault_handler(int irq, void *data) -{ - struct iio_dev *indio_dev = data; - struct ad5421_state *st = iio_priv(indio_dev); - unsigned int fault; - unsigned int old_fault = 0; - unsigned int events; - - fault = ad5421_read(indio_dev, AD5421_REG_FAULT); - if (!fault) - return IRQ_NONE; - - /* If we had a fault, this might mean that the DAC has lost its state - * and has been reset. Make sure that the control register actually - * contains what we expect it to contain. Otherwise the watchdog might - * be enabled and we get watchdog timeout faults, which will render the - * DAC unusable. */ - ad5421_update_ctrl(indio_dev, 0, 0); - - - /* The fault pin stays high as long as a fault condition is present and - * it is not possible to mask fault conditions. For certain fault - * conditions for example like over-temperature it takes some time - * until the fault condition disappears. If we would exit the interrupt - * handler immediately after handling the event it would be entered - * again instantly. Thus we fall back to polling in case we detect that - * a interrupt condition is still present. - */ - do { - /* 0xffff is a invalid value for the register and will only be - * read if there has been a communication error */ - if (fault == 0xffff) - fault = 0; - - /* we are only interested in new events */ - events = (old_fault ^ fault) & fault; - events &= st->fault_mask; - - if (events & AD5421_FAULT_OVER_CURRENT) { - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_CURRENT, - 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), - iio_get_time_ns()); - } - - if (events & AD5421_FAULT_UNDER_CURRENT) { - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_CURRENT, - 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_FALLING), - iio_get_time_ns()); - } - - if (events & AD5421_FAULT_TEMP_OVER_140) { - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_TEMP, - 0, - IIO_EV_TYPE_MAG, - IIO_EV_DIR_RISING), - iio_get_time_ns()); - } - - old_fault = fault; - fault = ad5421_read(indio_dev, AD5421_REG_FAULT); - - /* still active? go to sleep for some time */ - if (fault & AD5421_FAULT_TRIGGER_IRQ) - msleep(1000); - - } while (fault & AD5421_FAULT_TRIGGER_IRQ); - - - return IRQ_HANDLED; -} - -static void ad5421_get_current_min_max(struct ad5421_state *st, - unsigned int *min, unsigned int *max) -{ - /* The current range is configured using external pins, which are - * usually hard-wired and not run-time switchable. */ - switch (st->current_range) { - case AD5421_CURRENT_RANGE_4mA_20mA: - *min = 4000; - *max = 20000; - break; - case AD5421_CURRENT_RANGE_3mA8_21mA: - *min = 3800; - *max = 21000; - break; - case AD5421_CURRENT_RANGE_3mA2_24mA: - *min = 3200; - *max = 24000; - break; - default: - *min = 0; - *max = 1; - break; - } -} - -static inline unsigned int ad5421_get_offset(struct ad5421_state *st) -{ - unsigned int min, max; - - ad5421_get_current_min_max(st, &min, &max); - return (min * (1 << 16)) / (max - min); -} - -static inline unsigned int ad5421_get_scale(struct ad5421_state *st) -{ - unsigned int min, max; - - ad5421_get_current_min_max(st, &min, &max); - return ((max - min) * 1000) / (1 << 16); -} - -static int ad5421_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int *val, int *val2, long m) -{ - struct ad5421_state *st = iio_priv(indio_dev); - int ret; - - if (chan->type != IIO_CURRENT) - return -EINVAL; - - switch (m) { - case IIO_CHAN_INFO_RAW: - ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA); - if (ret < 0) - return ret; - *val = ret; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - *val = 0; - *val2 = ad5421_get_scale(st); - return IIO_VAL_INT_PLUS_MICRO; - case IIO_CHAN_INFO_OFFSET: - *val = ad5421_get_offset(st); - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBBIAS: - ret = ad5421_read(indio_dev, AD5421_REG_OFFSET); - if (ret < 0) - return ret; - *val = ret - 32768; - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBSCALE: - ret = ad5421_read(indio_dev, AD5421_REG_GAIN); - if (ret < 0) - return ret; - *val = ret; - return IIO_VAL_INT; - } - - return -EINVAL; -} - -static int ad5421_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, long mask) -{ - const unsigned int max_val = 1 << 16; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (val >= max_val || val < 0) - return -EINVAL; - - return ad5421_write(indio_dev, AD5421_REG_DAC_DATA, val); - case IIO_CHAN_INFO_CALIBBIAS: - val += 32768; - if (val >= max_val || val < 0) - return -EINVAL; - - return ad5421_write(indio_dev, AD5421_REG_OFFSET, val); - case IIO_CHAN_INFO_CALIBSCALE: - if (val >= max_val || val < 0) - return -EINVAL; - - return ad5421_write(indio_dev, AD5421_REG_GAIN, val); - default: - break; - } - - return -EINVAL; -} - -static int ad5421_write_event_config(struct iio_dev *indio_dev, - u64 event_code, int state) -{ - struct ad5421_state *st = iio_priv(indio_dev); - unsigned int mask; - - switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { - case IIO_CURRENT: - if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == - IIO_EV_DIR_RISING) - mask = AD5421_FAULT_OVER_CURRENT; - else - mask = AD5421_FAULT_UNDER_CURRENT; - break; - case IIO_TEMP: - mask = AD5421_FAULT_TEMP_OVER_140; - break; - default: - return -EINVAL; - } - - mutex_lock(&indio_dev->mlock); - if (state) - st->fault_mask |= mask; - else - st->fault_mask &= ~mask; - mutex_unlock(&indio_dev->mlock); - - return 0; -} - -static int ad5421_read_event_config(struct iio_dev *indio_dev, - u64 event_code) -{ - struct ad5421_state *st = iio_priv(indio_dev); - unsigned int mask; - - switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { - case IIO_CURRENT: - if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == - IIO_EV_DIR_RISING) - mask = AD5421_FAULT_OVER_CURRENT; - else - mask = AD5421_FAULT_UNDER_CURRENT; - break; - case IIO_TEMP: - mask = AD5421_FAULT_TEMP_OVER_140; - break; - default: - return -EINVAL; - } - - return (bool)(st->fault_mask & mask); -} - -static int ad5421_read_event_value(struct iio_dev *indio_dev, u64 event_code, - int *val) -{ - int ret; - - switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { - case IIO_CURRENT: - ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA); - if (ret < 0) - return ret; - *val = ret; - break; - case IIO_TEMP: - *val = 140000; - break; - default: - return -EINVAL; - } - - return 0; -} - -static const struct iio_info ad5421_info = { - .read_raw = ad5421_read_raw, - .write_raw = ad5421_write_raw, - .read_event_config = ad5421_read_event_config, - .write_event_config = ad5421_write_event_config, - .read_event_value = ad5421_read_event_value, - .driver_module = THIS_MODULE, -}; - -static int __devinit ad5421_probe(struct spi_device *spi) -{ - struct ad5421_platform_data *pdata = dev_get_platdata(&spi->dev); - struct iio_dev *indio_dev; - struct ad5421_state *st; - int ret; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - dev_err(&spi->dev, "Failed to allocate iio device\n"); - return -ENOMEM; - } - - st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); - - st->spi = spi; - - indio_dev->dev.parent = &spi->dev; - indio_dev->name = "ad5421"; - indio_dev->info = &ad5421_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = ad5421_channels; - indio_dev->num_channels = ARRAY_SIZE(ad5421_channels); - - st->ctrl = AD5421_CTRL_WATCHDOG_DISABLE | - AD5421_CTRL_AUTO_FAULT_READBACK; - - if (pdata) { - st->current_range = pdata->current_range; - if (pdata->external_vref) - st->ctrl |= AD5421_CTRL_PWR_DOWN_INT_VREF; - } else { - st->current_range = AD5421_CURRENT_RANGE_4mA_20mA; - } - - /* write initial ctrl register value */ - ad5421_update_ctrl(indio_dev, 0, 0); - - if (spi->irq) { - ret = request_threaded_irq(spi->irq, - NULL, - ad5421_fault_handler, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, - "ad5421 fault", - indio_dev); - if (ret) - goto error_free; - } - - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); - goto error_free_irq; - } - - return 0; - -error_free_irq: - if (spi->irq) - free_irq(spi->irq, indio_dev); -error_free: - iio_device_free(indio_dev); - - return ret; -} - -static int __devexit ad5421_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - - iio_device_unregister(indio_dev); - if (spi->irq) - free_irq(spi->irq, indio_dev); - iio_device_free(indio_dev); - - return 0; -} - -static struct spi_driver ad5421_driver = { - .driver = { - .name = "ad5421", - .owner = THIS_MODULE, - }, - .probe = ad5421_probe, - .remove = __devexit_p(ad5421_remove), -}; -module_spi_driver(ad5421_driver); - -MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_DESCRIPTION("Analog Devices AD5421 DAC"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("spi:ad5421"); diff --git a/drivers/staging/iio/dac/ad5421.h b/drivers/staging/iio/dac/ad5421.h deleted file mode 100644 index cd2bb84ff1b0..000000000000 --- a/drivers/staging/iio/dac/ad5421.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef __IIO_DAC_AD5421_H__ -#define __IIO_DAC_AD5421_H__ - -/* - * TODO: This file needs to go into include/linux/iio - */ - -/** - * enum ad5421_current_range - Current range the AD5421 is configured for. - * @AD5421_CURRENT_RANGE_4mA_20mA: 4 mA to 20 mA (RANGE1,0 pins = 00) - * @AD5421_CURRENT_RANGE_3mA8_21mA: 3.8 mA to 21 mA (RANGE1,0 pins = x1) - * @AD5421_CURRENT_RANGE_3mA2_24mA: 3.2 mA to 24 mA (RANGE1,0 pins = 10) - */ - -enum ad5421_current_range { - AD5421_CURRENT_RANGE_4mA_20mA, - AD5421_CURRENT_RANGE_3mA8_21mA, - AD5421_CURRENT_RANGE_3mA2_24mA, -}; - -/** - * struct ad5421_platform_data - AD5421 DAC driver platform data - * @external_vref: whether an external reference voltage is used or not - * @current_range: Current range the AD5421 is configured for - */ - -struct ad5421_platform_data { - bool external_vref; - enum ad5421_current_range current_range; -}; - -#endif diff --git a/drivers/staging/iio/dac/ad5446.c b/drivers/staging/iio/dac/ad5446.c deleted file mode 100644 index 49f557fc673b..000000000000 --- a/drivers/staging/iio/dac/ad5446.c +++ /dev/null @@ -1,381 +0,0 @@ -/* - * AD5446 SPI DAC driver - * - * Copyright 2010 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "ad5446.h" - -static int ad5446_write(struct ad5446_state *st, unsigned val) -{ - __be16 data = cpu_to_be16(val); - return spi_write(st->spi, &data, sizeof(data)); -} - -static int ad5660_write(struct ad5446_state *st, unsigned val) -{ - uint8_t data[3]; - - data[0] = (val >> 16) & 0xFF; - data[1] = (val >> 8) & 0xFF; - data[2] = val & 0xFF; - - return spi_write(st->spi, data, sizeof(data)); -} - -static const char * const ad5446_powerdown_modes[] = { - "1kohm_to_gnd", "100kohm_to_gnd", "three_state" -}; - -static int ad5446_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) -{ - struct ad5446_state *st = iio_priv(indio_dev); - - st->pwr_down_mode = mode + 1; - - return 0; -} - -static int ad5446_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct ad5446_state *st = iio_priv(indio_dev); - - return st->pwr_down_mode - 1; -} - -static const struct iio_enum ad5446_powerdown_mode_enum = { - .items = ad5446_powerdown_modes, - .num_items = ARRAY_SIZE(ad5446_powerdown_modes), - .get = ad5446_get_powerdown_mode, - .set = ad5446_set_powerdown_mode, -}; - -static ssize_t ad5446_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, - const struct iio_chan_spec *chan, - char *buf) -{ - struct ad5446_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", st->pwr_down); -} - -static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, - const struct iio_chan_spec *chan, - const char *buf, size_t len) -{ - struct ad5446_state *st = iio_priv(indio_dev); - unsigned int shift; - unsigned int val; - bool powerdown; - int ret; - - ret = strtobool(buf, &powerdown); - if (ret) - return ret; - - mutex_lock(&indio_dev->mlock); - st->pwr_down = powerdown; - - if (st->pwr_down) { - shift = chan->scan_type.realbits + chan->scan_type.shift; - val = st->pwr_down_mode << shift; - } else { - val = st->cached_val; - } - - ret = st->chip_info->write(st, val); - mutex_unlock(&indio_dev->mlock); - - return ret ? ret : len; -} - -static const struct iio_chan_spec_ext_info ad5064_ext_info_powerdown[] = { - { - .name = "powerdown", - .read = ad5446_read_dac_powerdown, - .write = ad5446_write_dac_powerdown, - }, - IIO_ENUM("powerdown_mode", false, &ad5446_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5446_powerdown_mode_enum), - { }, -}; - -#define _AD5446_CHANNEL(bits, storage, shift, ext) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .channel = 0, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT, \ - .scan_type = IIO_ST('u', (bits), (storage), (shift)), \ - .ext_info = (ext), \ -} - -#define AD5446_CHANNEL(bits, storage, shift) \ - _AD5446_CHANNEL(bits, storage, shift, NULL) - -#define AD5446_CHANNEL_POWERDOWN(bits, storage, shift) \ - _AD5446_CHANNEL(bits, storage, shift, ad5064_ext_info_powerdown) - -static const struct ad5446_chip_info ad5446_chip_info_tbl[] = { - [ID_AD5444] = { - .channel = AD5446_CHANNEL(12, 16, 2), - .write = ad5446_write, - }, - [ID_AD5446] = { - .channel = AD5446_CHANNEL(14, 16, 0), - .write = ad5446_write, - }, - [ID_AD5541A] = { - .channel = AD5446_CHANNEL(16, 16, 0), - .write = ad5446_write, - }, - [ID_AD5512A] = { - .channel = AD5446_CHANNEL(12, 16, 4), - .write = ad5446_write, - }, - [ID_AD5553] = { - .channel = AD5446_CHANNEL(14, 16, 0), - .write = ad5446_write, - }, - [ID_AD5601] = { - .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6), - .write = ad5446_write, - }, - [ID_AD5611] = { - .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 4), - .write = ad5446_write, - }, - [ID_AD5621] = { - .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), - .write = ad5446_write, - }, - [ID_AD5620_2500] = { - .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), - .int_vref_mv = 2500, - .write = ad5446_write, - }, - [ID_AD5620_1250] = { - .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), - .int_vref_mv = 1250, - .write = ad5446_write, - }, - [ID_AD5640_2500] = { - .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0), - .int_vref_mv = 2500, - .write = ad5446_write, - }, - [ID_AD5640_1250] = { - .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0), - .int_vref_mv = 1250, - .write = ad5446_write, - }, - [ID_AD5660_2500] = { - .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), - .int_vref_mv = 2500, - .write = ad5660_write, - }, - [ID_AD5660_1250] = { - .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), - .int_vref_mv = 1250, - .write = ad5660_write, - }, - [ID_AD5662] = { - .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), - .write = ad5660_write, - }, -}; - -static int ad5446_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad5446_state *st = iio_priv(indio_dev); - unsigned long scale_uv; - - switch (m) { - case IIO_CHAN_INFO_RAW: - *val = st->cached_val; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; - *val = scale_uv / 1000; - *val2 = (scale_uv % 1000) * 1000; - return IIO_VAL_INT_PLUS_MICRO; - - } - return -EINVAL; -} - -static int ad5446_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct ad5446_state *st = iio_priv(indio_dev); - int ret = 0; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (val >= (1 << chan->scan_type.realbits) || val < 0) - return -EINVAL; - - val <<= chan->scan_type.shift; - mutex_lock(&indio_dev->mlock); - st->cached_val = val; - if (!st->pwr_down) - ret = st->chip_info->write(st, val); - mutex_unlock(&indio_dev->mlock); - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static const struct iio_info ad5446_info = { - .read_raw = ad5446_read_raw, - .write_raw = ad5446_write_raw, - .driver_module = THIS_MODULE, -}; - -static int __devinit ad5446_probe(struct spi_device *spi) -{ - struct ad5446_state *st; - struct iio_dev *indio_dev; - struct regulator *reg; - int ret, voltage_uv = 0; - - reg = regulator_get(&spi->dev, "vcc"); - if (!IS_ERR(reg)) { - ret = regulator_enable(reg); - if (ret) - goto error_put_reg; - - voltage_uv = regulator_get_voltage(reg); - } - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - ret = -ENOMEM; - goto error_disable_reg; - } - st = iio_priv(indio_dev); - st->chip_info = - &ad5446_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - - spi_set_drvdata(spi, indio_dev); - st->reg = reg; - st->spi = spi; - - /* Establish that the iio_dev is a child of the spi device */ - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->info = &ad5446_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = &st->chip_info->channel; - indio_dev->num_channels = 1; - - st->pwr_down_mode = MODE_PWRDWN_1k; - - if (st->chip_info->int_vref_mv) - st->vref_mv = st->chip_info->int_vref_mv; - else if (voltage_uv) - st->vref_mv = voltage_uv / 1000; - else - dev_warn(&spi->dev, "reference voltage unspecified\n"); - - ret = iio_device_register(indio_dev); - if (ret) - goto error_free_device; - - return 0; - -error_free_device: - iio_device_free(indio_dev); -error_disable_reg: - if (!IS_ERR(reg)) - regulator_disable(reg); -error_put_reg: - if (!IS_ERR(reg)) - regulator_put(reg); - - return ret; -} - -static int ad5446_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5446_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (!IS_ERR(st->reg)) { - regulator_disable(st->reg); - regulator_put(st->reg); - } - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5446_id[] = { - {"ad5444", ID_AD5444}, - {"ad5446", ID_AD5446}, - {"ad5512a", ID_AD5512A}, - {"ad5541a", ID_AD5541A}, - {"ad5542a", ID_AD5541A}, /* ad5541a and ad5542a are compatible */ - {"ad5543", ID_AD5541A}, /* ad5541a and ad5543 are compatible */ - {"ad5553", ID_AD5553}, - {"ad5601", ID_AD5601}, - {"ad5611", ID_AD5611}, - {"ad5621", ID_AD5621}, - {"ad5620-2500", ID_AD5620_2500}, /* AD5620/40/60: */ - {"ad5620-1250", ID_AD5620_1250}, /* part numbers may look differently */ - {"ad5640-2500", ID_AD5640_2500}, - {"ad5640-1250", ID_AD5640_1250}, - {"ad5660-2500", ID_AD5660_2500}, - {"ad5660-1250", ID_AD5660_1250}, - {"ad5662", ID_AD5662}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad5446_id); - -static struct spi_driver ad5446_driver = { - .driver = { - .name = "ad5446", - .owner = THIS_MODULE, - }, - .probe = ad5446_probe, - .remove = __devexit_p(ad5446_remove), - .id_table = ad5446_id, -}; -module_spi_driver(ad5446_driver); - -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5446.h b/drivers/staging/iio/dac/ad5446.h deleted file mode 100644 index dfd68ce7427e..000000000000 --- a/drivers/staging/iio/dac/ad5446.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * AD5446 SPI DAC driver - * - * Copyright 2010 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ -#ifndef IIO_DAC_AD5446_H_ -#define IIO_DAC_AD5446_H_ - -/* DAC Control Bits */ - -#define AD5446_LOAD (0x0 << 14) /* Load and update */ -#define AD5446_SDO_DIS (0x1 << 14) /* Disable SDO */ -#define AD5446_NOP (0x2 << 14) /* No operation */ -#define AD5446_CLK_RISING (0x3 << 14) /* Clock data on rising edge */ - -#define AD5620_LOAD (0x0 << 14) /* Load and update Norm Operation*/ -#define AD5620_PWRDWN_1k (0x1 << 14) /* Power-down: 1kOhm to GND */ -#define AD5620_PWRDWN_100k (0x2 << 14) /* Power-down: 100kOhm to GND */ -#define AD5620_PWRDWN_TRISTATE (0x3 << 14) /* Power-down: Three-state */ - -#define AD5660_LOAD (0x0 << 16) /* Load and update Norm Operation*/ -#define AD5660_PWRDWN_1k (0x1 << 16) /* Power-down: 1kOhm to GND */ -#define AD5660_PWRDWN_100k (0x2 << 16) /* Power-down: 100kOhm to GND */ -#define AD5660_PWRDWN_TRISTATE (0x3 << 16) /* Power-down: Three-state */ - -#define MODE_PWRDWN_1k 0x1 -#define MODE_PWRDWN_100k 0x2 -#define MODE_PWRDWN_TRISTATE 0x3 - -/** - * struct ad5446_state - driver instance specific data - * @spi: spi_device - * @chip_info: chip model specific constants, available modes etc - * @reg: supply regulator - * @vref_mv: actual reference voltage used - */ - -struct ad5446_state { - struct spi_device *spi; - const struct ad5446_chip_info *chip_info; - struct regulator *reg; - unsigned short vref_mv; - unsigned cached_val; - unsigned pwr_down_mode; - unsigned pwr_down; -}; - -/** - * struct ad5446_chip_info - chip specific information - * @channel: channel spec for the DAC - * @int_vref_mv: AD5620/40/60: the internal reference voltage - * @write: chip specific helper function to write to the register - */ - -struct ad5446_chip_info { - struct iio_chan_spec channel; - u16 int_vref_mv; - int (*write)(struct ad5446_state *st, unsigned val); -}; - -/** - * ad5446_supported_device_ids: - * The AD5620/40/60 parts are available in different fixed internal reference - * voltage options. The actual part numbers may look differently - * (and a bit cryptic), however this style is used to make clear which - * parts are supported here. - */ - -enum ad5446_supported_device_ids { - ID_AD5444, - ID_AD5446, - ID_AD5541A, - ID_AD5512A, - ID_AD5553, - ID_AD5601, - ID_AD5611, - ID_AD5621, - ID_AD5620_2500, - ID_AD5620_1250, - ID_AD5640_2500, - ID_AD5640_1250, - ID_AD5660_2500, - ID_AD5660_1250, - ID_AD5662, -}; - -#endif /* IIO_DAC_AD5446_H_ */ diff --git a/drivers/staging/iio/dac/ad5504.c b/drivers/staging/iio/dac/ad5504.c deleted file mode 100644 index 1289e9bc1688..000000000000 --- a/drivers/staging/iio/dac/ad5504.c +++ /dev/null @@ -1,394 +0,0 @@ -/* - * AD5504, AD5501 High Voltage Digital to Analog Converter - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "ad5504.h" - -#define AD5505_BITS 12 -#define AD5504_RES_MASK ((1 << (AD5505_BITS)) - 1) - -#define AD5504_CMD_READ (1 << 15) -#define AD5504_CMD_WRITE (0 << 15) -#define AD5504_ADDR(addr) ((addr) << 12) - -/* Registers */ -#define AD5504_ADDR_NOOP 0 -#define AD5504_ADDR_DAC(x) ((x) + 1) -#define AD5504_ADDR_ALL_DAC 5 -#define AD5504_ADDR_CTRL 7 - -/* Control Register */ -#define AD5504_DAC_PWR(ch) ((ch) << 2) -#define AD5504_DAC_PWRDWN_MODE(mode) ((mode) << 6) -#define AD5504_DAC_PWRDN_20K 0 -#define AD5504_DAC_PWRDN_3STATE 1 - -/** - * struct ad5446_state - driver instance specific data - * @us: spi_device - * @reg: supply regulator - * @vref_mv: actual reference voltage used - * @pwr_down_mask power down mask - * @pwr_down_mode current power down mode - */ - -struct ad5504_state { - struct spi_device *spi; - struct regulator *reg; - unsigned short vref_mv; - unsigned pwr_down_mask; - unsigned pwr_down_mode; -}; - -/** - * ad5504_supported_device_ids: - */ - -enum ad5504_supported_device_ids { - ID_AD5504, - ID_AD5501, -}; - -static int ad5504_spi_write(struct spi_device *spi, u8 addr, u16 val) -{ - u16 tmp = cpu_to_be16(AD5504_CMD_WRITE | - AD5504_ADDR(addr) | - (val & AD5504_RES_MASK)); - - return spi_write(spi, (u8 *)&tmp, 2); -} - -static int ad5504_spi_read(struct spi_device *spi, u8 addr) -{ - u16 tmp = cpu_to_be16(AD5504_CMD_READ | AD5504_ADDR(addr)); - u16 val; - int ret; - struct spi_transfer t = { - .tx_buf = &tmp, - .rx_buf = &val, - .len = 2, - }; - struct spi_message m; - - spi_message_init(&m); - spi_message_add_tail(&t, &m); - ret = spi_sync(spi, &m); - - if (ret < 0) - return ret; - - return be16_to_cpu(val) & AD5504_RES_MASK; -} - -static int ad5504_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad5504_state *st = iio_priv(indio_dev); - unsigned long scale_uv; - int ret; - - switch (m) { - case IIO_CHAN_INFO_RAW: - ret = ad5504_spi_read(st->spi, chan->address); - if (ret < 0) - return ret; - - *val = ret; - - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; - *val = scale_uv / 1000; - *val2 = (scale_uv % 1000) * 1000; - return IIO_VAL_INT_PLUS_MICRO; - - } - return -EINVAL; -} - -static int ad5504_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct ad5504_state *st = iio_priv(indio_dev); - int ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (val >= (1 << chan->scan_type.realbits) || val < 0) - return -EINVAL; - - return ad5504_spi_write(st->spi, chan->address, val); - default: - ret = -EINVAL; - } - - return -EINVAL; -} - -static const char * const ad5504_powerdown_modes[] = { - "20kohm_to_gnd", - "three_state", -}; - -static int ad5504_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct ad5504_state *st = iio_priv(indio_dev); - - return st->pwr_down_mode; -} - -static int ad5504_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) -{ - struct ad5504_state *st = iio_priv(indio_dev); - - st->pwr_down_mode = mode; - - return 0; -} - -static const struct iio_enum ad5504_powerdown_mode_enum = { - .items = ad5504_powerdown_modes, - .num_items = ARRAY_SIZE(ad5504_powerdown_modes), - .get = ad5504_get_powerdown_mode, - .set = ad5504_set_powerdown_mode, -}; - -static ssize_t ad5504_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, char *buf) -{ - struct ad5504_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", - !(st->pwr_down_mask & (1 << chan->channel))); -} - -static ssize_t ad5504_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, - size_t len) -{ - bool pwr_down; - int ret; - struct ad5504_state *st = iio_priv(indio_dev); - - ret = strtobool(buf, &pwr_down); - if (ret) - return ret; - - if (pwr_down) - st->pwr_down_mask |= (1 << chan->channel); - else - st->pwr_down_mask &= ~(1 << chan->channel); - - ret = ad5504_spi_write(st->spi, AD5504_ADDR_CTRL, - AD5504_DAC_PWRDWN_MODE(st->pwr_down_mode) | - AD5504_DAC_PWR(st->pwr_down_mask)); - - /* writes to the CTRL register must be followed by a NOOP */ - ad5504_spi_write(st->spi, AD5504_ADDR_NOOP, 0); - - return ret ? ret : len; -} - -static IIO_CONST_ATTR(temp0_thresh_rising_value, "110000"); -static IIO_CONST_ATTR(temp0_thresh_rising_en, "1"); - -static struct attribute *ad5504_ev_attributes[] = { - &iio_const_attr_temp0_thresh_rising_value.dev_attr.attr, - &iio_const_attr_temp0_thresh_rising_en.dev_attr.attr, - NULL, -}; - -static struct attribute_group ad5504_ev_attribute_group = { - .attrs = ad5504_ev_attributes, - .name = "events", -}; - -static irqreturn_t ad5504_event_handler(int irq, void *private) -{ - iio_push_event(private, - IIO_UNMOD_EVENT_CODE(IIO_TEMP, - 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), - iio_get_time_ns()); - - return IRQ_HANDLED; -} - -static const struct iio_info ad5504_info = { - .write_raw = ad5504_write_raw, - .read_raw = ad5504_read_raw, - .event_attrs = &ad5504_ev_attribute_group, - .driver_module = THIS_MODULE, -}; - -static const struct iio_chan_spec_ext_info ad5504_ext_info[] = { - { - .name = "powerdown", - .read = ad5504_read_dac_powerdown, - .write = ad5504_write_dac_powerdown, - }, - IIO_ENUM("powerdown_mode", true, &ad5504_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5504_powerdown_mode_enum), - { }, -}; - -#define AD5504_CHANNEL(_chan) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .channel = (_chan), \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT, \ - .address = AD5504_ADDR_DAC(_chan), \ - .scan_type = IIO_ST('u', 12, 16, 0), \ - .ext_info = ad5504_ext_info, \ -} - -static const struct iio_chan_spec ad5504_channels[] = { - AD5504_CHANNEL(0), - AD5504_CHANNEL(1), - AD5504_CHANNEL(2), - AD5504_CHANNEL(3), -}; - -static int __devinit ad5504_probe(struct spi_device *spi) -{ - struct ad5504_platform_data *pdata = spi->dev.platform_data; - struct iio_dev *indio_dev; - struct ad5504_state *st; - struct regulator *reg; - int ret, voltage_uv = 0; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - ret = -ENOMEM; - goto error_ret; - } - reg = regulator_get(&spi->dev, "vcc"); - if (!IS_ERR(reg)) { - ret = regulator_enable(reg); - if (ret) - goto error_put_reg; - - voltage_uv = regulator_get_voltage(reg); - } - - spi_set_drvdata(spi, indio_dev); - st = iio_priv(indio_dev); - if (voltage_uv) - st->vref_mv = voltage_uv / 1000; - else if (pdata) - st->vref_mv = pdata->vref_mv; - else - dev_warn(&spi->dev, "reference voltage unspecified\n"); - - st->reg = reg; - st->spi = spi; - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(st->spi)->name; - indio_dev->info = &ad5504_info; - if (spi_get_device_id(st->spi)->driver_data == ID_AD5501) - indio_dev->num_channels = 1; - else - indio_dev->num_channels = 4; - indio_dev->channels = ad5504_channels; - indio_dev->modes = INDIO_DIRECT_MODE; - - if (spi->irq) { - ret = request_threaded_irq(spi->irq, - NULL, - &ad5504_event_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - spi_get_device_id(st->spi)->name, - indio_dev); - if (ret) - goto error_disable_reg; - } - - ret = iio_device_register(indio_dev); - if (ret) - goto error_free_irq; - - return 0; - -error_free_irq: - if (spi->irq) - free_irq(spi->irq, indio_dev); -error_disable_reg: - if (!IS_ERR(reg)) - regulator_disable(reg); -error_put_reg: - if (!IS_ERR(reg)) - regulator_put(reg); - - iio_device_free(indio_dev); -error_ret: - return ret; -} - -static int __devexit ad5504_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5504_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (spi->irq) - free_irq(spi->irq, indio_dev); - - if (!IS_ERR(st->reg)) { - regulator_disable(st->reg); - regulator_put(st->reg); - } - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5504_id[] = { - {"ad5504", ID_AD5504}, - {"ad5501", ID_AD5501}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad5504_id); - -static struct spi_driver ad5504_driver = { - .driver = { - .name = "ad5504", - .owner = THIS_MODULE, - }, - .probe = ad5504_probe, - .remove = __devexit_p(ad5504_remove), - .id_table = ad5504_id, -}; -module_spi_driver(ad5504_driver); - -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("Analog Devices AD5501/AD5501 DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5504.h b/drivers/staging/iio/dac/ad5504.h deleted file mode 100644 index d4980bf688bf..000000000000 --- a/drivers/staging/iio/dac/ad5504.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * AD5504 SPI DAC driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#ifndef SPI_AD5504_H_ -#define SPI_AD5504_H_ - -/* - * TODO: struct ad5504_platform_data needs to go into include/linux/iio - */ - -struct ad5504_platform_data { - u16 vref_mv; -}; - -#endif /* SPI_AD5504_H_ */ diff --git a/drivers/staging/iio/dac/ad5624r.h b/drivers/staging/iio/dac/ad5624r.h deleted file mode 100644 index 5dca3028cdfd..000000000000 --- a/drivers/staging/iio/dac/ad5624r.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * AD5624R SPI DAC driver - * - * Copyright 2010-2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ -#ifndef SPI_AD5624R_H_ -#define SPI_AD5624R_H_ - -#define AD5624R_DAC_CHANNELS 4 - -#define AD5624R_ADDR_DAC0 0x0 -#define AD5624R_ADDR_DAC1 0x1 -#define AD5624R_ADDR_DAC2 0x2 -#define AD5624R_ADDR_DAC3 0x3 -#define AD5624R_ADDR_ALL_DAC 0x7 - -#define AD5624R_CMD_WRITE_INPUT_N 0x0 -#define AD5624R_CMD_UPDATE_DAC_N 0x1 -#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2 -#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_N 0x3 -#define AD5624R_CMD_POWERDOWN_DAC 0x4 -#define AD5624R_CMD_RESET 0x5 -#define AD5624R_CMD_LDAC_SETUP 0x6 -#define AD5624R_CMD_INTERNAL_REFER_SETUP 0x7 - -#define AD5624R_LDAC_PWRDN_NONE 0x0 -#define AD5624R_LDAC_PWRDN_1K 0x1 -#define AD5624R_LDAC_PWRDN_100K 0x2 -#define AD5624R_LDAC_PWRDN_3STATE 0x3 - -/** - * struct ad5624r_chip_info - chip specific information - * @channels: channel spec for the DAC - * @int_vref_mv: AD5620/40/60: the internal reference voltage - */ - -struct ad5624r_chip_info { - const struct iio_chan_spec *channels; - u16 int_vref_mv; -}; - -/** - * struct ad5446_state - driver instance specific data - * @indio_dev: the industrial I/O device - * @us: spi_device - * @chip_info: chip model specific constants, available modes etc - * @reg: supply regulator - * @vref_mv: actual reference voltage used - * @pwr_down_mask power down mask - * @pwr_down_mode current power down mode - */ - -struct ad5624r_state { - struct spi_device *us; - const struct ad5624r_chip_info *chip_info; - struct regulator *reg; - unsigned short vref_mv; - unsigned pwr_down_mask; - unsigned pwr_down_mode; -}; - -/** - * ad5624r_supported_device_ids: - * The AD5624/44/64 parts are available in different - * fixed internal reference voltage options. - */ - -enum ad5624r_supported_device_ids { - ID_AD5624R3, - ID_AD5644R3, - ID_AD5664R3, - ID_AD5624R5, - ID_AD5644R5, - ID_AD5664R5, -}; - -#endif /* SPI_AD5624R_H_ */ diff --git a/drivers/staging/iio/dac/ad5624r_spi.c b/drivers/staging/iio/dac/ad5624r_spi.c deleted file mode 100644 index 6a7d6a48cc6d..000000000000 --- a/drivers/staging/iio/dac/ad5624r_spi.c +++ /dev/null @@ -1,324 +0,0 @@ -/* - * AD5624R, AD5644R, AD5664R Digital to analog convertors spi driver - * - * Copyright 2010-2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "ad5624r.h" - -static int ad5624r_spi_write(struct spi_device *spi, - u8 cmd, u8 addr, u16 val, u8 len) -{ - u32 data; - u8 msg[3]; - - /* - * The input shift register is 24 bits wide. The first two bits are - * don't care bits. The next three are the command bits, C2 to C0, - * followed by the 3-bit DAC address, A2 to A0, and then the - * 16-, 14-, 12-bit data-word. The data-word comprises the 16-, - * 14-, 12-bit input code followed by 0, 2, or 4 don't care bits, - * for the AD5664R, AD5644R, and AD5624R, respectively. - */ - data = (0 << 22) | (cmd << 19) | (addr << 16) | (val << (16 - len)); - msg[0] = data >> 16; - msg[1] = data >> 8; - msg[2] = data; - - return spi_write(spi, msg, 3); -} - -static int ad5624r_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad5624r_state *st = iio_priv(indio_dev); - unsigned long scale_uv; - - switch (m) { - case IIO_CHAN_INFO_SCALE: - scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; - *val = scale_uv / 1000; - *val2 = (scale_uv % 1000) * 1000; - return IIO_VAL_INT_PLUS_MICRO; - - } - return -EINVAL; -} - -static int ad5624r_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct ad5624r_state *st = iio_priv(indio_dev); - int ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (val >= (1 << chan->scan_type.realbits) || val < 0) - return -EINVAL; - - return ad5624r_spi_write(st->us, - AD5624R_CMD_WRITE_INPUT_N_UPDATE_N, - chan->address, val, - chan->scan_type.shift); - default: - ret = -EINVAL; - } - - return -EINVAL; -} - -static const char * const ad5624r_powerdown_modes[] = { - "1kohm_to_gnd", - "100kohm_to_gnd", - "three_state" -}; - -static int ad5624r_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct ad5624r_state *st = iio_priv(indio_dev); - - return st->pwr_down_mode; -} - -static int ad5624r_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) -{ - struct ad5624r_state *st = iio_priv(indio_dev); - - st->pwr_down_mode = mode; - - return 0; -} - -static const struct iio_enum ad5624r_powerdown_mode_enum = { - .items = ad5624r_powerdown_modes, - .num_items = ARRAY_SIZE(ad5624r_powerdown_modes), - .get = ad5624r_get_powerdown_mode, - .set = ad5624r_set_powerdown_mode, -}; - -static ssize_t ad5624r_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, char *buf) -{ - struct ad5624r_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", - !!(st->pwr_down_mask & (1 << chan->channel))); -} - -static ssize_t ad5624r_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, - size_t len) -{ - bool pwr_down; - int ret; - struct ad5624r_state *st = iio_priv(indio_dev); - - ret = strtobool(buf, &pwr_down); - if (ret) - return ret; - - if (pwr_down) - st->pwr_down_mask |= (1 << chan->channel); - else - st->pwr_down_mask &= ~(1 << chan->channel); - - ret = ad5624r_spi_write(st->us, AD5624R_CMD_POWERDOWN_DAC, 0, - (st->pwr_down_mode << 4) | - st->pwr_down_mask, 16); - - return ret ? ret : len; -} - -static const struct iio_info ad5624r_info = { - .write_raw = ad5624r_write_raw, - .read_raw = ad5624r_read_raw, - .driver_module = THIS_MODULE, -}; - -static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = { - { - .name = "powerdown", - .read = ad5624r_read_dac_powerdown, - .write = ad5624r_write_dac_powerdown, - }, - IIO_ENUM("powerdown_mode", true, &ad5624r_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5624r_powerdown_mode_enum), - { }, -}; - -#define AD5624R_CHANNEL(_chan, _bits) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .channel = (_chan), \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT, \ - .address = (_chan), \ - .scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)), \ - .ext_info = ad5624r_ext_info, \ -} - -#define DECLARE_AD5624R_CHANNELS(_name, _bits) \ - const struct iio_chan_spec _name##_channels[] = { \ - AD5624R_CHANNEL(0, _bits), \ - AD5624R_CHANNEL(1, _bits), \ - AD5624R_CHANNEL(2, _bits), \ - AD5624R_CHANNEL(3, _bits), \ -} - -static DECLARE_AD5624R_CHANNELS(ad5624r, 12); -static DECLARE_AD5624R_CHANNELS(ad5644r, 14); -static DECLARE_AD5624R_CHANNELS(ad5664r, 16); - -static const struct ad5624r_chip_info ad5624r_chip_info_tbl[] = { - [ID_AD5624R3] = { - .channels = ad5624r_channels, - .int_vref_mv = 1250, - }, - [ID_AD5624R5] = { - .channels = ad5624r_channels, - .int_vref_mv = 2500, - }, - [ID_AD5644R3] = { - .channels = ad5644r_channels, - .int_vref_mv = 1250, - }, - [ID_AD5644R5] = { - .channels = ad5644r_channels, - .int_vref_mv = 2500, - }, - [ID_AD5664R3] = { - .channels = ad5664r_channels, - .int_vref_mv = 1250, - }, - [ID_AD5664R5] = { - .channels = ad5664r_channels, - .int_vref_mv = 2500, - }, -}; - -static int __devinit ad5624r_probe(struct spi_device *spi) -{ - struct ad5624r_state *st; - struct iio_dev *indio_dev; - int ret, voltage_uv = 0; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - ret = -ENOMEM; - goto error_ret; - } - st = iio_priv(indio_dev); - st->reg = regulator_get(&spi->dev, "vcc"); - if (!IS_ERR(st->reg)) { - ret = regulator_enable(st->reg); - if (ret) - goto error_put_reg; - - voltage_uv = regulator_get_voltage(st->reg); - } - - spi_set_drvdata(spi, indio_dev); - st->chip_info = - &ad5624r_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - - if (voltage_uv) - st->vref_mv = voltage_uv / 1000; - else - st->vref_mv = st->chip_info->int_vref_mv; - - st->us = spi; - - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->info = &ad5624r_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = st->chip_info->channels; - indio_dev->num_channels = AD5624R_DAC_CHANNELS; - - ret = ad5624r_spi_write(spi, AD5624R_CMD_INTERNAL_REFER_SETUP, 0, - !!voltage_uv, 16); - if (ret) - goto error_disable_reg; - - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg; - - return 0; - -error_disable_reg: - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); -error_put_reg: - if (!IS_ERR(st->reg)) - regulator_put(st->reg); - iio_device_free(indio_dev); -error_ret: - - return ret; -} - -static int __devexit ad5624r_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5624r_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (!IS_ERR(st->reg)) { - regulator_disable(st->reg); - regulator_put(st->reg); - } - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5624r_id[] = { - {"ad5624r3", ID_AD5624R3}, - {"ad5644r3", ID_AD5644R3}, - {"ad5664r3", ID_AD5664R3}, - {"ad5624r5", ID_AD5624R5}, - {"ad5644r5", ID_AD5644R5}, - {"ad5664r5", ID_AD5664R5}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad5624r_id); - -static struct spi_driver ad5624r_driver = { - .driver = { - .name = "ad5624r", - .owner = THIS_MODULE, - }, - .probe = ad5624r_probe, - .remove = __devexit_p(ad5624r_remove), - .id_table = ad5624r_id, -}; -module_spi_driver(ad5624r_driver); - -MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); -MODULE_DESCRIPTION("Analog Devices AD5624/44/64R DAC spi driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5686.c b/drivers/staging/iio/dac/ad5686.c deleted file mode 100644 index 6948d75e1036..000000000000 --- a/drivers/staging/iio/dac/ad5686.c +++ /dev/null @@ -1,418 +0,0 @@ -/* - * AD5686R, AD5685R, AD5684R Digital to analog converters driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define AD5686_DAC_CHANNELS 4 - -#define AD5686_ADDR(x) ((x) << 16) -#define AD5686_CMD(x) ((x) << 20) - -#define AD5686_ADDR_DAC(chan) (0x1 << (chan)) -#define AD5686_ADDR_ALL_DAC 0xF - -#define AD5686_CMD_NOOP 0x0 -#define AD5686_CMD_WRITE_INPUT_N 0x1 -#define AD5686_CMD_UPDATE_DAC_N 0x2 -#define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3 -#define AD5686_CMD_POWERDOWN_DAC 0x4 -#define AD5686_CMD_LDAC_MASK 0x5 -#define AD5686_CMD_RESET 0x6 -#define AD5686_CMD_INTERNAL_REFER_SETUP 0x7 -#define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8 -#define AD5686_CMD_READBACK_ENABLE 0x9 - -#define AD5686_LDAC_PWRDN_NONE 0x0 -#define AD5686_LDAC_PWRDN_1K 0x1 -#define AD5686_LDAC_PWRDN_100K 0x2 -#define AD5686_LDAC_PWRDN_3STATE 0x3 - -/** - * struct ad5686_chip_info - chip specific information - * @int_vref_mv: AD5620/40/60: the internal reference voltage - * @channel: channel specification -*/ - -struct ad5686_chip_info { - u16 int_vref_mv; - struct iio_chan_spec channel[AD5686_DAC_CHANNELS]; -}; - -/** - * struct ad5446_state - driver instance specific data - * @spi: spi_device - * @chip_info: chip model specific constants, available modes etc - * @reg: supply regulator - * @vref_mv: actual reference voltage used - * @pwr_down_mask: power down mask - * @pwr_down_mode: current power down mode - * @data: spi transfer buffers - */ - -struct ad5686_state { - struct spi_device *spi; - const struct ad5686_chip_info *chip_info; - struct regulator *reg; - unsigned short vref_mv; - unsigned pwr_down_mask; - unsigned pwr_down_mode; - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - */ - - union { - u32 d32; - u8 d8[4]; - } data[3] ____cacheline_aligned; -}; - -/** - * ad5686_supported_device_ids: - */ - -enum ad5686_supported_device_ids { - ID_AD5684, - ID_AD5685, - ID_AD5686, -}; -static int ad5686_spi_write(struct ad5686_state *st, - u8 cmd, u8 addr, u16 val, u8 shift) -{ - val <<= shift; - - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | - AD5686_ADDR(addr) | - val); - - return spi_write(st->spi, &st->data[0].d8[1], 3); -} - -static int ad5686_spi_read(struct ad5686_state *st, u8 addr) -{ - struct spi_transfer t[] = { - { - .tx_buf = &st->data[0].d8[1], - .len = 3, - .cs_change = 1, - }, { - .tx_buf = &st->data[1].d8[1], - .rx_buf = &st->data[2].d8[1], - .len = 3, - }, - }; - struct spi_message m; - int ret; - - spi_message_init(&m); - spi_message_add_tail(&t[0], &m); - spi_message_add_tail(&t[1], &m); - - st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_READBACK_ENABLE) | - AD5686_ADDR(addr)); - st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP)); - - ret = spi_sync(st->spi, &m); - if (ret < 0) - return ret; - - return be32_to_cpu(st->data[2].d32); -} - -static const char * const ad5686_powerdown_modes[] = { - "1kohm_to_gnd", - "100kohm_to_gnd", - "three_state" -}; - -static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct ad5686_state *st = iio_priv(indio_dev); - - return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1; -} - -static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) -{ - struct ad5686_state *st = iio_priv(indio_dev); - - st->pwr_down_mode &= ~(0x3 << (chan->channel * 2)); - st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2)); - - return 0; -} - -static const struct iio_enum ad5686_powerdown_mode_enum = { - .items = ad5686_powerdown_modes, - .num_items = ARRAY_SIZE(ad5686_powerdown_modes), - .get = ad5686_get_powerdown_mode, - .set = ad5686_set_powerdown_mode, -}; - -static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, char *buf) -{ - struct ad5686_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", !!(st->pwr_down_mask & - (0x3 << (chan->channel * 2)))); -} - -static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, - size_t len) -{ - bool readin; - int ret; - struct ad5686_state *st = iio_priv(indio_dev); - - ret = strtobool(buf, &readin); - if (ret) - return ret; - - if (readin == true) - st->pwr_down_mask |= (0x3 << (chan->channel * 2)); - else - st->pwr_down_mask &= ~(0x3 << (chan->channel * 2)); - - ret = ad5686_spi_write(st, AD5686_CMD_POWERDOWN_DAC, 0, - st->pwr_down_mask & st->pwr_down_mode, 0); - - return ret ? ret : len; -} - -static int ad5686_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad5686_state *st = iio_priv(indio_dev); - unsigned long scale_uv; - int ret; - - switch (m) { - case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); - ret = ad5686_spi_read(st, chan->address); - mutex_unlock(&indio_dev->mlock); - if (ret < 0) - return ret; - *val = ret; - return IIO_VAL_INT; - break; - case IIO_CHAN_INFO_SCALE: - scale_uv = (st->vref_mv * 100000) - >> (chan->scan_type.realbits); - *val = scale_uv / 100000; - *val2 = (scale_uv % 100000) * 10; - return IIO_VAL_INT_PLUS_MICRO; - - } - return -EINVAL; -} - -static int ad5686_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct ad5686_state *st = iio_priv(indio_dev); - int ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (val > (1 << chan->scan_type.realbits) || val < 0) - return -EINVAL; - - mutex_lock(&indio_dev->mlock); - ret = ad5686_spi_write(st, - AD5686_CMD_WRITE_INPUT_N_UPDATE_N, - chan->address, - val, - chan->scan_type.shift); - mutex_unlock(&indio_dev->mlock); - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static const struct iio_info ad5686_info = { - .read_raw = ad5686_read_raw, - .write_raw = ad5686_write_raw, - .driver_module = THIS_MODULE, -}; - -static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { - { - .name = "powerdown", - .read = ad5686_read_dac_powerdown, - .write = ad5686_write_dac_powerdown, - }, - IIO_ENUM("powerdown_mode", false, &ad5686_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5686_powerdown_mode_enum), - { }, -}; - -#define AD5868_CHANNEL(chan, bits, shift) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .channel = chan, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT, \ - .address = AD5686_ADDR_DAC(chan), \ - .scan_type = IIO_ST('u', bits, 16, shift), \ - .ext_info = ad5686_ext_info, \ -} - -static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { - [ID_AD5684] = { - .channel[0] = AD5868_CHANNEL(0, 12, 4), - .channel[1] = AD5868_CHANNEL(1, 12, 4), - .channel[2] = AD5868_CHANNEL(2, 12, 4), - .channel[3] = AD5868_CHANNEL(3, 12, 4), - .int_vref_mv = 2500, - }, - [ID_AD5685] = { - .channel[0] = AD5868_CHANNEL(0, 14, 2), - .channel[1] = AD5868_CHANNEL(1, 14, 2), - .channel[2] = AD5868_CHANNEL(2, 14, 2), - .channel[3] = AD5868_CHANNEL(3, 14, 2), - .int_vref_mv = 2500, - }, - [ID_AD5686] = { - .channel[0] = AD5868_CHANNEL(0, 16, 0), - .channel[1] = AD5868_CHANNEL(1, 16, 0), - .channel[2] = AD5868_CHANNEL(2, 16, 0), - .channel[3] = AD5868_CHANNEL(3, 16, 0), - .int_vref_mv = 2500, - }, -}; - - -static int __devinit ad5686_probe(struct spi_device *spi) -{ - struct ad5686_state *st; - struct iio_dev *indio_dev; - int ret, regdone = 0, voltage_uv = 0; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) - return -ENOMEM; - - st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); - - st->reg = regulator_get(&spi->dev, "vcc"); - if (!IS_ERR(st->reg)) { - ret = regulator_enable(st->reg); - if (ret) - goto error_put_reg; - - voltage_uv = regulator_get_voltage(st->reg); - } - - st->chip_info = - &ad5686_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - - if (voltage_uv) - st->vref_mv = voltage_uv / 1000; - else - st->vref_mv = st->chip_info->int_vref_mv; - - st->spi = spi; - - /* Set all the power down mode for all channels to 1K pulldown */ - st->pwr_down_mode = 0x55; - - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->info = &ad5686_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = st->chip_info->channel; - indio_dev->num_channels = AD5686_DAC_CHANNELS; - - regdone = 1; - ret = ad5686_spi_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0, - !!voltage_uv, 0); - if (ret) - goto error_disable_reg; - - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg; - - return 0; - -error_disable_reg: - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); -error_put_reg: - if (!IS_ERR(st->reg)) - regulator_put(st->reg); - - iio_device_free(indio_dev); - - return ret; -} - -static int __devexit ad5686_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5686_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (!IS_ERR(st->reg)) { - regulator_disable(st->reg); - regulator_put(st->reg); - } - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5686_id[] = { - {"ad5684", ID_AD5684}, - {"ad5685", ID_AD5685}, - {"ad5686", ID_AD5686}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad5686_id); - -static struct spi_driver ad5686_driver = { - .driver = { - .name = "ad5686", - .owner = THIS_MODULE, - }, - .probe = ad5686_probe, - .remove = __devexit_p(ad5686_remove), - .id_table = ad5686_id, -}; -module_spi_driver(ad5686_driver); - -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5764.c b/drivers/staging/iio/dac/ad5764.c deleted file mode 100644 index ffce30447445..000000000000 --- a/drivers/staging/iio/dac/ad5764.c +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Analog devices AD5764, AD5764R, AD5744, AD5744R quad-channel - * Digital to Analog Converters driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define AD5764_REG_SF_NOP 0x0 -#define AD5764_REG_SF_CONFIG 0x1 -#define AD5764_REG_SF_CLEAR 0x4 -#define AD5764_REG_SF_LOAD 0x5 -#define AD5764_REG_DATA(x) ((2 << 3) | (x)) -#define AD5764_REG_COARSE_GAIN(x) ((3 << 3) | (x)) -#define AD5764_REG_FINE_GAIN(x) ((4 << 3) | (x)) -#define AD5764_REG_OFFSET(x) ((5 << 3) | (x)) - -#define AD5764_NUM_CHANNELS 4 - -/** - * struct ad5764_chip_info - chip specific information - * @int_vref: Value of the internal reference voltage in uV - 0 if external - * reference voltage is used - * @channel channel specification -*/ - -struct ad5764_chip_info { - unsigned long int_vref; - const struct iio_chan_spec *channels; -}; - -/** - * struct ad5764_state - driver instance specific data - * @spi: spi_device - * @chip_info: chip info - * @vref_reg: vref supply regulators - * @data: spi transfer buffers - */ - -struct ad5764_state { - struct spi_device *spi; - const struct ad5764_chip_info *chip_info; - struct regulator_bulk_data vref_reg[2]; - - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - */ - union { - __be32 d32; - u8 d8[4]; - } data[2] ____cacheline_aligned; -}; - -enum ad5764_type { - ID_AD5744, - ID_AD5744R, - ID_AD5764, - ID_AD5764R, -}; - -#define AD5764_CHANNEL(_chan, _bits) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .channel = (_chan), \ - .address = (_chan), \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_OFFSET_SHARED_BIT | \ - IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \ - IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \ - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \ - .scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)) \ -} - -#define DECLARE_AD5764_CHANNELS(_name, _bits) \ -const struct iio_chan_spec _name##_channels[] = { \ - AD5764_CHANNEL(0, (_bits)), \ - AD5764_CHANNEL(1, (_bits)), \ - AD5764_CHANNEL(2, (_bits)), \ - AD5764_CHANNEL(3, (_bits)), \ -}; - -static DECLARE_AD5764_CHANNELS(ad5764, 16); -static DECLARE_AD5764_CHANNELS(ad5744, 14); - -static const struct ad5764_chip_info ad5764_chip_infos[] = { - [ID_AD5744] = { - .int_vref = 0, - .channels = ad5744_channels, - }, - [ID_AD5744R] = { - .int_vref = 5000000, - .channels = ad5744_channels, - }, - [ID_AD5764] = { - .int_vref = 0, - .channels = ad5764_channels, - }, - [ID_AD5764R] = { - .int_vref = 5000000, - .channels = ad5764_channels, - }, -}; - -static int ad5764_write(struct iio_dev *indio_dev, unsigned int reg, - unsigned int val) -{ - struct ad5764_state *st = iio_priv(indio_dev); - int ret; - - mutex_lock(&indio_dev->mlock); - st->data[0].d32 = cpu_to_be32((reg << 16) | val); - - ret = spi_write(st->spi, &st->data[0].d8[1], 3); - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg, - unsigned int *val) -{ - struct ad5764_state *st = iio_priv(indio_dev); - struct spi_message m; - int ret; - struct spi_transfer t[] = { - { - .tx_buf = &st->data[0].d8[1], - .len = 3, - .cs_change = 1, - }, { - .rx_buf = &st->data[1].d8[1], - .len = 3, - }, - }; - - spi_message_init(&m); - spi_message_add_tail(&t[0], &m); - spi_message_add_tail(&t[1], &m); - - mutex_lock(&indio_dev->mlock); - - st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16)); - - ret = spi_sync(st->spi, &m); - if (ret >= 0) - *val = be32_to_cpu(st->data[1].d32) & 0xffff; - - mutex_unlock(&indio_dev->mlock); - - return ret; -} - -static int ad5764_chan_info_to_reg(struct iio_chan_spec const *chan, long info) -{ - switch (info) { - case 0: - return AD5764_REG_DATA(chan->address); - case IIO_CHAN_INFO_CALIBBIAS: - return AD5764_REG_OFFSET(chan->address); - case IIO_CHAN_INFO_CALIBSCALE: - return AD5764_REG_FINE_GAIN(chan->address); - default: - break; - } - - return 0; -} - -static int ad5764_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, long info) -{ - const int max_val = (1 << chan->scan_type.realbits); - unsigned int reg; - - switch (info) { - case IIO_CHAN_INFO_RAW: - if (val >= max_val || val < 0) - return -EINVAL; - val <<= chan->scan_type.shift; - break; - case IIO_CHAN_INFO_CALIBBIAS: - if (val >= 128 || val < -128) - return -EINVAL; - break; - case IIO_CHAN_INFO_CALIBSCALE: - if (val >= 32 || val < -32) - return -EINVAL; - break; - default: - return -EINVAL; - } - - reg = ad5764_chan_info_to_reg(chan, info); - return ad5764_write(indio_dev, reg, (u16)val); -} - -static int ad5764_get_channel_vref(struct ad5764_state *st, - unsigned int channel) -{ - if (st->chip_info->int_vref) - return st->chip_info->int_vref; - else - return regulator_get_voltage(st->vref_reg[channel / 2].consumer); -} - -static int ad5764_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int *val, int *val2, long info) -{ - struct ad5764_state *st = iio_priv(indio_dev); - unsigned long scale_uv; - unsigned int reg; - int vref; - int ret; - - switch (info) { - case IIO_CHAN_INFO_RAW: - reg = AD5764_REG_DATA(chan->address); - ret = ad5764_read(indio_dev, reg, val); - if (ret < 0) - return ret; - *val >>= chan->scan_type.shift; - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBBIAS: - reg = AD5764_REG_OFFSET(chan->address); - ret = ad5764_read(indio_dev, reg, val); - if (ret < 0) - return ret; - *val = sign_extend32(*val, 7); - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBSCALE: - reg = AD5764_REG_FINE_GAIN(chan->address); - ret = ad5764_read(indio_dev, reg, val); - if (ret < 0) - return ret; - *val = sign_extend32(*val, 5); - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - /* vout = 4 * vref + ((dac_code / 65535) - 0.5) */ - vref = ad5764_get_channel_vref(st, chan->channel); - if (vref < 0) - return vref; - - scale_uv = (vref * 4 * 100) >> chan->scan_type.realbits; - *val = scale_uv / 100000; - *val2 = (scale_uv % 100000) * 10; - return IIO_VAL_INT_PLUS_MICRO; - case IIO_CHAN_INFO_OFFSET: - *val = -(1 << chan->scan_type.realbits) / 2; - return IIO_VAL_INT; - } - - return -EINVAL; -} - -static const struct iio_info ad5764_info = { - .read_raw = ad5764_read_raw, - .write_raw = ad5764_write_raw, - .driver_module = THIS_MODULE, -}; - -static int __devinit ad5764_probe(struct spi_device *spi) -{ - enum ad5764_type type = spi_get_device_id(spi)->driver_data; - struct iio_dev *indio_dev; - struct ad5764_state *st; - int ret; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - dev_err(&spi->dev, "Failed to allocate iio device\n"); - return -ENOMEM; - } - - st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); - - st->spi = spi; - st->chip_info = &ad5764_chip_infos[type]; - - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->info = &ad5764_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->num_channels = AD5764_NUM_CHANNELS; - indio_dev->channels = st->chip_info->channels; - - if (st->chip_info->int_vref == 0) { - st->vref_reg[0].supply = "vrefAB"; - st->vref_reg[1].supply = "vrefCD"; - - ret = regulator_bulk_get(&st->spi->dev, - ARRAY_SIZE(st->vref_reg), st->vref_reg); - if (ret) { - dev_err(&spi->dev, "Failed to request vref regulators: %d\n", - ret); - goto error_free; - } - - ret = regulator_bulk_enable(ARRAY_SIZE(st->vref_reg), - st->vref_reg); - if (ret) { - dev_err(&spi->dev, "Failed to enable vref regulators: %d\n", - ret); - goto error_free_reg; - } - } - - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); - goto error_disable_reg; - } - - return 0; - -error_disable_reg: - if (st->chip_info->int_vref == 0) - regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg); -error_free_reg: - if (st->chip_info->int_vref == 0) - regulator_bulk_free(ARRAY_SIZE(st->vref_reg), st->vref_reg); -error_free: - iio_device_free(indio_dev); - - return ret; -} - -static int __devexit ad5764_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5764_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - if (st->chip_info->int_vref == 0) { - regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg); - regulator_bulk_free(ARRAY_SIZE(st->vref_reg), st->vref_reg); - } - - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5764_ids[] = { - { "ad5744", ID_AD5744 }, - { "ad5744r", ID_AD5744R }, - { "ad5764", ID_AD5764 }, - { "ad5764r", ID_AD5764R }, - { } -}; -MODULE_DEVICE_TABLE(spi, ad5764_ids); - -static struct spi_driver ad5764_driver = { - .driver = { - .name = "ad5764", - .owner = THIS_MODULE, - }, - .probe = ad5764_probe, - .remove = __devexit_p(ad5764_remove), - .id_table = ad5764_ids, -}; -module_spi_driver(ad5764_driver); - -MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_DESCRIPTION("Analog Devices AD5744/AD5744R/AD5764/AD5764R DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5791.c b/drivers/staging/iio/dac/ad5791.c deleted file mode 100644 index 5de28c2a57ef..000000000000 --- a/drivers/staging/iio/dac/ad5791.c +++ /dev/null @@ -1,486 +0,0 @@ -/* - * AD5760, AD5780, AD5781, AD5790, AD5791 Voltage Output Digital to Analog - * Converter - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "ad5791.h" - -#define AD5791_RES_MASK(x) ((1 << (x)) - 1) -#define AD5791_DAC_MASK AD5791_RES_MASK(20) -#define AD5791_DAC_MSB (1 << 19) - -#define AD5791_CMD_READ (1 << 23) -#define AD5791_CMD_WRITE (0 << 23) -#define AD5791_ADDR(addr) ((addr) << 20) - -/* Registers */ -#define AD5791_ADDR_NOOP 0 -#define AD5791_ADDR_DAC0 1 -#define AD5791_ADDR_CTRL 2 -#define AD5791_ADDR_CLRCODE 3 -#define AD5791_ADDR_SW_CTRL 4 - -/* Control Register */ -#define AD5791_CTRL_RBUF (1 << 1) -#define AD5791_CTRL_OPGND (1 << 2) -#define AD5791_CTRL_DACTRI (1 << 3) -#define AD5791_CTRL_BIN2SC (1 << 4) -#define AD5791_CTRL_SDODIS (1 << 5) -#define AD5761_CTRL_LINCOMP(x) ((x) << 6) - -#define AD5791_LINCOMP_0_10 0 -#define AD5791_LINCOMP_10_12 1 -#define AD5791_LINCOMP_12_16 2 -#define AD5791_LINCOMP_16_19 3 -#define AD5791_LINCOMP_19_20 12 - -#define AD5780_LINCOMP_0_10 0 -#define AD5780_LINCOMP_10_20 12 - -/* Software Control Register */ -#define AD5791_SWCTRL_LDAC (1 << 0) -#define AD5791_SWCTRL_CLR (1 << 1) -#define AD5791_SWCTRL_RESET (1 << 2) - -#define AD5791_DAC_PWRDN_6K 0 -#define AD5791_DAC_PWRDN_3STATE 1 - -/** - * struct ad5791_chip_info - chip specific information - * @get_lin_comp: function pointer to the device specific function - */ - -struct ad5791_chip_info { - int (*get_lin_comp) (unsigned int span); -}; - -/** - * struct ad5791_state - driver instance specific data - * @us: spi_device - * @reg_vdd: positive supply regulator - * @reg_vss: negative supply regulator - * @chip_info: chip model specific constants - * @vref_mv: actual reference voltage used - * @vref_neg_mv: voltage of the negative supply - * @pwr_down_mode current power down mode - */ - -struct ad5791_state { - struct spi_device *spi; - struct regulator *reg_vdd; - struct regulator *reg_vss; - const struct ad5791_chip_info *chip_info; - unsigned short vref_mv; - unsigned int vref_neg_mv; - unsigned ctrl; - unsigned pwr_down_mode; - bool pwr_down; -}; - -/** - * ad5791_supported_device_ids: - */ - -enum ad5791_supported_device_ids { - ID_AD5760, - ID_AD5780, - ID_AD5781, - ID_AD5791, -}; - -static int ad5791_spi_write(struct spi_device *spi, u8 addr, u32 val) -{ - union { - u32 d32; - u8 d8[4]; - } data; - - data.d32 = cpu_to_be32(AD5791_CMD_WRITE | - AD5791_ADDR(addr) | - (val & AD5791_DAC_MASK)); - - return spi_write(spi, &data.d8[1], 3); -} - -static int ad5791_spi_read(struct spi_device *spi, u8 addr, u32 *val) -{ - union { - u32 d32; - u8 d8[4]; - } data[3]; - int ret; - struct spi_message msg; - struct spi_transfer xfers[] = { - { - .tx_buf = &data[0].d8[1], - .bits_per_word = 8, - .len = 3, - .cs_change = 1, - }, { - .tx_buf = &data[1].d8[1], - .rx_buf = &data[2].d8[1], - .bits_per_word = 8, - .len = 3, - }, - }; - - data[0].d32 = cpu_to_be32(AD5791_CMD_READ | - AD5791_ADDR(addr)); - data[1].d32 = cpu_to_be32(AD5791_ADDR(AD5791_ADDR_NOOP)); - - spi_message_init(&msg); - spi_message_add_tail(&xfers[0], &msg); - spi_message_add_tail(&xfers[1], &msg); - ret = spi_sync(spi, &msg); - - *val = be32_to_cpu(data[2].d32); - - return ret; -} - -static const char * const ad5791_powerdown_modes[] = { - "6kohm_to_gnd", - "three_state", -}; - -static int ad5791_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct ad5791_state *st = iio_priv(indio_dev); - - return st->pwr_down_mode; -} - -static int ad5791_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) -{ - struct ad5791_state *st = iio_priv(indio_dev); - - st->pwr_down_mode = mode; - - return 0; -} - -static const struct iio_enum ad5791_powerdown_mode_enum = { - .items = ad5791_powerdown_modes, - .num_items = ARRAY_SIZE(ad5791_powerdown_modes), - .get = ad5791_get_powerdown_mode, - .set = ad5791_set_powerdown_mode, -}; - -static ssize_t ad5791_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, char *buf) -{ - struct ad5791_state *st = iio_priv(indio_dev); - - return sprintf(buf, "%d\n", st->pwr_down); -} - -static ssize_t ad5791_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, - size_t len) -{ - bool pwr_down; - int ret; - struct ad5791_state *st = iio_priv(indio_dev); - - ret = strtobool(buf, &pwr_down); - if (ret) - return ret; - - if (!pwr_down) { - st->ctrl &= ~(AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI); - } else { - if (st->pwr_down_mode == AD5791_DAC_PWRDN_6K) - st->ctrl |= AD5791_CTRL_OPGND; - else if (st->pwr_down_mode == AD5791_DAC_PWRDN_3STATE) - st->ctrl |= AD5791_CTRL_DACTRI; - } - st->pwr_down = pwr_down; - - ret = ad5791_spi_write(st->spi, AD5791_ADDR_CTRL, st->ctrl); - - return ret ? ret : len; -} - -static int ad5791_get_lin_comp(unsigned int span) -{ - if (span <= 10000) - return AD5791_LINCOMP_0_10; - else if (span <= 12000) - return AD5791_LINCOMP_10_12; - else if (span <= 16000) - return AD5791_LINCOMP_12_16; - else if (span <= 19000) - return AD5791_LINCOMP_16_19; - else - return AD5791_LINCOMP_19_20; -} - -static int ad5780_get_lin_comp(unsigned int span) -{ - if (span <= 10000) - return AD5780_LINCOMP_0_10; - else - return AD5780_LINCOMP_10_20; -} -static const struct ad5791_chip_info ad5791_chip_info_tbl[] = { - [ID_AD5760] = { - .get_lin_comp = ad5780_get_lin_comp, - }, - [ID_AD5780] = { - .get_lin_comp = ad5780_get_lin_comp, - }, - [ID_AD5781] = { - .get_lin_comp = ad5791_get_lin_comp, - }, - [ID_AD5791] = { - .get_lin_comp = ad5791_get_lin_comp, - }, -}; - -static int ad5791_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct ad5791_state *st = iio_priv(indio_dev); - u64 val64; - int ret; - - switch (m) { - case IIO_CHAN_INFO_RAW: - ret = ad5791_spi_read(st->spi, chan->address, val); - if (ret) - return ret; - *val &= AD5791_DAC_MASK; - *val >>= chan->scan_type.shift; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - *val = 0; - *val2 = (((u64)st->vref_mv) * 1000000ULL) >> chan->scan_type.realbits; - return IIO_VAL_INT_PLUS_MICRO; - case IIO_CHAN_INFO_OFFSET: - val64 = (((u64)st->vref_neg_mv) << chan->scan_type.realbits); - do_div(val64, st->vref_mv); - *val = -val64; - return IIO_VAL_INT; - default: - return -EINVAL; - } - -}; - -static const struct iio_chan_spec_ext_info ad5791_ext_info[] = { - { - .name = "powerdown", - .shared = true, - .read = ad5791_read_dac_powerdown, - .write = ad5791_write_dac_powerdown, - }, - IIO_ENUM("powerdown_mode", true, &ad5791_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5791_powerdown_mode_enum), - { }, -}; - -#define AD5791_CHAN(bits, shift) { \ - .type = IIO_VOLTAGE, \ - .output = 1, \ - .indexed = 1, \ - .address = AD5791_ADDR_DAC0, \ - .channel = 0, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT | \ - IIO_CHAN_INFO_OFFSET_SHARED_BIT, \ - .scan_type = IIO_ST('u', bits, 24, shift), \ - .ext_info = ad5791_ext_info, \ -} - -static const struct iio_chan_spec ad5791_channels[] = { - [ID_AD5760] = AD5791_CHAN(16, 4), - [ID_AD5780] = AD5791_CHAN(18, 2), - [ID_AD5781] = AD5791_CHAN(18, 2), - [ID_AD5791] = AD5791_CHAN(20, 0) -}; - -static int ad5791_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct ad5791_state *st = iio_priv(indio_dev); - - switch (mask) { - case IIO_CHAN_INFO_RAW: - val &= AD5791_RES_MASK(chan->scan_type.realbits); - val <<= chan->scan_type.shift; - - return ad5791_spi_write(st->spi, chan->address, val); - - default: - return -EINVAL; - } -} - -static const struct iio_info ad5791_info = { - .read_raw = &ad5791_read_raw, - .write_raw = &ad5791_write_raw, - .driver_module = THIS_MODULE, -}; - -static int __devinit ad5791_probe(struct spi_device *spi) -{ - struct ad5791_platform_data *pdata = spi->dev.platform_data; - struct iio_dev *indio_dev; - struct ad5791_state *st; - int ret, pos_voltage_uv = 0, neg_voltage_uv = 0; - - indio_dev = iio_device_alloc(sizeof(*st)); - if (indio_dev == NULL) { - ret = -ENOMEM; - goto error_ret; - } - st = iio_priv(indio_dev); - st->reg_vdd = regulator_get(&spi->dev, "vdd"); - if (!IS_ERR(st->reg_vdd)) { - ret = regulator_enable(st->reg_vdd); - if (ret) - goto error_put_reg_pos; - - pos_voltage_uv = regulator_get_voltage(st->reg_vdd); - } - - st->reg_vss = regulator_get(&spi->dev, "vss"); - if (!IS_ERR(st->reg_vss)) { - ret = regulator_enable(st->reg_vss); - if (ret) - goto error_put_reg_neg; - - neg_voltage_uv = regulator_get_voltage(st->reg_vss); - } - - st->pwr_down = true; - st->spi = spi; - - if (!IS_ERR(st->reg_vss) && !IS_ERR(st->reg_vdd)) { - st->vref_mv = (pos_voltage_uv + neg_voltage_uv) / 1000; - st->vref_neg_mv = neg_voltage_uv / 1000; - } else if (pdata) { - st->vref_mv = pdata->vref_pos_mv + pdata->vref_neg_mv; - st->vref_neg_mv = pdata->vref_neg_mv; - } else { - dev_warn(&spi->dev, "reference voltage unspecified\n"); - } - - ret = ad5791_spi_write(spi, AD5791_ADDR_SW_CTRL, AD5791_SWCTRL_RESET); - if (ret) - goto error_disable_reg_neg; - - st->chip_info = &ad5791_chip_info_tbl[spi_get_device_id(spi) - ->driver_data]; - - - st->ctrl = AD5761_CTRL_LINCOMP(st->chip_info->get_lin_comp(st->vref_mv)) - | ((pdata && pdata->use_rbuf_gain2) ? 0 : AD5791_CTRL_RBUF) | - AD5791_CTRL_BIN2SC; - - ret = ad5791_spi_write(spi, AD5791_ADDR_CTRL, st->ctrl | - AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI); - if (ret) - goto error_disable_reg_neg; - - spi_set_drvdata(spi, indio_dev); - indio_dev->dev.parent = &spi->dev; - indio_dev->info = &ad5791_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels - = &ad5791_channels[spi_get_device_id(spi)->driver_data]; - indio_dev->num_channels = 1; - indio_dev->name = spi_get_device_id(st->spi)->name; - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg_neg; - - return 0; - -error_disable_reg_neg: - if (!IS_ERR(st->reg_vss)) - regulator_disable(st->reg_vss); -error_put_reg_neg: - if (!IS_ERR(st->reg_vss)) - regulator_put(st->reg_vss); - - if (!IS_ERR(st->reg_vdd)) - regulator_disable(st->reg_vdd); -error_put_reg_pos: - if (!IS_ERR(st->reg_vdd)) - regulator_put(st->reg_vdd); - iio_device_free(indio_dev); -error_ret: - - return ret; -} - -static int __devexit ad5791_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad5791_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (!IS_ERR(st->reg_vdd)) { - regulator_disable(st->reg_vdd); - regulator_put(st->reg_vdd); - } - - if (!IS_ERR(st->reg_vss)) { - regulator_disable(st->reg_vss); - regulator_put(st->reg_vss); - } - iio_device_free(indio_dev); - - return 0; -} - -static const struct spi_device_id ad5791_id[] = { - {"ad5760", ID_AD5760}, - {"ad5780", ID_AD5780}, - {"ad5781", ID_AD5781}, - {"ad5790", ID_AD5791}, - {"ad5791", ID_AD5791}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad5791_id); - -static struct spi_driver ad5791_driver = { - .driver = { - .name = "ad5791", - .owner = THIS_MODULE, - }, - .probe = ad5791_probe, - .remove = __devexit_p(ad5791_remove), - .id_table = ad5791_id, -}; -module_spi_driver(ad5791_driver); - -MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/dac/ad5791.h b/drivers/staging/iio/dac/ad5791.h deleted file mode 100644 index 87a6c922f18e..000000000000 --- a/drivers/staging/iio/dac/ad5791.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * AD5791 SPI DAC driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ - -#ifndef SPI_AD5791_H_ -#define SPI_AD5791_H_ - -/* - * TODO: struct ad5791_platform_data needs to go into include/linux/iio - */ - -/** - * struct ad5791_platform_data - platform specific information - * @vref_pos_mv: Vdd Positive Analog Supply Volatge (mV) - * @vref_neg_mv: Vdd Negative Analog Supply Volatge (mV) - * @use_rbuf_gain2: ext. amplifier connected in gain of two configuration - */ - -struct ad5791_platform_data { - u16 vref_pos_mv; - u16 vref_neg_mv; - bool use_rbuf_gain2; -}; - -#endif /* SPI_AD5791_H_ */ diff --git a/drivers/staging/iio/dac/max517.c b/drivers/staging/iio/dac/max517.c deleted file mode 100644 index 403e06fbc39e..000000000000 --- a/drivers/staging/iio/dac/max517.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * max517.c - Support for Maxim MAX517, MAX518 and MAX519 - * - * Copyright (C) 2010, 2011 Roland Stigge - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "max517.h" - -#define MAX517_DRV_NAME "max517" - -/* Commands */ -#define COMMAND_CHANNEL0 0x00 -#define COMMAND_CHANNEL1 0x01 /* for MAX518 and MAX519 */ -#define COMMAND_PD 0x08 /* Power Down */ - -enum max517_device_ids { - ID_MAX517, - ID_MAX518, - ID_MAX519, -}; - -struct max517_data { - struct iio_dev *indio_dev; - struct i2c_client *client; - unsigned short vref_mv[2]; -}; - -/* - * channel: bit 0: channel 1 - * bit 1: channel 2 - * (this way, it's possible to set both channels at once) - */ -static int max517_set_value(struct iio_dev *indio_dev, - long val, int channel) -{ - struct max517_data *data = iio_priv(indio_dev); - struct i2c_client *client = data->client; - u8 outbuf[2]; - int res; - - if (val < 0 || val > 255) - return -EINVAL; - - outbuf[0] = channel; - outbuf[1] = val; - - res = i2c_master_send(client, outbuf, 2); - if (res < 0) - return res; - else if (res != 2) - return -EIO; - else - return 0; -} - -static int max517_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long m) -{ - struct max517_data *data = iio_priv(indio_dev); - unsigned int scale_uv; - - switch (m) { - case IIO_CHAN_INFO_SCALE: - /* Corresponds to Vref / 2^(bits) */ - scale_uv = (data->vref_mv[chan->channel] * 1000) >> 8; - *val = scale_uv / 1000000; - *val2 = scale_uv % 1000000; - return IIO_VAL_INT_PLUS_MICRO; - default: - break; - } - return -EINVAL; -} - -static int max517_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, long mask) -{ - int ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - ret = max517_set_value(indio_dev, val, chan->channel); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -#ifdef CONFIG_PM_SLEEP -static int max517_suspend(struct device *dev) -{ - u8 outbuf = COMMAND_PD; - - return i2c_master_send(to_i2c_client(dev), &outbuf, 1); -} - -static int max517_resume(struct device *dev) -{ - u8 outbuf = 0; - - return i2c_master_send(to_i2c_client(dev), &outbuf, 1); -} - -static SIMPLE_DEV_PM_OPS(max517_pm_ops, max517_suspend, max517_resume); -#define MAX517_PM_OPS (&max517_pm_ops) -#else -#define MAX517_PM_OPS NULL -#endif - -static const struct iio_info max517_info = { - .read_raw = max517_read_raw, - .write_raw = max517_write_raw, - .driver_module = THIS_MODULE, -}; - -#define MAX517_CHANNEL(chan) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .output = 1, \ - .channel = (chan), \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ - .scan_type = IIO_ST('u', 8, 8, 0), \ -} - -static const struct iio_chan_spec max517_channels[] = { - MAX517_CHANNEL(0), - MAX517_CHANNEL(1) -}; - -static int max517_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct max517_data *data; - struct iio_dev *indio_dev; - struct max517_platform_data *platform_data = client->dev.platform_data; - int err; - - indio_dev = iio_device_alloc(sizeof(*data)); - if (indio_dev == NULL) { - err = -ENOMEM; - goto exit; - } - data = iio_priv(indio_dev); - i2c_set_clientdata(client, indio_dev); - data->client = client; - - /* establish that the iio_dev is a child of the i2c device */ - indio_dev->dev.parent = &client->dev; - - /* reduced channel set for MAX517 */ - if (id->driver_data == ID_MAX517) - indio_dev->num_channels = 1; - else - indio_dev->num_channels = 2; - indio_dev->channels = max517_channels; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->info = &max517_info; - - /* - * Reference voltage on MAX518 and default is 5V, else take vref_mv - * from platform_data - */ - if (id->driver_data == ID_MAX518 || !platform_data) { - data->vref_mv[0] = data->vref_mv[1] = 5000; /* mV */ - } else { - data->vref_mv[0] = platform_data->vref_mv[0]; - data->vref_mv[1] = platform_data->vref_mv[1]; - } - - err = iio_device_register(indio_dev); - if (err) - goto exit_free_device; - - dev_info(&client->dev, "DAC registered\n"); - - return 0; - -exit_free_device: - iio_device_free(indio_dev); -exit: - return err; -} - -static int max517_remove(struct i2c_client *client) -{ - iio_device_unregister(i2c_get_clientdata(client)); - iio_device_free(i2c_get_clientdata(client)); - - return 0; -} - -static const struct i2c_device_id max517_id[] = { - { "max517", ID_MAX517 }, - { "max518", ID_MAX518 }, - { "max519", ID_MAX519 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, max517_id); - -static struct i2c_driver max517_driver = { - .driver = { - .name = MAX517_DRV_NAME, - .pm = MAX517_PM_OPS, - }, - .probe = max517_probe, - .remove = max517_remove, - .id_table = max517_id, -}; -module_i2c_driver(max517_driver); - -MODULE_AUTHOR("Roland Stigge "); -MODULE_DESCRIPTION("MAX517/MAX518/MAX519 8-bit DAC"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/dac/max517.h b/drivers/staging/iio/dac/max517.h deleted file mode 100644 index 8106cf24642a..000000000000 --- a/drivers/staging/iio/dac/max517.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * MAX517 DAC driver - * - * Copyright 2011 Roland Stigge - * - * Licensed under the GPL-2 or later. - */ -#ifndef IIO_DAC_MAX517_H_ -#define IIO_DAC_MAX517_H_ - -/* - * TODO: struct max517_platform_data needs to go into include/linux/iio - */ - -struct max517_platform_data { - u16 vref_mv[2]; -}; - -#endif /* IIO_DAC_MAX517_H_ */ diff --git a/include/linux/iio/dac/ad5421.h b/include/linux/iio/dac/ad5421.h new file mode 100644 index 000000000000..8fd8f057a890 --- /dev/null +++ b/include/linux/iio/dac/ad5421.h @@ -0,0 +1,28 @@ +#ifndef __IIO_DAC_AD5421_H__ +#define __IIO_DAC_AD5421_H__ + +/** + * enum ad5421_current_range - Current range the AD5421 is configured for. + * @AD5421_CURRENT_RANGE_4mA_20mA: 4 mA to 20 mA (RANGE1,0 pins = 00) + * @AD5421_CURRENT_RANGE_3mA8_21mA: 3.8 mA to 21 mA (RANGE1,0 pins = x1) + * @AD5421_CURRENT_RANGE_3mA2_24mA: 3.2 mA to 24 mA (RANGE1,0 pins = 10) + */ + +enum ad5421_current_range { + AD5421_CURRENT_RANGE_4mA_20mA, + AD5421_CURRENT_RANGE_3mA8_21mA, + AD5421_CURRENT_RANGE_3mA2_24mA, +}; + +/** + * struct ad5421_platform_data - AD5421 DAC driver platform data + * @external_vref: whether an external reference voltage is used or not + * @current_range: Current range the AD5421 is configured for + */ + +struct ad5421_platform_data { + bool external_vref; + enum ad5421_current_range current_range; +}; + +#endif diff --git a/include/linux/iio/dac/ad5504.h b/include/linux/iio/dac/ad5504.h new file mode 100644 index 000000000000..43895376a9ca --- /dev/null +++ b/include/linux/iio/dac/ad5504.h @@ -0,0 +1,16 @@ +/* + * AD5504 SPI DAC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef SPI_AD5504_H_ +#define SPI_AD5504_H_ + +struct ad5504_platform_data { + u16 vref_mv; +}; + +#endif /* SPI_AD5504_H_ */ diff --git a/include/linux/iio/dac/ad5791.h b/include/linux/iio/dac/ad5791.h new file mode 100644 index 000000000000..45ee281c6660 --- /dev/null +++ b/include/linux/iio/dac/ad5791.h @@ -0,0 +1,25 @@ +/* + * AD5791 SPI DAC driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef SPI_AD5791_H_ +#define SPI_AD5791_H_ + +/** + * struct ad5791_platform_data - platform specific information + * @vref_pos_mv: Vdd Positive Analog Supply Volatge (mV) + * @vref_neg_mv: Vdd Negative Analog Supply Volatge (mV) + * @use_rbuf_gain2: ext. amplifier connected in gain of two configuration + */ + +struct ad5791_platform_data { + u16 vref_pos_mv; + u16 vref_neg_mv; + bool use_rbuf_gain2; +}; + +#endif /* SPI_AD5791_H_ */ diff --git a/include/linux/iio/dac/max517.h b/include/linux/iio/dac/max517.h new file mode 100644 index 000000000000..f6d1d252f08d --- /dev/null +++ b/include/linux/iio/dac/max517.h @@ -0,0 +1,15 @@ +/* + * MAX517 DAC driver + * + * Copyright 2011 Roland Stigge + * + * Licensed under the GPL-2 or later. + */ +#ifndef IIO_DAC_MAX517_H_ +#define IIO_DAC_MAX517_H_ + +struct max517_platform_data { + u16 vref_mv[2]; +}; + +#endif /* IIO_DAC_MAX517_H_ */ -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 From 72d7872852e1734e94686012a2e9deade3457329 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 16:18:26 +0300 Subject: mac80211: allow low-level drivers to set netdev feature bits Low level drivers can now set certain netdev feature bits in netdev_features member of the ieee80211_hw struct. These will be propagated to every netdev created from this HW. The white-listed features currently include only ones related to HW checksumming. Signed-off-by: Arik Nemtsov Signed-off-by: John W. Linville --- include/net/mac80211.h | 5 +++++ net/mac80211/iface.c | 2 ++ net/mac80211/main.c | 7 +++++++ 3 files changed, 14 insertions(+) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 1937c7d98304..0286c0476e44 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1297,6 +1297,10 @@ enum ieee80211_hw_flags { * reports, by default it is set to _MCS, _GI and _BW but doesn't * include _FMT. Use %IEEE80211_RADIOTAP_MCS_HAVE_* values, only * adding _BW is supported today. + * + * @netdev_features: netdev features to be set in each netdev created + * from this HW. Note only HW checksum features are currently + * compatible with mac80211. Other feature bits will be rejected. */ struct ieee80211_hw { struct ieee80211_conf conf; @@ -1319,6 +1323,7 @@ struct ieee80211_hw { u8 max_tx_aggregation_subframes; u8 offchannel_tx_hw_queue; u8 radiotap_mcs_details; + netdev_features_t netdev_features; }; /** diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d4c19a7773db..f970e0b3c4b9 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1352,6 +1352,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, sdata->u.mgd.use_4addr = params->use_4addr; } + ndev->features |= local->hw.netdev_features; + ret = register_netdevice(ndev); if (ret) goto fail; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index f5548e953259..779ac613ee57 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -682,6 +682,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) enum ieee80211_band band; int channels, max_bitrates; bool supp_ht; + netdev_features_t feature_whitelist; static const u32 cipher_suites[] = { /* keep WEP first, it may be removed below */ WLAN_CIPHER_SUITE_WEP40, @@ -708,6 +709,12 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if ((hw->flags & IEEE80211_HW_SCAN_WHILE_IDLE) && !local->ops->hw_scan) return -EINVAL; + /* Only HW csum features are currently compatible with mac80211 */ + feature_whitelist = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_HW_CSUM; + if (WARN_ON(hw->netdev_features & ~feature_whitelist)) + return -EINVAL; + if (hw->max_report_rates == 0) hw->max_report_rates = hw->max_rates; -- cgit v1.2.3 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') 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 From d63e9ae3b12fd0c6a3795c9b08de6b476f80b8c3 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 15 May 2012 14:20:31 -0700 Subject: net: mac80211: Add and use ht_vdbg debugging macro Simplify the use of #ifdef CONFIG_MAC80211_HT_DEBUG/#endif by adding a logging macro to encapsulate the test. Convert the appropriate uses too. Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- include/net/mac80211.h | 13 +++++++ net/mac80211/agg-rx.c | 24 ++++-------- net/mac80211/agg-tx.c | 103 ++++++++++++++----------------------------------- 3 files changed, 50 insertions(+), 90 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 0286c0476e44..808462e2a71d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3844,4 +3844,17 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif, */ int ieee80211_ave_rssi(struct ieee80211_vif *vif); +/* Extra debugging macros */ + +#ifdef CONFIG_MAC80211_HT_DEBUG +#define ht_vdbg(fmt, ...) \ + pr_debug(fmt, ##__VA_ARGS__) +#else +#define ht_vdbg(fmt, ...) \ +do { \ + if (0) \ + pr_debug(fmt, ##__VA_ARGS__); \ +} while (0) +#endif + #endif /* MAC80211_H */ diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index ec55f42705b7..a096b0dae37d 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -74,12 +74,10 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, RCU_INIT_POINTER(sta->ampdu_mlme.tid_rx[tid], NULL); -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("Rx BA session stop requested for %pM tid %u %s reason: %d\n", - sta->sta.addr, tid, - initiator == WLAN_BACK_RECIPIENT ? "recipient" : "inititator", - (int)reason); -#endif /* CONFIG_MAC80211_HT_DEBUG */ + ht_vdbg("Rx BA session stop requested for %pM tid %u %s reason: %d\n", + sta->sta.addr, tid, + initiator == WLAN_BACK_RECIPIENT ? "recipient" : "inititator", + (int)reason); if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP, &sta->sta, tid, NULL, 0)) @@ -154,9 +152,8 @@ static void sta_rx_agg_session_timer_expired(unsigned long data) return; } -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("rx session timer expired on tid %d\n", (u16)*ptid); -#endif + ht_vdbg("rx session timer expired on tid %d\n", (u16)*ptid); + set_bit(*ptid, sta->ampdu_mlme.tid_rx_timer_expired); ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work); } @@ -243,9 +240,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, status = WLAN_STATUS_REQUEST_DECLINED; if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("Suspend in progress - Denying ADDBA request\n"); -#endif + ht_vdbg("Suspend in progress - Denying ADDBA request\n"); goto end_no_lock; } @@ -317,10 +312,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START, &sta->sta, tid, &start_seq_num, 0); -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("Rx A-MPDU request on tid %d result %d\n", tid, ret); -#endif /* CONFIG_MAC80211_HT_DEBUG */ - + ht_vdbg("Rx A-MPDU request on tid %d result %d\n", tid, ret); if (ret) { kfree(tid_agg_rx->reorder_buf); kfree(tid_agg_rx->reorder_time); diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 90b2c0ffd5b0..da07f01cfe4d 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -184,10 +184,8 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, spin_unlock_bh(&sta->lock); -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("Tx BA session stop requested for %pM tid %u\n", - sta->sta.addr, tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ + ht_vdbg("Tx BA session stop requested for %pM tid %u\n", + sta->sta.addr, tid); del_timer_sync(&tid_tx->addba_resp_timer); del_timer_sync(&tid_tx->session_timer); @@ -253,16 +251,12 @@ static void sta_addba_resp_timer_expired(unsigned long data) if (!tid_tx || test_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) { rcu_read_unlock(); -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("timer expired on tid %d but we are not (or no longer) expecting addBA response there\n", - tid); -#endif + ht_vdbg("timer expired on tid %d but we are not (or no longer) expecting addBA response there\n", + tid); return; } -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("addBA response timer expired on tid %d\n", tid); -#endif + ht_vdbg("addBA response timer expired on tid %d\n", tid); ieee80211_stop_tx_ba_session(&sta->sta, tid); rcu_read_unlock(); @@ -371,10 +365,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START, &sta->sta, tid, &start_seq_num, 0); if (ret) { -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("BA request denied - HW unavailable for tid %d\n", - tid); -#endif + ht_vdbg("BA request denied - HW unavailable for tid %d\n", tid); spin_lock_bh(&sta->lock); ieee80211_agg_splice_packets(sdata, tid_tx, tid); ieee80211_assign_tid_tx(sta, tid, NULL); @@ -387,9 +378,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) /* activate the timer for the recipient's addBA response */ mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL); -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("activated addBA response timer on tid %d\n", tid); -#endif + ht_vdbg("activated addBA response timer on tid %d\n", tid); spin_lock_bh(&sta->lock); sta->ampdu_mlme.last_addba_req_time[tid] = jiffies; @@ -436,9 +425,7 @@ static void sta_tx_agg_session_timer_expired(unsigned long data) rcu_read_unlock(); -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("tx session timer expired on tid %d\n", (u16)*ptid); -#endif + ht_vdbg("tx session timer expired on tid %d\n", (u16)*ptid); ieee80211_stop_tx_ba_session(&sta->sta, *ptid); } @@ -462,10 +449,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW)) return -EINVAL; -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("Open BA session requested for %pM tid %u\n", - pubsta->addr, tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ + ht_vdbg("Open BA session requested for %pM tid %u\n", + pubsta->addr, tid); if (sdata->vif.type != NL80211_IFTYPE_STATION && sdata->vif.type != NL80211_IFTYPE_MESH_POINT && @@ -475,9 +460,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, return -EINVAL; if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("BA sessions blocked - Denying BA session request\n"); -#endif + ht_vdbg("BA sessions blocked - Denying BA session request\n"); return -EINVAL; } @@ -495,10 +478,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, */ if (sta->sdata->vif.type == NL80211_IFTYPE_ADHOC && !sta->sta.ht_cap.ht_supported) { -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("BA request denied - IBSS STA %pM does not advertise HT support\n", - pubsta->addr); -#endif /* CONFIG_MAC80211_HT_DEBUG */ + ht_vdbg("BA request denied - IBSS STA %pM does not advertise HT support\n", + pubsta->addr); return -EINVAL; } @@ -518,10 +499,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_BURST_RETRIES && time_before(jiffies, sta->ampdu_mlme.last_addba_req_time[tid] + HT_AGG_RETRIES_PERIOD)) { -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("BA request denied - waiting a grace period after %d failed requests on tid %u\n", - sta->ampdu_mlme.addba_req_num[tid], tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ + ht_vdbg("BA request denied - waiting a grace period after %d failed requests on tid %u\n", + sta->ampdu_mlme.addba_req_num[tid], tid); ret = -EBUSY; goto err_unlock_sta; } @@ -529,10 +508,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, tid_tx = rcu_dereference_protected_tid_tx(sta, tid); /* check if the TID is not in aggregation flow already */ if (tid_tx || sta->ampdu_mlme.tid_start_tx[tid]) { -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("BA request denied - session is not idle on tid %u\n", - tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ + ht_vdbg("BA request denied - session is not idle on tid %u\n", + tid); ret = -EAGAIN; goto err_unlock_sta; } @@ -587,9 +564,7 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, tid_tx = rcu_dereference_protected_tid_tx(sta, tid); -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("Aggregation is on for tid %d\n", tid); -#endif + ht_vdbg("Aggregation is on for tid %d\n", tid); drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_TX_OPERATIONAL, @@ -623,9 +598,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) trace_api_start_tx_ba_cb(sdata, ra, tid); if (tid >= STA_TID_NUM) { -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("Bad TID value: tid = %d (>= %d)\n", tid, STA_TID_NUM); -#endif + ht_vdbg("Bad TID value: tid = %d (>= %d)\n", tid, STA_TID_NUM); return; } @@ -633,9 +606,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) sta = sta_info_get_bss(sdata, ra); if (!sta) { mutex_unlock(&local->sta_mtx); -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("Could not find station: %pM\n", ra); -#endif + ht_vdbg("Could not find station: %pM\n", ra); return; } @@ -643,9 +614,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (WARN_ON(!tid_tx)) { -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("addBA was not requested!\n"); -#endif + ht_vdbg("addBA was not requested!\n"); goto unlock; } @@ -745,23 +714,17 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) trace_api_stop_tx_ba_cb(sdata, ra, tid); if (tid >= STA_TID_NUM) { -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("Bad TID value: tid = %d (>= %d)\n", tid, STA_TID_NUM); -#endif + ht_vdbg("Bad TID value: tid = %d (>= %d)\n", tid, STA_TID_NUM); return; } -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("Stopping Tx BA session for %pM tid %d\n", ra, tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ + ht_vdbg("Stopping Tx BA session for %pM tid %d\n", ra, tid); mutex_lock(&local->sta_mtx); sta = sta_info_get_bss(sdata, ra); if (!sta) { -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("Could not find station: %pM\n", ra); -#endif + ht_vdbg("Could not find station: %pM\n", ra); goto unlock; } @@ -770,9 +733,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (!tid_tx || !test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("unexpected callback to A-MPDU stop\n"); -#endif + ht_vdbg("unexpected callback to A-MPDU stop\n"); goto unlock_sta; } @@ -848,17 +809,13 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, goto out; if (mgmt->u.action.u.addba_resp.dialog_token != tid_tx->dialog_token) { -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("wrong addBA response token, tid %d\n", tid); -#endif + ht_vdbg("wrong addBA response token, tid %d\n", tid); goto out; } del_timer_sync(&tid_tx->addba_resp_timer); -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("switched off addBA timer for tid %d\n", tid); -#endif + ht_vdbg("switched off addBA timer for tid %d\n", tid); /* * addba_resp_timer may have fired before we got here, and @@ -867,10 +824,8 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, */ if (test_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state) || test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { -#ifdef CONFIG_MAC80211_HT_DEBUG - pr_debug("got addBA resp for tid %d but we already gave up\n", - tid); -#endif + ht_vdbg("got addBA resp for tid %d but we already gave up\n", + tid); goto out; } -- cgit v1.2.3 From 499f42bb03a9bd8a23f73e7c3886f70f52e7edc5 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 15 May 2012 14:20:32 -0700 Subject: net: mac80211: Add and use ibss_vdbg debugging macro Simplify the use of #ifdef CONFIG_MAC80211_IBSS_DEBUG/#endif by adding a logging macro to encapsulate the test. Convert the appropriate uses too. Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- include/net/mac80211.h | 11 +++++++ net/mac80211/ibss.c | 79 ++++++++++++++++++------------------------------- net/mac80211/sta_info.c | 6 ++-- 3 files changed, 41 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 808462e2a71d..98da61e52a45 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3857,4 +3857,15 @@ do { \ } while (0) #endif +#ifdef CONFIG_MAC80211_IBSS_DEBUG +#define ibss_vdbg(fmt, ...) \ + pr_debug(fmt, ##__VA_ARGS__) +#else +#define ibss_vdbg(fmt, ...) \ +do { \ + if (0) \ + pr_debug(fmt, ##__VA_ARGS__); \ +} while (0) +#endif + #endif /* MAC80211_H */ diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 148c27553024..0bc47a825692 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -281,10 +281,8 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta, if (sta_info_insert_rcu(sta)) return sta_info_get(sdata, addr); if (auth) { -#ifdef CONFIG_MAC80211_IBSS_DEBUG - pr_debug("TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n", - sdata->vif.addr, sdata->u.ibss.bssid, addr); -#endif + ibss_vdbg("TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n", + sdata->vif.addr, sdata->u.ibss.bssid, addr); ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, NULL, 0, addr, sdata->u.ibss.bssid, NULL, 0, 0); } @@ -354,11 +352,9 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) return; -#ifdef CONFIG_MAC80211_IBSS_DEBUG - pr_debug("%s: RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n", - sdata->name, mgmt->sa, mgmt->da, mgmt->bssid, - auth_transaction); -#endif + ibss_vdbg("%s: RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n", + sdata->name, mgmt->sa, mgmt->da, mgmt->bssid, + auth_transaction); sta_info_destroy_addr(sdata, mgmt->sa); ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false); rcu_read_unlock(); @@ -421,12 +417,10 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ieee80211_mandatory_rates(local, band); if (sta->sta.supp_rates[band] != prev_rates) { -#ifdef CONFIG_MAC80211_IBSS_DEBUG - pr_debug("%s: updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n", - sdata->name, sta->sta.addr, - prev_rates, - sta->sta.supp_rates[band]); -#endif + ibss_vdbg("%s: updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n", + sdata->name, sta->sta.addr, + prev_rates, + sta->sta.supp_rates[band]); rates_updated = true; } } else { @@ -541,20 +535,16 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, rx_timestamp = drv_get_tsf(local, sdata); } -#ifdef CONFIG_MAC80211_IBSS_DEBUG - pr_debug("RX beacon SA=%pM BSSID=%pM TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n", - mgmt->sa, mgmt->bssid, - (unsigned long long)rx_timestamp, - (unsigned long long)beacon_timestamp, - (unsigned long long)(rx_timestamp - beacon_timestamp), - jiffies); -#endif + ibss_vdbg("RX beacon SA=%pM BSSID=%pM TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n", + mgmt->sa, mgmt->bssid, + (unsigned long long)rx_timestamp, + (unsigned long long)beacon_timestamp, + (unsigned long long)(rx_timestamp - beacon_timestamp), + jiffies); if (beacon_timestamp > rx_timestamp) { -#ifdef CONFIG_MAC80211_IBSS_DEBUG - pr_debug("%s: beacon TSF higher than local TSF - IBSS merge with BSSID %pM\n", - sdata->name, mgmt->bssid); -#endif + ibss_vdbg("%s: beacon TSF higher than local TSF - IBSS merge with BSSID %pM\n", + sdata->name, mgmt->bssid); ieee80211_sta_join_ibss(sdata, bss); supp_rates = ieee80211_sta_get_rates(local, elems, band, NULL); ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, @@ -717,10 +707,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) lockdep_assert_held(&ifibss->mtx); active_ibss = ieee80211_sta_active_ibss(sdata); -#ifdef CONFIG_MAC80211_IBSS_DEBUG - pr_debug("%s: sta_find_ibss (active_ibss=%d)\n", - sdata->name, active_ibss); -#endif /* CONFIG_MAC80211_IBSS_DEBUG */ + ibss_vdbg("%s: sta_find_ibss (active_ibss=%d)\n", + sdata->name, active_ibss); if (active_ibss) return; @@ -743,11 +731,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) struct ieee80211_bss *bss; bss = (void *)cbss->priv; -#ifdef CONFIG_MAC80211_IBSS_DEBUG - pr_debug(" sta_find_ibss: selected %pM current %pM\n", - cbss->bssid, ifibss->bssid); -#endif /* CONFIG_MAC80211_IBSS_DEBUG */ - + ibss_vdbg(" sta_find_ibss: selected %pM current %pM\n", + cbss->bssid, ifibss->bssid); pr_debug("%s: Selected IBSS BSSID %pM based on configured SSID\n", sdata->name, cbss->bssid); @@ -756,9 +741,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) return; } -#ifdef CONFIG_MAC80211_IBSS_DEBUG - pr_debug(" did not try to join ibss\n"); -#endif /* CONFIG_MAC80211_IBSS_DEBUG */ + ibss_vdbg(" did not try to join ibss\n"); /* Selected IBSS not found in current scan results - try to scan */ if (time_after(jiffies, ifibss->last_scan_completed + @@ -815,11 +798,9 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, tx_last_beacon = drv_tx_last_beacon(local); -#ifdef CONFIG_MAC80211_IBSS_DEBUG - pr_debug("%s: RX ProbeReq SA=%pM DA=%pM BSSID=%pM (tx_last_beacon=%d)\n", - sdata->name, mgmt->sa, mgmt->da, - mgmt->bssid, tx_last_beacon); -#endif /* CONFIG_MAC80211_IBSS_DEBUG */ + ibss_vdbg("%s: RX ProbeReq SA=%pM DA=%pM BSSID=%pM (tx_last_beacon=%d)\n", + sdata->name, mgmt->sa, mgmt->da, + mgmt->bssid, tx_last_beacon); if (!tx_last_beacon && is_multicast_ether_addr(mgmt->da)) return; @@ -832,10 +813,8 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, pos = mgmt->u.probe_req.variable; if (pos[0] != WLAN_EID_SSID || pos + 2 + pos[1] > end) { -#ifdef CONFIG_MAC80211_IBSS_DEBUG - pr_debug("%s: Invalid SSID IE in ProbeReq from %pM\n", - sdata->name, mgmt->sa); -#endif + ibss_vdbg("%s: Invalid SSID IE in ProbeReq from %pM\n", + sdata->name, mgmt->sa); return; } if (pos[1] != 0 && @@ -852,9 +831,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, resp = (struct ieee80211_mgmt *) skb->data; memcpy(resp->da, mgmt->sa, ETH_ALEN); -#ifdef CONFIG_MAC80211_IBSS_DEBUG - pr_debug("%s: Sending ProbeResp to %pM\n", sdata->name, resp->da); -#endif /* CONFIG_MAC80211_IBSS_DEBUG */ + ibss_vdbg("%s: Sending ProbeResp to %pM\n", sdata->name, resp->da); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; ieee80211_tx_skb(sdata, skb); } diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 4be509807607..0a70f797dcf1 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -887,10 +887,8 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, continue; if (time_after(jiffies, sta->last_rx + exp_time)) { -#ifdef CONFIG_MAC80211_IBSS_DEBUG - pr_debug("%s: expiring inactive STA %pM\n", - sdata->name, sta->sta.addr); -#endif + ibss_vdbg("%s: expiring inactive STA %pM\n", + sdata->name, sta->sta.addr); WARN_ON(__sta_info_destroy(sta)); } } -- cgit v1.2.3 From 51ca9d8db280b960345e7306e6a036dd3880ecff Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 16 May 2012 19:09:48 +0300 Subject: mac80211: remove ieee80211_get_operstate() ieee80211_get_operstate() was used by drivers in order to know whether the sta link is up, but it's no longer needed (nor used) as mac80211 notifies the drivers about authorization changes (via the sta_state callback) Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- include/net/mac80211.h | 10 ---------- net/mac80211/mlme.c | 7 ------- 2 files changed, 17 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 98da61e52a45..d2ed86da8e4c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3555,16 +3555,6 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, enum nl80211_cqm_rssi_threshold_event rssi_event, gfp_t gfp); -/** - * ieee80211_get_operstate - get the operstate of the vif - * - * @vif: &struct ieee80211_vif pointer from the add_interface callback. - * - * The driver might need to know the operstate of the net_device - * (specifically, whether the link is IF_OPER_UP after resume) - */ -unsigned char ieee80211_get_operstate(struct ieee80211_vif *vif); - /** * ieee80211_chswitch_done - Complete channel switch process * @vif: &struct ieee80211_vif pointer from the add_interface callback. diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 419301c69582..0c4e26ff7d91 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3530,10 +3530,3 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp); } EXPORT_SYMBOL(ieee80211_cqm_rssi_notify); - -unsigned char ieee80211_get_operstate(struct ieee80211_vif *vif) -{ - struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); - return sdata->dev->operstate; -} -EXPORT_SYMBOL(ieee80211_get_operstate); -- cgit v1.2.3 From d58e7e37aac0465b08527adadc8016421bd4060e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 May 2012 23:50:17 +0200 Subject: cfg80211: simplify cfg80211_can_beacon_sec_chan API Change cfg80211_can_beacon_sec_chan() to return true if there is no secondary channel to simplify all the current users of it. They all check the channel type before calling the function because it returns false if there's no secondary channel. Also actually document the return value. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 5 ++++- net/mac80211/ibss.c | 3 +-- net/wireless/chan.c | 22 ++++++---------------- 3 files changed, 11 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 0289d4ce7070..a8496f4ff5f1 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3359,11 +3359,14 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy, const u8 *frame, size_t len, int freq, int sig_dbm, gfp_t gfp); -/* +/** * cfg80211_can_beacon_sec_chan - test if ht40 on extension channel can be used * @wiphy: the wiphy * @chan: main channel * @channel_type: HT mode + * + * This function returns true if there is no secondary channel or the secondary + * channel can be used for beaconing (i.e. is not a radar channel etc.) */ bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, struct ieee80211_channel *chan, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 0bc47a825692..725cb4be229d 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -82,8 +82,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, local->oper_channel = chan; channel_type = ifibss->channel_type; - if (channel_type > NL80211_CHAN_HT20 && - !cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type)) + if (!cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type)) channel_type = NL80211_CHAN_HT20; if (!ieee80211_set_channel_type(local, sdata, channel_type)) { /* can only fail due to HT40+/- mismatch */ diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 884801ac4dd0..20b87d895722 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -60,7 +60,7 @@ bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, diff = -20; break; default: - return false; + return true; } sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff); @@ -107,21 +107,11 @@ int cfg80211_set_freq(struct cfg80211_registered_device *rdev, wdev->iftype == NL80211_IFTYPE_AP || wdev->iftype == NL80211_IFTYPE_AP_VLAN || wdev->iftype == NL80211_IFTYPE_MESH_POINT || - wdev->iftype == NL80211_IFTYPE_P2P_GO)) { - switch (channel_type) { - case NL80211_CHAN_HT40PLUS: - case NL80211_CHAN_HT40MINUS: - if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, chan, - channel_type)) { - printk(KERN_DEBUG - "cfg80211: Secondary channel not " - "allowed to initiate communication\n"); - return -EINVAL; - } - break; - default: - break; - } + wdev->iftype == NL80211_IFTYPE_P2P_GO) && + !cfg80211_can_beacon_sec_chan(&rdev->wiphy, chan, channel_type)) { + printk(KERN_DEBUG + "cfg80211: Secondary channel not allowed to beacon\n"); + return -EINVAL; } result = rdev->ops->set_channel(&rdev->wiphy, -- cgit v1.2.3 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') 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 From cc1d2806bf06ab92268343d26eb3d8d8f00f8bc9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 May 2012 23:50:20 +0200 Subject: cfg80211: provide channel to join_mesh function Just like the AP mode patch, instead of setting the channel and then joining the mesh network, provide the channel to join the network on to the join_mesh() function. Like in AP mode, you can also give the channel to the join-mesh nl80211 command now. Unlike AP mode, it picks a default channel if none was given. As libertas uses mesh mode interfaces but has no join_mesh callback and we can't simply break it, keep some compatibility code for that case and configure the channel directly for it. In the non-libertas case, where we store the channel until join, allow setting it while the interface is down. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 4 ++ net/mac80211/cfg.c | 6 +++ net/wireless/core.h | 7 +++- net/wireless/mesh.c | 91 +++++++++++++++++++++++++++++++++++++++++++++- net/wireless/nl80211.c | 43 +++++++++++++++++----- net/wireless/wext-compat.c | 12 +++++- 6 files changed, 148 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a54fb895f613..4c90c44b8b75 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -831,6 +831,8 @@ struct mesh_config { /** * struct mesh_setup - 802.11s mesh setup configuration + * @channel: the channel to start the mesh network on + * @channel_type: the channel type to use * @mesh_id: the mesh ID * @mesh_id_len: length of the mesh ID, at least 1 and at most 32 bytes * @sync_method: which synchronization method to use @@ -845,6 +847,8 @@ struct mesh_config { * These parameters are fixed when the mesh is created. */ struct mesh_setup { + struct ieee80211_channel *channel; + enum nl80211_channel_type channel_type; const u8 *mesh_id; u8 mesh_id_len; u8 sync_method; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 8e9d525c4653..f47af8b3185e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1598,6 +1598,12 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, err = copy_mesh_setup(ifmsh, setup); if (err) return err; + + err = ieee80211_set_channel(wiphy, dev, setup->channel, + setup->channel_type); + if (err) + return err; + ieee80211_start_mesh(sdata); return 0; diff --git a/net/wireless/core.h b/net/wireless/core.h index 8523f3878677..1d3d24126946 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -303,14 +303,17 @@ extern const struct mesh_config default_mesh_config; extern const struct mesh_setup default_mesh_setup; int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev, - const struct mesh_setup *setup, + struct mesh_setup *setup, const struct mesh_config *conf); int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev, - const struct mesh_setup *setup, + struct mesh_setup *setup, const struct mesh_config *conf); int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev); +int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, int freq, + enum nl80211_channel_type channel_type); /* MLME */ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 2749cb86b462..2e3b700eba32 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -65,6 +65,9 @@ const struct mesh_config default_mesh_config = { }; const struct mesh_setup default_mesh_setup = { + /* cfg80211_join_mesh() will pick a channel if needed */ + .channel = NULL, + .channel_type = NL80211_CHAN_NO_HT, .sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET, .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP, .path_metric = IEEE80211_PATH_METRIC_AIRTIME, @@ -75,7 +78,7 @@ const struct mesh_setup default_mesh_setup = { int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev, - const struct mesh_setup *setup, + struct mesh_setup *setup, const struct mesh_config *conf) { struct wireless_dev *wdev = dev->ieee80211_ptr; @@ -101,6 +104,51 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, if (!rdev->ops->join_mesh) return -EOPNOTSUPP; + if (!setup->channel) { + /* if no channel explicitly given, use preset channel */ + setup->channel = wdev->preset_chan; + setup->channel_type = wdev->preset_chantype; + } + + if (!setup->channel) { + /* if we don't have that either, use the first usable channel */ + enum ieee80211_band band; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + int i; + + sband = rdev->wiphy.bands[band]; + if (!sband) + continue; + + for (i = 0; i < sband->n_channels; i++) { + chan = &sband->channels[i]; + if (chan->flags & (IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_PASSIVE_SCAN | + IEEE80211_CHAN_DISABLED | + IEEE80211_CHAN_RADAR)) + continue; + setup->channel = chan; + break; + } + + if (setup->channel) + break; + } + + /* no usable channel ... */ + if (!setup->channel) + return -EINVAL; + + setup->channel_type = NL80211_CHAN_NO_HT; + } + + if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, setup->channel, + setup->channel_type)) + return -EINVAL; + err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup); if (!err) { memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); @@ -112,7 +160,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev, - const struct mesh_setup *setup, + struct mesh_setup *setup, const struct mesh_config *conf) { struct wireless_dev *wdev = dev->ieee80211_ptr; @@ -125,6 +173,45 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, return err; } +int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, int freq, + enum nl80211_channel_type channel_type) +{ + struct ieee80211_channel *channel; + + /* + * Workaround for libertas (only!), it puts the interface + * into mesh mode but doesn't implement join_mesh. Instead, + * it is configured via sysfs and then joins the mesh when + * you set the channel. Note that the libertas mesh isn't + * compatible with 802.11 mesh. + */ + if (!rdev->ops->join_mesh) { + int err; + + if (!netif_running(wdev->netdev)) + return -ENETDOWN; + wdev_lock(wdev); + err = cfg80211_set_freq(rdev, wdev, freq, channel_type); + wdev_unlock(wdev); + + return err; + } + + if (wdev->mesh_id_len) + return -EBUSY; + + channel = rdev_freq_to_chan(rdev, freq, channel_type); + if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, + channel, + channel_type)) { + return -EINVAL; + } + wdev->preset_chan = channel; + wdev->preset_chantype = channel_type; + return 0; +} + void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 089a5204dad5..b22f1f876881 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -921,7 +921,8 @@ 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; } - if (dev->ops->set_channel || dev->ops->start_ap) { + if (dev->ops->set_channel || dev->ops->start_ap || + dev->ops->join_mesh) { i++; if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL)) goto nla_put_failure; @@ -1166,17 +1167,19 @@ static int parse_txq_params(struct nlattr *tb[], static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) { /* - * You can only set the channel explicitly for AP and - * mesh type interfaces; all others have their channel - * managed via their respective "establish a connection" - * command (connect, join, ...) + * You can only set the channel explicitly for WDS interfaces, + * all others have their channel managed via their respective + * "establish a connection" command (connect, join, ...) + * + * For AP/GO and mesh mode, the channel can be set with the + * channel userspace API, but is only stored and passed to the + * low-level driver when the AP starts or the mesh is joined. + * This is for backward compatibility, userspace can also give + * the channel in the start-ap or join-mesh commands instead. * * 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 || @@ -1246,6 +1249,9 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, wdev->preset_chantype = channel_type; result = 0; break; + case NL80211_IFTYPE_MESH_POINT: + result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type); + break; default: wdev_lock(wdev); result = cfg80211_set_freq(rdev, wdev, freq, channel_type); @@ -1335,8 +1341,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) result = 0; mutex_lock(&rdev->mtx); - } else if (netif_running(netdev) && - nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) + } else if (nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) wdev = netdev->ieee80211_ptr; else wdev = NULL; @@ -6080,6 +6085,24 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) return err; } + 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; + + setup.channel = rdev_freq_to_chan(rdev, + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), + channel_type); + if (!setup.channel) + return -EINVAL; + setup.channel_type = channel_type; + } else { + /* cfg80211_join_mesh() will sort it out */ + setup.channel = NULL; + } + return cfg80211_join_mesh(rdev, dev, &setup, &cfg); } diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index b082fcc26f06..faeb03548aa4 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -796,7 +796,6 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, case NL80211_IFTYPE_ADHOC: return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra); case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_MESH_POINT: freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); if (freq < 0) return freq; @@ -808,6 +807,17 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, wdev_unlock(wdev); mutex_unlock(&rdev->devlist_mtx); return err; + case NL80211_IFTYPE_MESH_POINT: + freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); + if (freq < 0) + return freq; + if (freq == 0) + return -EINVAL; + mutex_lock(&rdev->devlist_mtx); + err = cfg80211_set_mesh_freq(rdev, wdev, freq, + NL80211_CHAN_NO_HT); + mutex_unlock(&rdev->devlist_mtx); + return err; default: return -EOPNOTSUPP; } -- cgit v1.2.3 From bacef661acdb634170a8faddbc1cf28e8f8b9eee Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Fri, 25 May 2012 16:20:31 +0100 Subject: x86-64/efi: Use EFI to deal with platform wall clock Other than ix86, x86-64 on EFI so far didn't set the {g,s}et_wallclock accessors to the EFI routines, thus incorrectly using raw RTC accesses instead. Simply removing the #ifdef around the respective code isn't enough, however: While so far early get-time calls were done in physical mode, this doesn't work properly for x86-64, as virtual addresses would still need to be set up for all runtime regions (which wasn't the case on the system I have access to), so instead the patch moves the call to efi_enter_virtual_mode() ahead (which in turn allows to drop all code related to calling efi-get-time in physical mode). Additionally the earlier calling of efi_set_executable() requires the CPA code to cope, i.e. during early boot it must be avoided to call cpa_flush_array(), as the first thing this function does is a BUG_ON(irqs_disabled()). Also make the two EFI functions in question here static - they're not being referenced elsewhere. Signed-off-by: Jan Beulich Tested-by: Matt Fleming Acked-by: Matthew Garrett Cc: Linus Torvalds Cc: Andrew Morton Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/4FBFBF5F020000780008637F@nat28.tlf.novell.com Signed-off-by: Ingo Molnar --- arch/x86/mm/pageattr.c | 10 ++++++---- arch/x86/platform/efi/efi.c | 30 ++++-------------------------- include/linux/efi.h | 2 -- init/main.c | 8 ++++---- 4 files changed, 14 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index e1ebde315210..ee09aca63998 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -919,11 +919,13 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages, /* * On success we use clflush, when the CPU supports it to - * avoid the wbindv. If the CPU does not support it and in the - * error case we fall back to cpa_flush_all (which uses - * wbindv): + * avoid the wbindv. If the CPU does not support it, in the + * error case, and during early boot (for EFI) we fall back + * to cpa_flush_all (which uses wbinvd): */ - if (!ret && cpu_has_clflush) { + if (early_boot_irqs_disabled) + __cpa_flush_all((void *)(long)cache); + else if (!ret && cpu_has_clflush) { if (cpa.flags & (CPA_PAGES_ARRAY | CPA_ARRAY)) { cpa_flush_array(addr, numpages, cache, cpa.flags, pages); diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 92660edaa1e7..2dc29f51e75a 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -234,22 +234,7 @@ static efi_status_t __init phys_efi_set_virtual_address_map( return status; } -static efi_status_t __init phys_efi_get_time(efi_time_t *tm, - efi_time_cap_t *tc) -{ - unsigned long flags; - efi_status_t status; - - spin_lock_irqsave(&rtc_lock, flags); - efi_call_phys_prelog(); - status = efi_call_phys2(efi_phys.get_time, virt_to_phys(tm), - virt_to_phys(tc)); - efi_call_phys_epilog(); - spin_unlock_irqrestore(&rtc_lock, flags); - return status; -} - -int efi_set_rtc_mmss(unsigned long nowtime) +static int efi_set_rtc_mmss(unsigned long nowtime) { int real_seconds, real_minutes; efi_status_t status; @@ -278,7 +263,7 @@ int efi_set_rtc_mmss(unsigned long nowtime) return 0; } -unsigned long efi_get_time(void) +static unsigned long efi_get_time(void) { efi_status_t status; efi_time_t eft; @@ -621,18 +606,13 @@ static int __init efi_runtime_init(void) } /* * We will only need *early* access to the following - * two EFI runtime services before set_virtual_address_map + * EFI runtime service before set_virtual_address_map * is invoked. */ - efi_phys.get_time = (efi_get_time_t *)runtime->get_time; efi_phys.set_virtual_address_map = (efi_set_virtual_address_map_t *) runtime->set_virtual_address_map; - /* - * Make efi_get_time can be called before entering - * virtual mode. - */ - efi.get_time = phys_efi_get_time; + early_iounmap(runtime, sizeof(efi_runtime_services_t)); return 0; @@ -720,12 +700,10 @@ void __init efi_init(void) efi_enabled = 0; return; } -#ifdef CONFIG_X86_32 if (efi_native) { x86_platform.get_wallclock = efi_get_time; x86_platform.set_wallclock = efi_set_rtc_mmss; } -#endif #if EFI_DEBUG print_efi_memmap(); diff --git a/include/linux/efi.h b/include/linux/efi.h index ec45ccd8708a..103adc6d7e3a 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -503,8 +503,6 @@ extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size); extern int __init efi_uart_console_only (void); extern void efi_initialize_iomem_resources(struct resource *code_resource, struct resource *data_resource, struct resource *bss_resource); -extern unsigned long efi_get_time(void); -extern int efi_set_rtc_mmss(unsigned long nowtime); extern void efi_reserve_boot_services(void); extern struct efi_memory_map memmap; diff --git a/init/main.c b/init/main.c index 1ca6b32c4828..eef30128321a 100644 --- a/init/main.c +++ b/init/main.c @@ -460,6 +460,10 @@ static void __init mm_init(void) percpu_init_late(); pgtable_cache_init(); vmalloc_init(); +#ifdef CONFIG_X86 + if (efi_enabled) + efi_enter_virtual_mode(); +#endif } asmlinkage void __init start_kernel(void) @@ -601,10 +605,6 @@ asmlinkage void __init start_kernel(void) calibrate_delay(); pidmap_init(); anon_vma_init(); -#ifdef CONFIG_X86 - if (efi_enabled) - efi_enter_virtual_mode(); -#endif thread_info_cache_init(); cred_init(); fork_init(totalram_pages); -- cgit v1.2.3 From eac1f14fd1e7243aa782ef85f2a217e0c3a709ad Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 5 Jun 2012 15:24:24 -0700 Subject: drm/i915: Inifite timeout for wait ioctl Change the ns_timeout parameter of the wait ioctl to a signed value. Doing this allows the kernel to provide an infinite wait when a timeout of less than 0 is provided. This mimics select/poll. Initially the parameter was meant to match up with the GL spec 1:1, but after being made aware of how much 2^64 - 1 nanoseconds actually is, I do not think anyone will ever notice the loss of 1 bit. The infinite timeout on waiting is similar to the existing i915 userspace interface with the exception that struct_mutex is dropped while doing the wait in this ioctl. Cc: Chris Wilson Signed-off-by: Ben Widawsky Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 15 ++++++++++----- include/drm/i915_drm.h | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index af67803e635f..deaa0d4bb456 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2082,11 +2082,14 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) struct drm_i915_gem_wait *args = data; struct drm_i915_gem_object *obj; struct intel_ring_buffer *ring = NULL; - struct timespec timeout; + struct timespec timeout_stack, *timeout = NULL; u32 seqno = 0; int ret = 0; - timeout = ns_to_timespec(args->timeout_ns); + if (args->timeout_ns >= 0) { + timeout_stack = ns_to_timespec(args->timeout_ns); + timeout = &timeout_stack; + } ret = i915_mutex_lock_interruptible(dev); if (ret) @@ -2122,9 +2125,11 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) drm_gem_object_unreference(&obj->base); mutex_unlock(&dev->struct_mutex); - ret = __wait_seqno(ring, seqno, true, &timeout); - WARN_ON(!timespec_valid(&timeout)); - args->timeout_ns = timespec_to_ns(&timeout); + ret = __wait_seqno(ring, seqno, true, timeout); + if (timeout) { + WARN_ON(!timespec_valid(timeout)); + args->timeout_ns = timespec_to_ns(timeout); + } return ret; out: diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index bab174334da5..aae346e7f635 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -893,7 +893,7 @@ struct drm_i915_gem_wait { __u32 bo_handle; __u32 flags; /** Number of nanoseconds to wait, Returns time remaining. */ - __u64 timeout_ns; + __s64 timeout_ns; }; #endif /* _I915_DRM_H_ */ -- cgit v1.2.3 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') 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 From 172cf15d18889313bf2c3bfb81fcea08369274ef Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 5 Jun 2012 15:24:25 -0700 Subject: drm/i915: Add wait render timeout get param Signed-off-by: Ben Widawsky Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 +++ include/drm/i915_drm.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 262a74d1f852..97a5a5857f5b 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1006,6 +1006,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_ALIASING_PPGTT: value = dev_priv->mm.aliasing_ppgtt ? 1 : 0; break; + case I915_PARAM_HAS_WAIT_TIMEOUT: + value = 1; + break; default: DRM_DEBUG_DRIVER("Unknown parameter %d\n", param->param); diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index aae346e7f635..5c28ee1d1911 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -300,6 +300,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_HAS_GEN7_SOL_RESET 16 #define I915_PARAM_HAS_LLC 17 #define I915_PARAM_HAS_ALIASING_PPGTT 18 +#define I915_PARAM_HAS_WAIT_TIMEOUT 19 typedef struct drm_i915_getparam { int param; -- cgit v1.2.3 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') 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 From 67130934fb579fdf0f2f6d745960264378b57dc8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 26 May 2012 23:26:43 -0500 Subject: libceph: embed ceph connection structure in mon_client A monitor client has a pointer to a ceph connection structure in it. This is the only one of the three ceph client types that do it this way; the OSD and MDS clients embed the connection into their main structures. There is always exactly one ceph connection for a monitor client, so there is no need to allocate it separate from the monitor client structure. So switch the ceph_mon_client structure to embed its ceph_connection structure. Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- include/linux/ceph/mon_client.h | 2 +- net/ceph/mon_client.c | 47 ++++++++++++++++++----------------------- 2 files changed, 21 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/mon_client.h b/include/linux/ceph/mon_client.h index 545f85917780..2113e3850a4e 100644 --- a/include/linux/ceph/mon_client.h +++ b/include/linux/ceph/mon_client.h @@ -70,7 +70,7 @@ struct ceph_mon_client { bool hunting; int cur_mon; /* last monitor i contacted */ unsigned long sub_sent, sub_renew_after; - struct ceph_connection *con; + struct ceph_connection con; bool have_fsid; /* pending generic requests */ diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index 704dc95dc620..ac4d6b100730 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -106,9 +106,9 @@ static void __send_prepared_auth_request(struct ceph_mon_client *monc, int len) monc->pending_auth = 1; monc->m_auth->front.iov_len = len; monc->m_auth->hdr.front_len = cpu_to_le32(len); - ceph_con_revoke(monc->con, monc->m_auth); + ceph_con_revoke(&monc->con, monc->m_auth); ceph_msg_get(monc->m_auth); /* keep our ref */ - ceph_con_send(monc->con, monc->m_auth); + ceph_con_send(&monc->con, monc->m_auth); } /* @@ -117,8 +117,8 @@ static void __send_prepared_auth_request(struct ceph_mon_client *monc, int len) static void __close_session(struct ceph_mon_client *monc) { dout("__close_session closing mon%d\n", monc->cur_mon); - ceph_con_revoke(monc->con, monc->m_auth); - ceph_con_close(monc->con); + ceph_con_revoke(&monc->con, monc->m_auth); + ceph_con_close(&monc->con); monc->cur_mon = -1; monc->pending_auth = 0; ceph_auth_reset(monc->auth); @@ -142,9 +142,9 @@ static int __open_session(struct ceph_mon_client *monc) monc->want_next_osdmap = !!monc->want_next_osdmap; dout("open_session mon%d opening\n", monc->cur_mon); - monc->con->peer_name.type = CEPH_ENTITY_TYPE_MON; - monc->con->peer_name.num = cpu_to_le64(monc->cur_mon); - ceph_con_open(monc->con, + monc->con.peer_name.type = CEPH_ENTITY_TYPE_MON; + monc->con.peer_name.num = cpu_to_le64(monc->cur_mon); + ceph_con_open(&monc->con, &monc->monmap->mon_inst[monc->cur_mon].addr); /* initiatiate authentication handshake */ @@ -226,8 +226,8 @@ static void __send_subscribe(struct ceph_mon_client *monc) msg->front.iov_len = p - msg->front.iov_base; msg->hdr.front_len = cpu_to_le32(msg->front.iov_len); - ceph_con_revoke(monc->con, msg); - ceph_con_send(monc->con, ceph_msg_get(msg)); + ceph_con_revoke(&monc->con, msg); + ceph_con_send(&monc->con, ceph_msg_get(msg)); monc->sub_sent = jiffies | 1; /* never 0 */ } @@ -247,7 +247,7 @@ static void handle_subscribe_ack(struct ceph_mon_client *monc, if (monc->hunting) { pr_info("mon%d %s session established\n", monc->cur_mon, - ceph_pr_addr(&monc->con->peer_addr.in_addr)); + ceph_pr_addr(&monc->con.peer_addr.in_addr)); monc->hunting = false; } dout("handle_subscribe_ack after %d seconds\n", seconds); @@ -461,7 +461,7 @@ static int do_generic_request(struct ceph_mon_client *monc, req->request->hdr.tid = cpu_to_le64(req->tid); __insert_generic_request(monc, req); monc->num_generic_requests++; - ceph_con_send(monc->con, ceph_msg_get(req->request)); + ceph_con_send(&monc->con, ceph_msg_get(req->request)); mutex_unlock(&monc->mutex); err = wait_for_completion_interruptible(&req->completion); @@ -684,8 +684,8 @@ static void __resend_generic_request(struct ceph_mon_client *monc) for (p = rb_first(&monc->generic_request_tree); p; p = rb_next(p)) { req = rb_entry(p, struct ceph_mon_generic_request, node); - ceph_con_revoke(monc->con, req->request); - ceph_con_send(monc->con, ceph_msg_get(req->request)); + ceph_con_revoke(&monc->con, req->request); + ceph_con_send(&monc->con, ceph_msg_get(req->request)); } } @@ -705,7 +705,7 @@ static void delayed_work(struct work_struct *work) __close_session(monc); __open_session(monc); /* continue hunting */ } else { - ceph_con_keepalive(monc->con); + ceph_con_keepalive(&monc->con); __validate_auth(monc); @@ -760,19 +760,16 @@ int ceph_monc_init(struct ceph_mon_client *monc, struct ceph_client *cl) goto out; /* connection */ - monc->con = kmalloc(sizeof(*monc->con), GFP_KERNEL); - if (!monc->con) - goto out_monmap; - ceph_con_init(&monc->client->msgr, monc->con); - monc->con->private = monc; - monc->con->ops = &mon_con_ops; + ceph_con_init(&monc->client->msgr, &monc->con); + monc->con.private = monc; + monc->con.ops = &mon_con_ops; /* authentication */ monc->auth = ceph_auth_init(cl->options->name, cl->options->key); if (IS_ERR(monc->auth)) { err = PTR_ERR(monc->auth); - goto out_con; + goto out_monmap; } monc->auth->want_keys = CEPH_ENTITY_TYPE_AUTH | CEPH_ENTITY_TYPE_MON | @@ -824,8 +821,6 @@ out_subscribe_ack: ceph_msg_put(monc->m_subscribe_ack); out_auth: ceph_auth_destroy(monc->auth); -out_con: - monc->con->ops->put(monc->con); out_monmap: kfree(monc->monmap); out: @@ -841,9 +836,7 @@ void ceph_monc_stop(struct ceph_mon_client *monc) mutex_lock(&monc->mutex); __close_session(monc); - monc->con->private = NULL; - monc->con->ops->put(monc->con); - monc->con = NULL; + monc->con.private = NULL; mutex_unlock(&monc->mutex); @@ -1021,7 +1014,7 @@ static void mon_fault(struct ceph_connection *con) if (!monc->hunting) pr_info("mon%d %s session lost, " "hunting for new mon\n", monc->cur_mon, - ceph_pr_addr(&monc->con->peer_addr.in_addr)); + ceph_pr_addr(&monc->con.peer_addr.in_addr)); __close_session(monc); if (!monc->hunting) { -- cgit v1.2.3 From 1bfd89f4e6e1adc6a782d94aa5d4c53be1e404d7 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 26 May 2012 23:26:43 -0500 Subject: libceph: fully initialize connection in con_init() Move the initialization of a ceph connection's private pointer, operations vector pointer, and peer name information into ceph_con_init(). Rearrange the arguments so the connection pointer is first. Hide the byte-swapping of the peer entity number inside ceph_con_init() Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- fs/ceph/mds_client.c | 7 ++----- include/linux/ceph/messenger.h | 6 ++++-- net/ceph/messenger.c | 9 ++++++++- net/ceph/mon_client.c | 8 +++----- net/ceph/osd_client.c | 7 ++----- 5 files changed, 19 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index ad30261cd4c0..ecd7f15741c1 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -394,11 +394,8 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, s->s_seq = 0; mutex_init(&s->s_mutex); - ceph_con_init(&mdsc->fsc->client->msgr, &s->s_con); - s->s_con.private = s; - s->s_con.ops = &mds_con_ops; - s->s_con.peer_name.type = CEPH_ENTITY_TYPE_MDS; - s->s_con.peer_name.num = cpu_to_le64(mds); + ceph_con_init(&s->s_con, s, &mds_con_ops, &mdsc->fsc->client->msgr, + CEPH_ENTITY_TYPE_MDS, mds); spin_lock_init(&s->s_gen_ttl_lock); s->s_cap_gen = 0; diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 5e852f444f68..dd27837f79ac 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -227,8 +227,10 @@ extern void ceph_messenger_init(struct ceph_messenger *msgr, u32 required_features, bool nocrc); -extern void ceph_con_init(struct ceph_messenger *msgr, - struct ceph_connection *con); +extern void ceph_con_init(struct ceph_connection *con, void *private, + const struct ceph_connection_operations *ops, + struct ceph_messenger *msgr, __u8 entity_type, + __u64 entity_num); extern void ceph_con_open(struct ceph_connection *con, struct ceph_entity_addr *addr); extern bool ceph_con_opened(struct ceph_connection *con); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 36b440a00cc2..3b65f6e6911b 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -521,15 +521,22 @@ void ceph_con_put(struct ceph_connection *con) /* * initialize a new connection. */ -void ceph_con_init(struct ceph_messenger *msgr, struct ceph_connection *con) +void ceph_con_init(struct ceph_connection *con, void *private, + const struct ceph_connection_operations *ops, + struct ceph_messenger *msgr, __u8 entity_type, __u64 entity_num) { dout("con_init %p\n", con); memset(con, 0, sizeof(*con)); + con->private = private; + con->ops = ops; atomic_set(&con->nref, 1); con->msgr = msgr; con_sock_state_init(con); + con->peer_name.type = (__u8) entity_type; + con->peer_name.num = cpu_to_le64(entity_num); + mutex_init(&con->mutex); INIT_LIST_HEAD(&con->out_queue); INIT_LIST_HEAD(&con->out_sent); diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index 6adbea78b168..ab6b24a5169e 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -142,11 +142,9 @@ static int __open_session(struct ceph_mon_client *monc) monc->sub_renew_after = jiffies; /* i.e., expired */ monc->want_next_osdmap = !!monc->want_next_osdmap; - ceph_con_init(&monc->client->msgr, &monc->con); - monc->con.private = monc; - monc->con.ops = &mon_con_ops; - monc->con.peer_name.type = CEPH_ENTITY_TYPE_MON; - monc->con.peer_name.num = cpu_to_le64(monc->cur_mon); + ceph_con_init(&monc->con, monc, &mon_con_ops, + &monc->client->msgr, + CEPH_ENTITY_TYPE_MON, monc->cur_mon); dout("open_session mon%d opening\n", monc->cur_mon); ceph_con_open(&monc->con, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 5b41a6929cd9..448c9da8beff 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -640,11 +640,8 @@ static struct ceph_osd *create_osd(struct ceph_osd_client *osdc, int onum) INIT_LIST_HEAD(&osd->o_osd_lru); osd->o_incarnation = 1; - ceph_con_init(&osdc->client->msgr, &osd->o_con); - osd->o_con.private = osd; - osd->o_con.ops = &osd_con_ops; - osd->o_con.peer_name.type = CEPH_ENTITY_TYPE_OSD; - osd->o_con.peer_name.num = cpu_to_le64(onum); + ceph_con_init(&osd->o_con, osd, &osd_con_ops, &osdc->client->msgr, + CEPH_ENTITY_TYPE_OSD, onum); INIT_LIST_HEAD(&osd->o_keepalive_item); return osd; -- cgit v1.2.3 From 38941f8031bf042dba3ced6394ba3a3b16c244ea Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 1 Jun 2012 14:56:43 -0500 Subject: libceph: have messages point to their connection When a ceph message is queued for sending it is placed on a list of pending messages (ceph_connection->out_queue). When they are actually sent over the wire, they are moved from that list to another (ceph_connection->out_sent). When acknowledgement for the message is received, it is removed from the sent messages list. During that entire time the message is "in the possession" of a single ceph connection. Keep track of that connection in the message. This will be used in the next patch (and is a helpful bit of information for debugging anyway). Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- include/linux/ceph/messenger.h | 3 +++ net/ceph/messenger.c | 27 +++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index dd27837f79ac..6df837f72761 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -77,7 +77,10 @@ struct ceph_msg { unsigned nr_pages; /* size of page array */ unsigned page_alignment; /* io offset in first page */ struct ceph_pagelist *pagelist; /* instead of pages */ + + struct ceph_connection *con; struct list_head list_head; + struct kref kref; struct bio *bio; /* instead of pages/pagelist */ struct bio *bio_iter; /* bio iterator */ diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 98ca23726ea6..68b49b5b8e86 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -414,6 +414,9 @@ static int con_close_socket(struct ceph_connection *con) static void ceph_msg_remove(struct ceph_msg *msg) { list_del_init(&msg->list_head); + BUG_ON(msg->con == NULL); + msg->con = NULL; + ceph_msg_put(msg); } static void ceph_msg_remove_list(struct list_head *head) @@ -433,6 +436,8 @@ static void reset_connection(struct ceph_connection *con) ceph_msg_remove_list(&con->out_sent); if (con->in_msg) { + BUG_ON(con->in_msg->con != con); + con->in_msg->con = NULL; ceph_msg_put(con->in_msg); con->in_msg = NULL; } @@ -625,8 +630,10 @@ static void prepare_write_message(struct ceph_connection *con) &con->out_temp_ack); } + BUG_ON(list_empty(&con->out_queue)); m = list_first_entry(&con->out_queue, struct ceph_msg, list_head); con->out_msg = m; + BUG_ON(m->con != con); /* put message on sent list */ ceph_msg_get(m); @@ -1806,6 +1813,8 @@ static int read_partial_message(struct ceph_connection *con) "error allocating memory for incoming message"; return -ENOMEM; } + + BUG_ON(con->in_msg->con != con); m = con->in_msg; m->front.iov_len = 0; /* haven't read it yet */ if (m->middle) @@ -1901,6 +1910,8 @@ static void process_message(struct ceph_connection *con) { struct ceph_msg *msg; + BUG_ON(con->in_msg->con != con); + con->in_msg->con = NULL; msg = con->in_msg; con->in_msg = NULL; @@ -2260,6 +2271,8 @@ static void ceph_fault(struct ceph_connection *con) con_close_socket(con); if (con->in_msg) { + BUG_ON(con->in_msg->con != con); + con->in_msg->con = NULL; ceph_msg_put(con->in_msg); con->in_msg = NULL; } @@ -2378,6 +2391,8 @@ void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg) /* queue */ mutex_lock(&con->mutex); + BUG_ON(msg->con != NULL); + msg->con = con; BUG_ON(!list_empty(&msg->list_head)); list_add_tail(&msg->list_head, &con->out_queue); dout("----- %p to %s%lld %d=%s len %d+%d+%d -----\n", msg, @@ -2403,13 +2418,16 @@ void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg) { mutex_lock(&con->mutex); if (!list_empty(&msg->list_head)) { - dout("con_revoke %p msg %p - was on queue\n", con, msg); + dout("%s %p msg %p - was on queue\n", __func__, con, msg); list_del_init(&msg->list_head); + BUG_ON(msg->con == NULL); + msg->con = NULL; + ceph_msg_put(msg); msg->hdr.seq = 0; } if (con->out_msg == msg) { - dout("con_revoke %p msg %p - was sending\n", con, msg); + dout("%s %p msg %p - was sending\n", __func__, con, msg); con->out_msg = NULL; if (con->out_kvec_is_msg) { con->out_skip = con->out_kvec_bytes; @@ -2478,6 +2496,8 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags, if (m == NULL) goto out; kref_init(&m->kref); + + m->con = NULL; INIT_LIST_HEAD(&m->list_head); m->hdr.tid = 0; @@ -2598,6 +2618,8 @@ static bool ceph_con_in_msg_alloc(struct ceph_connection *con, mutex_unlock(&con->mutex); con->in_msg = con->ops->alloc_msg(con, hdr, &skip); mutex_lock(&con->mutex); + if (con->in_msg) + con->in_msg->con = con; if (skip) con->in_msg = NULL; @@ -2611,6 +2633,7 @@ static bool ceph_con_in_msg_alloc(struct ceph_connection *con, type, front_len); return false; } + con->in_msg->con = con; con->in_msg->page_alignment = le16_to_cpu(hdr->data_off); } memcpy(&con->in_msg->hdr, &con->in_hdr, sizeof(con->in_hdr)); -- cgit v1.2.3 From 6740a845b2543cc46e1902ba21bac743fbadd0dc Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 1 Jun 2012 14:56:43 -0500 Subject: libceph: make ceph_con_revoke() a msg operation ceph_con_revoke() is passed both a message and a ceph connection. Now that any message associated with a connection holds a pointer to that connection, there's no need to provide the connection when revoking a message. This has the added benefit of precluding the possibility of the providing the wrong connection pointer. If the message's connection pointer is null, it is not being tracked by any connection, so revoking it is a no-op. This is supported as a convenience for upper layers, so they can revoke a message that is not actually "in flight." Rename the function ceph_msg_revoke() to reflect that it is really an operation on a message, not a connection. Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- include/linux/ceph/messenger.h | 3 ++- net/ceph/messenger.c | 7 ++++++- net/ceph/mon_client.c | 8 ++++---- net/ceph/osd_client.c | 4 ++-- 4 files changed, 14 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 6df837f72761..9008f81c20cd 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -239,7 +239,8 @@ extern void ceph_con_open(struct ceph_connection *con, extern bool ceph_con_opened(struct ceph_connection *con); extern void ceph_con_close(struct ceph_connection *con); extern void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg); -extern void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg); + +extern void ceph_msg_revoke(struct ceph_msg *msg); extern void ceph_con_revoke_message(struct ceph_connection *con, struct ceph_msg *msg); extern void ceph_con_keepalive(struct ceph_connection *con); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 88ac083bb995..d636903ad4b2 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -2421,8 +2421,13 @@ EXPORT_SYMBOL(ceph_con_send); /* * Revoke a message that was previously queued for send */ -void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg) +void ceph_msg_revoke(struct ceph_msg *msg) { + struct ceph_connection *con = msg->con; + + if (!con) + return; /* Message not in our possession */ + mutex_lock(&con->mutex); if (!list_empty(&msg->list_head)) { dout("%s %p msg %p - was on queue\n", __func__, con, msg); diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index 8462ccec6333..7a16750d62a6 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -106,7 +106,7 @@ static void __send_prepared_auth_request(struct ceph_mon_client *monc, int len) monc->pending_auth = 1; monc->m_auth->front.iov_len = len; monc->m_auth->hdr.front_len = cpu_to_le32(len); - ceph_con_revoke(&monc->con, monc->m_auth); + ceph_msg_revoke(monc->m_auth); ceph_msg_get(monc->m_auth); /* keep our ref */ ceph_con_send(&monc->con, monc->m_auth); } @@ -117,7 +117,7 @@ static void __send_prepared_auth_request(struct ceph_mon_client *monc, int len) static void __close_session(struct ceph_mon_client *monc) { dout("__close_session closing mon%d\n", monc->cur_mon); - ceph_con_revoke(&monc->con, monc->m_auth); + ceph_msg_revoke(monc->m_auth); ceph_con_close(&monc->con); monc->con.private = NULL; monc->cur_mon = -1; @@ -229,7 +229,7 @@ static void __send_subscribe(struct ceph_mon_client *monc) msg->front.iov_len = p - msg->front.iov_base; msg->hdr.front_len = cpu_to_le32(msg->front.iov_len); - ceph_con_revoke(&monc->con, msg); + ceph_msg_revoke(msg); ceph_con_send(&monc->con, ceph_msg_get(msg)); monc->sub_sent = jiffies | 1; /* never 0 */ @@ -688,7 +688,7 @@ static void __resend_generic_request(struct ceph_mon_client *monc) for (p = rb_first(&monc->generic_request_tree); p; p = rb_next(p)) { req = rb_entry(p, struct ceph_mon_generic_request, node); - ceph_con_revoke(&monc->con, req->request); + ceph_msg_revoke(req->request); ceph_con_send(&monc->con, ceph_msg_get(req->request)); } } diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 24b427b1eca4..ad78705a4aff 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -852,7 +852,7 @@ static void __unregister_request(struct ceph_osd_client *osdc, if (req->r_osd) { /* make sure the original request isn't in flight. */ - ceph_con_revoke(&req->r_osd->o_con, req->r_request); + ceph_msg_revoke(req->r_request); list_del_init(&req->r_osd_item); if (list_empty(&req->r_osd->o_requests) && @@ -879,7 +879,7 @@ static void __unregister_request(struct ceph_osd_client *osdc, static void __cancel_request(struct ceph_osd_request *req) { if (req->r_sent && req->r_osd) { - ceph_con_revoke(&req->r_osd->o_con, req->r_request); + ceph_msg_revoke(req->r_request); req->r_sent = 0; } } -- cgit v1.2.3 From 8921d114f5574c6da2cdd00749d185633ecf88f3 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 1 Jun 2012 14:56:43 -0500 Subject: libceph: make ceph_con_revoke_message() a msg op ceph_con_revoke_message() is passed both a message and a ceph connection. A ceph_msg allocated for incoming messages on a connection always has a pointer to that connection, so there's no need to provide the connection when revoking such a message. Note that the existing logic does not preclude the message supplied being a null/bogus message pointer. The only user of this interface is the OSD client, and the only value an osd client passes is a request's r_reply field. That is always non-null (except briefly in an error path in ceph_osdc_alloc_request(), and that drops the only reference so the request won't ever have a reply to revoke). So we can safely assume the passed-in message is non-null, but add a BUG_ON() to make it very obvious we are imposing this restriction. Rename the function ceph_msg_revoke_incoming() to reflect that it is really an operation on an incoming message. Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- include/linux/ceph/messenger.h | 4 ++-- net/ceph/messenger.c | 22 ++++++++++++++++------ net/ceph/osd_client.c | 9 ++++----- 3 files changed, 22 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 9008f81c20cd..a334dbd1b324 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -241,8 +241,8 @@ extern void ceph_con_close(struct ceph_connection *con); extern void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg); extern void ceph_msg_revoke(struct ceph_msg *msg); -extern void ceph_con_revoke_message(struct ceph_connection *con, - struct ceph_msg *msg); +extern void ceph_msg_revoke_incoming(struct ceph_msg *msg); + extern void ceph_con_keepalive(struct ceph_connection *con); extern struct ceph_connection *ceph_con_get(struct ceph_connection *con); extern void ceph_con_put(struct ceph_connection *con); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index d636903ad4b2..3857f815c035 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -2456,17 +2456,27 @@ void ceph_msg_revoke(struct ceph_msg *msg) /* * Revoke a message that we may be reading data into */ -void ceph_con_revoke_message(struct ceph_connection *con, struct ceph_msg *msg) +void ceph_msg_revoke_incoming(struct ceph_msg *msg) { + struct ceph_connection *con; + + BUG_ON(msg == NULL); + if (!msg->con) { + dout("%s msg %p null con\n", __func__, msg); + + return; /* Message not in our possession */ + } + + con = msg->con; mutex_lock(&con->mutex); - if (con->in_msg && con->in_msg == msg) { + if (con->in_msg == msg) { unsigned front_len = le32_to_cpu(con->in_hdr.front_len); unsigned middle_len = le32_to_cpu(con->in_hdr.middle_len); unsigned data_len = le32_to_cpu(con->in_hdr.data_len); /* skip rest of message */ - dout("con_revoke_pages %p msg %p revoked\n", con, msg); - con->in_base_pos = con->in_base_pos - + dout("%s %p msg %p revoked\n", __func__, con, msg); + con->in_base_pos = con->in_base_pos - sizeof(struct ceph_msg_header) - front_len - middle_len - @@ -2477,8 +2487,8 @@ void ceph_con_revoke_message(struct ceph_connection *con, struct ceph_msg *msg) con->in_tag = CEPH_MSGR_TAG_READY; con->in_seq++; } else { - dout("con_revoke_pages %p msg %p pages %p no-op\n", - con, con->in_msg, msg); + dout("%s %p in_msg %p msg %p no-op\n", + __func__, con, con->in_msg, msg); } mutex_unlock(&con->mutex); } diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index ad78705a4aff..c178c770acb4 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -140,10 +140,9 @@ void ceph_osdc_release_request(struct kref *kref) if (req->r_request) ceph_msg_put(req->r_request); if (req->r_con_filling_msg) { - dout("release_request revoking pages %p from con %p\n", + dout("%s revoking pages %p from con %p\n", __func__, req->r_pages, req->r_con_filling_msg); - ceph_con_revoke_message(req->r_con_filling_msg, - req->r_reply); + ceph_msg_revoke_incoming(req->r_reply); req->r_con_filling_msg->ops->put(req->r_con_filling_msg); } if (req->r_reply) @@ -2022,9 +2021,9 @@ static struct ceph_msg *get_reply(struct ceph_connection *con, } if (req->r_con_filling_msg) { - dout("get_reply revoking msg %p from old con %p\n", + dout("%s revoking msg %p from old con %p\n", __func__, req->r_reply, req->r_con_filling_msg); - ceph_con_revoke_message(req->r_con_filling_msg, req->r_reply); + ceph_msg_revoke_incoming(req->r_reply); req->r_con_filling_msg->ops->put(req->r_con_filling_msg); req->r_con_filling_msg = NULL; } -- cgit v1.2.3 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') 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 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') 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 From e8c9bd5b8d807cfe6c923265969a523b1ba1e6c2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 6 Jun 2012 08:18:22 +0200 Subject: cfg80211: clarify set_channel APIs Now that we've removed all uses of the set_channel API except for the monitor channel and in libertas, clarify this. Split the libertas mesh use into a new libertas_set_mesh_channel() operation, just to keep backward compatibility, and rename the normal set_channel() to set_monitor_channel(). Also describe the desired set_monitor_channel() semantics more clearly. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/cfg.c | 39 +++++++++++++++++++++++--------- drivers/net/wireless/libertas/dev.h | 1 + drivers/net/wireless/libertas/mesh.c | 7 ++---- drivers/net/wireless/orinoco/cfg.c | 9 ++++---- include/net/cfg80211.h | 24 ++++++++++++-------- net/mac80211/cfg.c | 9 +++++++- net/wireless/chan.c | 43 +++++------------------------------- net/wireless/core.h | 5 ++--- net/wireless/mesh.c | 26 +++++++++++----------- net/wireless/mlme.c | 2 -- net/wireless/nl80211.c | 21 ++++++++++-------- net/wireless/wext-compat.c | 10 ++------- net/wireless/wext-sme.c | 10 +++++++-- 13 files changed, 100 insertions(+), 106 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 2fa879b015b6..f4a203049fb4 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -435,24 +435,40 @@ static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len) * Set Channel */ -static int lbs_cfg_set_channel(struct wiphy *wiphy, - struct net_device *netdev, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type) +static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type) { struct lbs_private *priv = wiphy_priv(wiphy); int ret = -ENOTSUPP; - lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d, type %d", - netdev_name(netdev), channel->center_freq, channel_type); + lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d", + channel->center_freq, channel_type); if (channel_type != NL80211_CHAN_NO_HT) goto out; - if (netdev == priv->mesh_dev) - ret = lbs_mesh_set_channel(priv, channel->hw_value); - else - ret = lbs_set_channel(priv, channel->hw_value); + ret = lbs_set_channel(priv, channel->hw_value); + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + +static int lbs_cfg_set_mesh_channel(struct wiphy *wiphy, + struct net_device *netdev, + struct ieee80211_channel *channel) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + int ret = -ENOTSUPP; + + lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d", + netdev_name(netdev), channel->center_freq); + + if (netdev != priv->mesh_dev) + goto out; + + ret = lbs_mesh_set_channel(priv, channel->hw_value); out: lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); @@ -2029,7 +2045,8 @@ static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev) */ static struct cfg80211_ops lbs_cfg80211_ops = { - .set_channel = lbs_cfg_set_channel, + .set_monitor_channel = lbs_cfg_set_monitor_channel, + .libertas_set_mesh_channel = lbs_cfg_set_mesh_channel, .scan = lbs_cfg_scan, .connect = lbs_cfg_connect, .disconnect = lbs_cfg_disconnect, diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index 672005430aca..60996ce89f77 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h @@ -58,6 +58,7 @@ struct lbs_private { uint16_t mesh_tlv; u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1]; u8 mesh_ssid_len; + u8 mesh_channel; #endif /* Debugfs */ diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c index e87c031b298f..97807751ebcf 100644 --- a/drivers/net/wireless/libertas/mesh.c +++ b/drivers/net/wireless/libertas/mesh.c @@ -131,16 +131,13 @@ static int lbs_mesh_config(struct lbs_private *priv, uint16_t action, int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel) { + priv->mesh_channel = channel; return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel); } static uint16_t lbs_mesh_get_channel(struct lbs_private *priv) { - struct wireless_dev *mesh_wdev = priv->mesh_dev->ieee80211_ptr; - if (mesh_wdev->channel) - return mesh_wdev->channel->hw_value; - else - return 1; + return priv->mesh_channel ?: 1; } /*************************************************************************** diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c index f7b15b8934fa..e15675585fb1 100644 --- a/drivers/net/wireless/orinoco/cfg.c +++ b/drivers/net/wireless/orinoco/cfg.c @@ -160,10 +160,9 @@ static int orinoco_scan(struct wiphy *wiphy, struct net_device *dev, return err; } -static int orinoco_set_channel(struct wiphy *wiphy, - struct net_device *netdev, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) +static int orinoco_set_monitor_channel(struct wiphy *wiphy, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) { struct orinoco_private *priv = wiphy_priv(wiphy); int err = 0; @@ -286,7 +285,7 @@ static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed) const struct cfg80211_ops orinoco_cfg_ops = { .change_virtual_intf = orinoco_change_vif, - .set_channel = orinoco_set_channel, + .set_monitor_channel = orinoco_set_monitor_channel, .scan = orinoco_scan, .set_wiphy_params = orinoco_set_wiphy_params, }; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 4c90c44b8b75..7319f25250b6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1420,11 +1420,14 @@ struct cfg80211_gtk_rekey_data { * * @set_txq_params: Set TX queue parameters * - * @set_channel: Set channel for a given wireless interface. Some devices - * may support multi-channel operation (by channel hopping) so cfg80211 - * doesn't verify much. Note, however, that the passed netdev may be - * %NULL as well if the user requested changing the channel for the - * device itself, or for a monitor interface. + * @libertas_set_mesh_channel: Only for backward compatibility for libertas, + * as it doesn't implement join_mesh and needs to set the channel to + * join the mesh instead. + * + * @set_monitor_channel: Set the monitor mode channel for the device. If other + * interfaces are active this callback should reject the configuration. + * If no interfaces are active or the device is down, the channel should + * be stored for when a monitor interface becomes active. * @get_channel: Get the current operating channel, should return %NULL if * there's no single defined operating channel if for example the * device implements channel hopping for multi-channel virtual interfaces. @@ -1614,9 +1617,13 @@ struct cfg80211_ops { int (*set_txq_params)(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_txq_params *params); - int (*set_channel)(struct wiphy *wiphy, struct net_device *dev, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type); + int (*libertas_set_mesh_channel)(struct wiphy *wiphy, + struct net_device *dev, + struct ieee80211_channel *chan); + + int (*set_monitor_channel)(struct wiphy *wiphy, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type); int (*scan)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_scan_request *request); @@ -2325,7 +2332,6 @@ struct wireless_dev { spinlock_t event_lock; struct cfg80211_internal_bss *current_bss; /* associated / joined */ - struct ieee80211_channel *channel; struct ieee80211_channel *preset_chan; enum nl80211_channel_type preset_chantype; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 50aea1ac7e03..d99359a6f76d 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -709,6 +709,13 @@ static int ieee80211_set_channel(struct wiphy *wiphy, return 0; } +static int ieee80211_set_monitor_channel(struct wiphy *wiphy, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + return ieee80211_set_channel(wiphy, NULL, chan, channel_type); +} + static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, const u8 *resp, size_t resp_len) { @@ -2932,7 +2939,7 @@ struct cfg80211_ops mac80211_config_ops = { #endif .change_bss = ieee80211_change_bss, .set_txq_params = ieee80211_set_txq_params, - .set_channel = ieee80211_set_channel, + .set_monitor_channel = ieee80211_set_monitor_channel, .suspend = ieee80211_suspend, .resume = ieee80211_resume, .scan = ieee80211_scan, diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 20b87d895722..c1999e45a07c 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -78,50 +78,17 @@ bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, } EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan); -int cfg80211_set_freq(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, int freq, - enum nl80211_channel_type channel_type) +int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, + int freq, enum nl80211_channel_type chantype) { struct ieee80211_channel *chan; - int result; - if (wdev && wdev->iftype == NL80211_IFTYPE_MONITOR) - wdev = NULL; - - if (wdev) { - ASSERT_WDEV_LOCK(wdev); - - if (!netif_running(wdev->netdev)) - return -ENETDOWN; - } - - if (!rdev->ops->set_channel) + if (!rdev->ops->set_monitor_channel) return -EOPNOTSUPP; - chan = rdev_freq_to_chan(rdev, freq, channel_type); + chan = rdev_freq_to_chan(rdev, freq, chantype); if (!chan) return -EINVAL; - /* Both channels should be able to initiate communication */ - if (wdev && (wdev->iftype == NL80211_IFTYPE_ADHOC || - wdev->iftype == NL80211_IFTYPE_AP || - wdev->iftype == NL80211_IFTYPE_AP_VLAN || - wdev->iftype == NL80211_IFTYPE_MESH_POINT || - wdev->iftype == NL80211_IFTYPE_P2P_GO) && - !cfg80211_can_beacon_sec_chan(&rdev->wiphy, chan, channel_type)) { - printk(KERN_DEBUG - "cfg80211: Secondary channel not allowed to beacon\n"); - return -EINVAL; - } - - result = rdev->ops->set_channel(&rdev->wiphy, - wdev ? wdev->netdev : NULL, - chan, channel_type); - if (result) - return result; - - if (wdev) - wdev->channel = chan; - - return 0; + return rdev->ops->set_monitor_channel(&rdev->wiphy, chan, chantype); } diff --git a/net/wireless/core.h b/net/wireless/core.h index 1d3d24126946..9348a47562a4 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -444,9 +444,8 @@ cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, struct ieee80211_channel * rdev_freq_to_chan(struct cfg80211_registered_device *rdev, int freq, enum nl80211_channel_type channel_type); -int cfg80211_set_freq(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, int freq, - enum nl80211_channel_type channel_type); +int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, + int freq, enum nl80211_channel_type chantype); int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, const u8 *rates, unsigned int n_rates, diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 2e3b700eba32..b44c736bf9cf 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -179,6 +179,13 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, { struct ieee80211_channel *channel; + channel = rdev_freq_to_chan(rdev, freq, channel_type); + if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, + channel, + channel_type)) { + return -EINVAL; + } + /* * Workaround for libertas (only!), it puts the interface * into mesh mode but doesn't implement join_mesh. Instead, @@ -186,27 +193,20 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, * you set the channel. Note that the libertas mesh isn't * compatible with 802.11 mesh. */ - if (!rdev->ops->join_mesh) { - int err; + if (rdev->ops->libertas_set_mesh_channel) { + if (channel_type != NL80211_CHAN_NO_HT) + return -EINVAL; if (!netif_running(wdev->netdev)) return -ENETDOWN; - wdev_lock(wdev); - err = cfg80211_set_freq(rdev, wdev, freq, channel_type); - wdev_unlock(wdev); - - return err; + return rdev->ops->libertas_set_mesh_channel(&rdev->wiphy, + wdev->netdev, + channel); } if (wdev->mesh_id_len) return -EBUSY; - channel = rdev_freq_to_chan(rdev, freq, channel_type); - if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, - channel, - channel_type)) { - return -EINVAL; - } wdev->preset_chan = channel; wdev->preset_chantype = channel_type; return 0; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index eb90988bbd36..da4406f11929 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -947,8 +947,6 @@ void cfg80211_ch_switch_notify(struct net_device *dev, int freq, if (WARN_ON(!chan)) goto out; - wdev->channel = chan; - nl80211_ch_switch_notify(rdev, dev, freq, type, GFP_KERNEL); out: wdev_unlock(wdev); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b22f1f876881..5e29bd38e7df 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -921,7 +921,7 @@ 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; } - if (dev->ops->set_channel || dev->ops->start_ap || + if (dev->ops->set_monitor_channel || dev->ops->start_ap || dev->ops->join_mesh) { i++; if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL)) @@ -1178,8 +1178,8 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) * the channel in the start-ap or join-mesh commands instead. * * 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. + * whatever else is going on, so they have their own special + * operation to set the monitor channel if possible. */ return !wdev || wdev->iftype == NL80211_IFTYPE_AP || @@ -1217,6 +1217,10 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; u32 freq; int result; + enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR; + + if (wdev) + iftype = wdev->iftype; if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) return -EINVAL; @@ -1231,7 +1235,7 @@ 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) switch (wdev->iftype) { + switch (iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: if (wdev->beacon_interval) { @@ -1252,12 +1256,11 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, case NL80211_IFTYPE_MESH_POINT: result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type); break; + case NL80211_IFTYPE_MONITOR: + result = cfg80211_set_monitor_channel(rdev, freq, channel_type); + break; default: - wdev_lock(wdev); - result = cfg80211_set_freq(rdev, wdev, freq, channel_type); - wdev_unlock(wdev); - } else { - result = cfg80211_set_freq(rdev, NULL, freq, channel_type); + result = -EINVAL; } mutex_unlock(&rdev->devlist_mtx); diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index faeb03548aa4..bc879833b21f 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -802,9 +802,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, if (freq == 0) return -EINVAL; mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); - wdev_unlock(wdev); + err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT); mutex_unlock(&rdev->devlist_mtx); return err; case NL80211_IFTYPE_MESH_POINT: @@ -848,11 +846,7 @@ static int cfg80211_wext_giwfreq(struct net_device *dev, freq->e = 6; return 0; default: - if (!wdev->channel) - return -EINVAL; - freq->m = wdev->channel->center_freq; - freq->e = 6; - return 0; + return -EINVAL; } } diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 7decbd357d51..1f773f668d1a 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -111,9 +111,15 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, wdev->wext.connect.channel = chan; - /* SSID is not set, we just want to switch channel */ + /* + * SSID is not set, we just want to switch monitor channel, + * this is really just backward compatibility, if the SSID + * is set then we use the channel to select the BSS to use + * to connect to instead. If we were connected on another + * channel we disconnected above and reconnect below. + */ if (chan && !wdev->wext.connect.ssid_len) { - err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); + err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT); goto out; } -- cgit v1.2.3 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') 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 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') 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 From 196ac1c13d4db6c276dbb1c9190c8d7d45a83f1f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 5 Jun 2012 14:28:40 +0200 Subject: mac80211: do remain-on-channel while idle The IDLE handling in HW off-channel is broken right now since we turn off IDLE only when the off-channel period already started. Therefore, all drivers that use it today (only iwlwifi!) must support off-channel while idle, so playing with idle isn't needed at all. Off-channel in general, since it's no longer used for authentication/association, shouldn't affect PS, so also remove that logic. Also document a small caveat for reporting TX status from off-channel frames in HW remain-on-channel. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 8 +++++++- net/mac80211/cfg.c | 7 +++---- net/mac80211/iface.c | 17 +++++++---------- net/mac80211/mlme.c | 6 ------ net/mac80211/offchannel.c | 4 ---- 5 files changed, 17 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d2ed86da8e4c..6e700bf8d4a5 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2183,7 +2183,13 @@ enum ieee80211_rate_control_changed { * offload. Frames to transmit on the off-channel channel are transmitted * normally except for the %IEEE80211_TX_CTL_TX_OFFCHAN flag. When the * duration (which will always be non-zero) expires, the driver must call - * ieee80211_remain_on_channel_expired(). This callback may sleep. + * ieee80211_remain_on_channel_expired(). + * The driver must not call ieee80211_remain_on_channel_expired() before + * the TX status for a frame that was sent off-channel, otherwise the TX + * status is reported to userspace in an invalid way. + * Note that this callback may be called while the device is in IDLE and + * must be accepted in this case. + * This callback may sleep. * @cancel_remain_on_channel: Requests that an ongoing off-channel period is * aborted before it expires. This callback may sleep. * diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d99359a6f76d..a16907919709 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2187,8 +2187,6 @@ static int ieee80211_cancel_remain_on_channel_hw(struct ieee80211_local *local, local->hw_roc_cookie = 0; local->hw_roc_channel = NULL; - ieee80211_recalc_idle(local); - return 0; } @@ -2248,7 +2246,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_work *wk; const struct ieee80211_mgmt *mgmt = (void *)buf; u32 flags; - bool is_offchan = false; + bool is_offchan = false, in_hw_roc = false; if (dont_wait_for_ack) flags = IEEE80211_TX_CTL_NO_ACK; @@ -2268,6 +2266,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, if (chan == local->hw_roc_channel) { /* TODO: check channel type? */ is_offchan = false; + in_hw_roc = true; flags |= IEEE80211_TX_CTL_TX_OFFCHAN; } @@ -2370,7 +2369,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, * wait is involved, we might otherwise not be on * the right channel for long enough! */ - if (!is_offchan && !wait && !sdata->vif.bss_conf.idle) { + if (!is_offchan && !wait && (in_hw_roc || !sdata->vif.bss_conf.idle)) { ieee80211_tx_skb(sdata, skb); return 0; } diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index ede5f4959904..968d71c50713 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1456,7 +1456,7 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; int count = 0; - bool working = false, scanning = false, hw_roc = false; + bool working = false, scanning = false; struct ieee80211_work *wk; unsigned int led_trig_start = 0, led_trig_stop = 0; @@ -1493,9 +1493,11 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) count++; } - list_for_each_entry(wk, &local->work_list, list) { - working = true; - wk->sdata->vif.bss_conf.idle = false; + if (!local->ops->remain_on_channel) { + list_for_each_entry(wk, &local->work_list, list) { + working = true; + wk->sdata->vif.bss_conf.idle = false; + } } if (local->scan_sdata && @@ -1504,9 +1506,6 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) local->scan_sdata->vif.bss_conf.idle = false; } - if (local->hw_roc_channel) - hw_roc = true; - list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type == NL80211_IFTYPE_MONITOR || sdata->vif.type == NL80211_IFTYPE_AP_VLAN) @@ -1518,7 +1517,7 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); } - if (working || scanning || hw_roc) + if (working || scanning) led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_WORK; else led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_WORK; @@ -1530,8 +1529,6 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) ieee80211_mod_tpt_led_trig(local, led_trig_start, led_trig_stop); - if (hw_roc) - return ieee80211_idle_off(local, "hw remain-on-channel"); if (working) return ieee80211_idle_off(local, "working"); if (scanning) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e7c4ec4ce166..0f45d02e0ba7 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -930,11 +930,6 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) return; } - if (!list_empty(&local->work_list)) { - local->ps_sdata = NULL; - goto change; - } - list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; @@ -1007,7 +1002,6 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) local->ps_sdata = NULL; } - change: ieee80211_change_ps(local); } diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 935aa4b6deee..8f482b15bc51 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -207,8 +207,6 @@ static void ieee80211_hw_roc_start(struct work_struct *work) GFP_KERNEL); } - ieee80211_recalc_idle(local); - mutex_unlock(&local->mtx); } @@ -260,8 +258,6 @@ static void ieee80211_hw_roc_done(struct work_struct *work) local->hw_roc_channel = NULL; local->hw_roc_cookie = 0; - ieee80211_recalc_idle(local); - mutex_unlock(&local->mtx); } -- cgit v1.2.3 From 2eb278e083549f4eb29838037004054b3b55df62 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 5 Jun 2012 14:28:42 +0200 Subject: mac80211: unify SW/offload remain-on-channel Redesign all the off-channel code, getting rid of the generic off-channel work concept, replacing it with a simple remain-on-channel list. This fixes a number of small issues with the ROC implementation: * offloaded remain-on-channel couldn't be queued, now we can queue it as well, if needed * in iwlwifi (the only user) offloaded ROC is mutually exclusive with scanning, use the new queue to handle that case -- I expect that it will later depend on a HW flag The bigger issue though is that there's a bad bug in the current implementation: if we get a mgmt TX request while HW roc is active, and this new request has a wait time, we actually schedule a software ROC instead since we can't guarantee the existing offloaded ROC will still be that long. To fix this, the queuing mechanism was needed. The queuing mechanism for offloaded ROC isn't yet optimal, ideally we should add API to have the HW extend the ROC if needed. We could add that later but for now use a software implementation. Overall, this unifies the behaviour between the offloaded and software-implemented case as much as possible. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 3 - net/mac80211/Makefile | 1 - net/mac80211/cfg.c | 478 ++++++++++++++++++++++++--------------------- net/mac80211/ieee80211_i.h | 89 +++------ net/mac80211/iface.c | 23 +-- net/mac80211/main.c | 10 +- net/mac80211/offchannel.c | 280 ++++++++++++++++++++++---- net/mac80211/scan.c | 4 +- net/mac80211/status.c | 28 +-- net/mac80211/work.c | 370 ----------------------------------- 10 files changed, 532 insertions(+), 754 deletions(-) delete mode 100644 net/mac80211/work.c (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 6e700bf8d4a5..d152f54064fd 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2184,9 +2184,6 @@ enum ieee80211_rate_control_changed { * normally except for the %IEEE80211_TX_CTL_TX_OFFCHAN flag. When the * duration (which will always be non-zero) expires, the driver must call * ieee80211_remain_on_channel_expired(). - * The driver must not call ieee80211_remain_on_channel_expired() before - * the TX status for a frame that was sent off-channel, otherwise the TX - * status is reported to userspace in an invalid way. * Note that this callback may be called while the device is in IDLE and * must be accepted in this case. * This callback may sleep. diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 3e9d931bba35..2b1470bac178 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -9,7 +9,6 @@ mac80211-y := \ scan.o offchannel.o \ ht.o agg-tx.o agg-rx.o \ ibss.o \ - work.o \ iface.o \ rate.o \ michael.o \ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index a16907919709..498c94e34427 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2112,35 +2112,171 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, return 0; } -static int ieee80211_remain_on_channel_hw(struct ieee80211_local *local, - struct net_device *dev, - struct ieee80211_channel *chan, - enum nl80211_channel_type chantype, - unsigned int duration, u64 *cookie) -{ +static int ieee80211_start_roc_work(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type, + unsigned int duration, u64 *cookie, + struct sk_buff *txskb) +{ + struct ieee80211_roc_work *roc, *tmp; + bool queued = false; int ret; - u32 random_cookie; lockdep_assert_held(&local->mtx); - if (local->hw_roc_cookie) - return -EBUSY; - /* must be nonzero */ - random_cookie = random32() | 1; - - *cookie = random_cookie; - local->hw_roc_dev = dev; - local->hw_roc_cookie = random_cookie; - local->hw_roc_channel = chan; - local->hw_roc_channel_type = chantype; - local->hw_roc_duration = duration; - ret = drv_remain_on_channel(local, chan, chantype, duration); + roc = kzalloc(sizeof(*roc), GFP_KERNEL); + if (!roc) + return -ENOMEM; + + roc->chan = channel; + roc->chan_type = channel_type; + roc->duration = duration; + roc->req_duration = duration; + roc->frame = txskb; + roc->mgmt_tx_cookie = (unsigned long)txskb; + roc->sdata = sdata; + INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work); + INIT_LIST_HEAD(&roc->dependents); + + /* if there's one pending or we're scanning, queue this one */ + if (!list_empty(&local->roc_list) || local->scanning) + goto out_check_combine; + + /* if not HW assist, just queue & schedule work */ + if (!local->ops->remain_on_channel) { + ieee80211_queue_delayed_work(&local->hw, &roc->work, 0); + goto out_queue; + } + + /* otherwise actually kick it off here (for error handling) */ + + /* + * If the duration is zero, then the driver + * wouldn't actually do anything. Set it to + * 10 for now. + * + * TODO: cancel the off-channel operation + * when we get the SKB's TX status and + * the wait time was zero before. + */ + if (!duration) + duration = 10; + + ret = drv_remain_on_channel(local, channel, channel_type, duration); if (ret) { - local->hw_roc_channel = NULL; - local->hw_roc_cookie = 0; + kfree(roc); + return ret; } - return ret; + roc->started = true; + goto out_queue; + + out_check_combine: + list_for_each_entry(tmp, &local->roc_list, list) { + if (tmp->chan != channel || tmp->chan_type != channel_type) + continue; + + /* + * Extend this ROC if possible: + * + * If it hasn't started yet, just increase the duration + * and add the new one to the list of dependents. + */ + if (!tmp->started) { + list_add_tail(&roc->list, &tmp->dependents); + tmp->duration = max(tmp->duration, roc->duration); + queued = true; + break; + } + + /* If it has already started, it's more difficult ... */ + if (local->ops->remain_on_channel) { + unsigned long j = jiffies; + + /* + * In the offloaded ROC case, if it hasn't begun, add + * this new one to the dependent list to be handled + * when the the master one begins. If it has begun, + * check that there's still a minimum time left and + * if so, start this one, transmitting the frame, but + * add it to the list directly after this one with a + * a reduced time so we'll ask the driver to execute + * it right after finishing the previous one, in the + * hope that it'll also be executed right afterwards, + * effectively extending the old one. + * If there's no minimum time left, just add it to the + * normal list. + */ + if (!tmp->hw_begun) { + list_add_tail(&roc->list, &tmp->dependents); + queued = true; + break; + } + + if (time_before(j + IEEE80211_ROC_MIN_LEFT, + tmp->hw_start_time + + msecs_to_jiffies(tmp->duration))) { + int new_dur; + + ieee80211_handle_roc_started(roc); + + new_dur = roc->duration - + jiffies_to_msecs(tmp->hw_start_time + + msecs_to_jiffies( + tmp->duration) - + j); + + if (new_dur > 0) { + /* add right after tmp */ + list_add(&roc->list, &tmp->list); + } else { + list_add_tail(&roc->list, + &tmp->dependents); + } + queued = true; + } + } else if (del_timer_sync(&tmp->work.timer)) { + unsigned long new_end; + + /* + * In the software ROC case, cancel the timer, if + * that fails then the finish work is already + * queued/pending and thus we queue the new ROC + * normally, if that succeeds then we can extend + * the timer duration and TX the frame (if any.) + */ + + list_add_tail(&roc->list, &tmp->dependents); + queued = true; + + new_end = jiffies + msecs_to_jiffies(roc->duration); + + /* ok, it was started & we canceled timer */ + if (time_after(new_end, tmp->work.timer.expires)) + mod_timer(&tmp->work.timer, new_end); + else + add_timer(&tmp->work.timer); + + ieee80211_handle_roc_started(roc); + } + break; + } + + out_queue: + if (!queued) + list_add_tail(&roc->list, &local->roc_list); + + /* + * cookie is either the roc (for normal roc) + * or the SKB (for mgmt TX) + */ + if (txskb) + *cookie = (unsigned long)txskb; + else + *cookie = (unsigned long)roc; + + return 0; } static int ieee80211_remain_on_channel(struct wiphy *wiphy, @@ -2152,84 +2288,76 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; + int ret; - if (local->ops->remain_on_channel) { - int ret; - - mutex_lock(&local->mtx); - ret = ieee80211_remain_on_channel_hw(local, dev, - chan, channel_type, - duration, cookie); - local->hw_roc_for_tx = false; - mutex_unlock(&local->mtx); - - return ret; - } + mutex_lock(&local->mtx); + ret = ieee80211_start_roc_work(local, sdata, chan, channel_type, + duration, cookie, NULL); + mutex_unlock(&local->mtx); - return ieee80211_wk_remain_on_channel(sdata, chan, channel_type, - duration, cookie); + return ret; } -static int ieee80211_cancel_remain_on_channel_hw(struct ieee80211_local *local, - u64 cookie) +static int ieee80211_cancel_roc(struct ieee80211_local *local, + u64 cookie, bool mgmt_tx) { + struct ieee80211_roc_work *roc, *tmp, *found = NULL; int ret; - lockdep_assert_held(&local->mtx); + mutex_lock(&local->mtx); + list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { + if (!mgmt_tx && (unsigned long)roc != cookie) + continue; + else if (mgmt_tx && roc->mgmt_tx_cookie != cookie) + continue; - if (local->hw_roc_cookie != cookie) - return -ENOENT; + found = roc; + break; + } - ret = drv_cancel_remain_on_channel(local); - if (ret) - return ret; + if (!found) { + mutex_unlock(&local->mtx); + return -ENOENT; + } - local->hw_roc_cookie = 0; - local->hw_roc_channel = NULL; + if (local->ops->remain_on_channel) { + if (found->started) { + ret = drv_cancel_remain_on_channel(local); + if (WARN_ON_ONCE(ret)) { + mutex_unlock(&local->mtx); + return ret; + } + } - return 0; -} + list_del(&found->list); -static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, - struct net_device *dev, - u64 cookie) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_local *local = sdata->local; + ieee80211_run_deferred_scan(local); + ieee80211_start_next_roc(local); + mutex_unlock(&local->mtx); - if (local->ops->cancel_remain_on_channel) { - int ret; + ieee80211_roc_notify_destroy(found); + } else { + /* work may be pending so use it all the time */ + found->abort = true; + ieee80211_queue_delayed_work(&local->hw, &found->work, 0); - mutex_lock(&local->mtx); - ret = ieee80211_cancel_remain_on_channel_hw(local, cookie); mutex_unlock(&local->mtx); - return ret; + /* work will clean up etc */ + flush_delayed_work(&found->work); } - return ieee80211_wk_cancel_remain_on_channel(sdata, cookie); + return 0; } -static enum work_done_result -ieee80211_offchan_tx_done(struct ieee80211_work *wk, struct sk_buff *skb) +static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, + struct net_device *dev, + u64 cookie) { - /* - * Use the data embedded in the work struct for reporting - * here so if the driver mangled the SKB before dropping - * it (which is the only way we really should get here) - * then we don't report mangled data. - * - * If there was no wait time, then by the time we get here - * the driver will likely not have reported the status yet, - * so in that case userspace will have to deal with it. - */ - - if (wk->offchan_tx.wait && !wk->offchan_tx.status) - cfg80211_mgmt_tx_status(wk->sdata->dev, - (unsigned long) wk->offchan_tx.frame, - wk->data, wk->data_len, false, GFP_KERNEL); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; - return WORK_DONE_DESTROY; + return ieee80211_cancel_roc(local, cookie, false); } static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, @@ -2243,10 +2371,10 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct sta_info *sta; - struct ieee80211_work *wk; const struct ieee80211_mgmt *mgmt = (void *)buf; + bool need_offchan = false; u32 flags; - bool is_offchan = false, in_hw_roc = false; + int ret; if (dont_wait_for_ack) flags = IEEE80211_TX_CTL_NO_ACK; @@ -2254,34 +2382,28 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | IEEE80211_TX_CTL_REQ_TX_STATUS; - /* Check that we are on the requested channel for transmission */ - if (chan != local->tmp_channel && - chan != local->oper_channel) - is_offchan = true; - if (channel_type_valid && - (channel_type != local->tmp_channel_type && - channel_type != local->_oper_channel_type)) - is_offchan = true; - - if (chan == local->hw_roc_channel) { - /* TODO: check channel type? */ - is_offchan = false; - in_hw_roc = true; - flags |= IEEE80211_TX_CTL_TX_OFFCHAN; - } - if (no_cck) flags |= IEEE80211_TX_CTL_NO_CCK_RATE; - if (is_offchan && !offchan) - return -EBUSY; - switch (sdata->vif.type) { case NL80211_IFTYPE_ADHOC: + if (!sdata->vif.bss_conf.ibss_joined) + need_offchan = true; + /* fall through */ +#ifdef CONFIG_MAC80211_MESH + case NL80211_IFTYPE_MESH_POINT: + if (ieee80211_vif_is_mesh(&sdata->vif) && + !sdata->u.mesh.mesh_id_len) + need_offchan = true; + /* fall through */ +#endif case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: - case NL80211_IFTYPE_MESH_POINT: + if (sdata->vif.type != NL80211_IFTYPE_ADHOC && + !ieee80211_vif_is_mesh(&sdata->vif) && + !rcu_access_pointer(sdata->bss->beacon)) + need_offchan = true; if (!ieee80211_is_action(mgmt->frame_control) || mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) break; @@ -2293,105 +2415,60 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: + if (!sdata->u.mgd.associated) + need_offchan = true; break; default: return -EOPNOTSUPP; } + mutex_lock(&local->mtx); + + /* Check if the operating channel is the requested channel */ + if (!need_offchan) { + need_offchan = chan != local->oper_channel; + if (channel_type_valid && + channel_type != local->_oper_channel_type) + need_offchan = true; + } + + if (need_offchan && !offchan) { + ret = -EBUSY; + goto out_unlock; + } + skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); - if (!skb) - return -ENOMEM; + if (!skb) { + ret = -ENOMEM; + goto out_unlock; + } skb_reserve(skb, local->hw.extra_tx_headroom); memcpy(skb_put(skb, len), buf, len); IEEE80211_SKB_CB(skb)->flags = flags; - if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL && - flags & IEEE80211_TX_CTL_TX_OFFCHAN) - IEEE80211_SKB_CB(skb)->hw_queue = - local->hw.offchannel_tx_hw_queue; - skb->dev = sdata->dev; - *cookie = (unsigned long) skb; - - if (is_offchan && local->ops->remain_on_channel) { - unsigned int duration; - int ret; - - mutex_lock(&local->mtx); - /* - * If the duration is zero, then the driver - * wouldn't actually do anything. Set it to - * 100 for now. - * - * TODO: cancel the off-channel operation - * when we get the SKB's TX status and - * the wait time was zero before. - */ - duration = 100; - if (wait) - duration = wait; - ret = ieee80211_remain_on_channel_hw(local, dev, chan, - channel_type, - duration, cookie); - if (ret) { - kfree_skb(skb); - mutex_unlock(&local->mtx); - return ret; - } - - local->hw_roc_for_tx = true; - local->hw_roc_duration = wait; - - /* - * queue up frame for transmission after - * ieee80211_ready_on_channel call - */ - - /* modify cookie to prevent API mismatches */ - *cookie ^= 2; - IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN; - if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) - IEEE80211_SKB_CB(skb)->hw_queue = - local->hw.offchannel_tx_hw_queue; - local->hw_roc_skb = skb; - local->hw_roc_skb_for_status = skb; - mutex_unlock(&local->mtx); - - return 0; - } - - /* - * Can transmit right away if the channel was the - * right one and there's no wait involved... If a - * wait is involved, we might otherwise not be on - * the right channel for long enough! - */ - if (!is_offchan && !wait && (in_hw_roc || !sdata->vif.bss_conf.idle)) { + if (!need_offchan) { ieee80211_tx_skb(sdata, skb); - return 0; - } - - wk = kzalloc(sizeof(*wk) + len, GFP_KERNEL); - if (!wk) { - kfree_skb(skb); - return -ENOMEM; + ret = 0; + goto out_unlock; } - wk->type = IEEE80211_WORK_OFFCHANNEL_TX; - wk->chan = chan; - wk->chan_type = channel_type; - wk->sdata = sdata; - wk->done = ieee80211_offchan_tx_done; - wk->offchan_tx.frame = skb; - wk->offchan_tx.wait = wait; - wk->data_len = len; - memcpy(wk->data, buf, len); + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN; + if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) + IEEE80211_SKB_CB(skb)->hw_queue = + local->hw.offchannel_tx_hw_queue; - ieee80211_add_work(wk); - return 0; + /* This will handle all kinds of coalescing and immediate TX */ + ret = ieee80211_start_roc_work(local, sdata, chan, channel_type, + wait, cookie, skb); + if (ret) + kfree_skb(skb); + out_unlock: + mutex_unlock(&local->mtx); + return ret; } static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, @@ -2400,45 +2477,8 @@ static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; - struct ieee80211_work *wk; - int ret = -ENOENT; - mutex_lock(&local->mtx); - - if (local->ops->cancel_remain_on_channel) { - cookie ^= 2; - ret = ieee80211_cancel_remain_on_channel_hw(local, cookie); - - if (ret == 0) { - kfree_skb(local->hw_roc_skb); - local->hw_roc_skb = NULL; - local->hw_roc_skb_for_status = NULL; - } - - mutex_unlock(&local->mtx); - - return ret; - } - - list_for_each_entry(wk, &local->work_list, list) { - if (wk->sdata != sdata) - continue; - - if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX) - continue; - - if (cookie != (unsigned long) wk->offchan_tx.frame) - continue; - - wk->timeout = jiffies; - - ieee80211_queue_work(&local->hw, &local->work_work); - ret = 0; - break; - } - mutex_unlock(&local->mtx); - - return ret; + return ieee80211_cancel_roc(local, cookie, true); } static void ieee80211_mgmt_frame_register(struct wiphy *wiphy, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8c026abcb8d9..e6cbf5b68c89 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -317,55 +317,30 @@ struct mesh_preq_queue { u8 flags; }; -enum ieee80211_work_type { - IEEE80211_WORK_ABORT, - IEEE80211_WORK_REMAIN_ON_CHANNEL, - IEEE80211_WORK_OFFCHANNEL_TX, -}; - -/** - * enum work_done_result - indicates what to do after work was done - * - * @WORK_DONE_DESTROY: This work item is no longer needed, destroy. - * @WORK_DONE_REQUEUE: This work item was reset to be reused, and - * should be requeued. - */ -enum work_done_result { - WORK_DONE_DESTROY, - WORK_DONE_REQUEUE, -}; +#if HZ/100 == 0 +#define IEEE80211_ROC_MIN_LEFT 1 +#else +#define IEEE80211_ROC_MIN_LEFT (HZ/100) +#endif -struct ieee80211_work { +struct ieee80211_roc_work { struct list_head list; + struct list_head dependents; - struct rcu_head rcu_head; + struct delayed_work work; struct ieee80211_sub_if_data *sdata; - enum work_done_result (*done)(struct ieee80211_work *wk, - struct sk_buff *skb); - struct ieee80211_channel *chan; enum nl80211_channel_type chan_type; - unsigned long timeout; - enum ieee80211_work_type type; + bool started, abort, hw_begun, notified; - bool started; + unsigned long hw_start_time; - union { - struct { - u32 duration; - } remain; - struct { - struct sk_buff *frame; - u32 wait; - bool status; - } offchan_tx; - }; - - size_t data_len; - u8 data[]; + u32 duration, req_duration; + struct sk_buff *frame; + u64 mgmt_tx_cookie; }; /* flags used in struct ieee80211_if_managed.flags */ @@ -847,13 +822,6 @@ struct ieee80211_local { const struct ieee80211_ops *ops; - /* - * work stuff, potentially off-channel (in the future) - */ - struct list_head work_list; - struct timer_list work_timer; - struct work_struct work_work; - /* * private workqueue to mac80211. mac80211 makes this accessible * via ieee80211_queue_work() @@ -1088,14 +1056,12 @@ struct ieee80211_local { } debugfs; #endif - struct ieee80211_channel *hw_roc_channel; - struct net_device *hw_roc_dev; - struct sk_buff *hw_roc_skb, *hw_roc_skb_for_status; + /* + * Remain-on-channel support + */ + struct list_head roc_list; struct work_struct hw_roc_start, hw_roc_done; - enum nl80211_channel_type hw_roc_channel_type; - unsigned int hw_roc_duration; - u32 hw_roc_cookie; - bool hw_roc_for_tx; + unsigned long hw_roc_start_time; struct idr ack_status_frames; spinlock_t ack_status_lock; @@ -1291,7 +1257,12 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local, bool offchannel_ps_enable); void ieee80211_offchannel_return(struct ieee80211_local *local, bool offchannel_ps_disable); -void ieee80211_hw_roc_setup(struct ieee80211_local *local); +void ieee80211_roc_setup(struct ieee80211_local *local); +void ieee80211_start_next_roc(struct ieee80211_local *local); +void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata); +void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc); +void ieee80211_sw_roc_work(struct work_struct *work); +void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); /* interface handling */ int ieee80211_iface_init(void); @@ -1501,18 +1472,6 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, enum nl80211_channel_type channel_type, u16 prot_mode); -/* internal work items */ -void ieee80211_work_init(struct ieee80211_local *local); -void ieee80211_add_work(struct ieee80211_work *wk); -void free_work(struct ieee80211_work *wk); -void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata); -int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, - unsigned int duration, u64 *cookie); -int ieee80211_wk_cancel_remain_on_channel( - struct ieee80211_sub_if_data *sdata, u64 cookie); - /* channel management */ enum ieee80211_chan_mode { CHAN_MODE_UNDEFINED, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 968d71c50713..87aeb4f21ffd 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -528,10 +528,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, */ netif_tx_stop_all_queues(sdata->dev); - /* - * Purge work for this interface. - */ - ieee80211_work_purge(sdata); + ieee80211_roc_purge(sdata); /* * Remove all stations associated with this interface. @@ -637,18 +634,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, ieee80211_configure_filter(local); break; default: - mutex_lock(&local->mtx); - if (local->hw_roc_dev == sdata->dev && - local->hw_roc_channel) { - /* ignore return value since this is racy */ - drv_cancel_remain_on_channel(local); - ieee80211_queue_work(&local->hw, &local->hw_roc_done); - } - mutex_unlock(&local->mtx); - - flush_work(&local->hw_roc_start); - flush_work(&local->hw_roc_done); - flush_work(&sdata->work); /* * When we get here, the interface is marked down. @@ -1457,8 +1442,8 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) struct ieee80211_sub_if_data *sdata; int count = 0; bool working = false, scanning = false; - struct ieee80211_work *wk; unsigned int led_trig_start = 0, led_trig_stop = 0; + struct ieee80211_roc_work *roc; #ifdef CONFIG_PROVE_LOCKING WARN_ON(debug_locks && !lockdep_rtnl_is_held() && @@ -1494,9 +1479,9 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local) } if (!local->ops->remain_on_channel) { - list_for_each_entry(wk, &local->work_list, list) { + list_for_each_entry(roc, &local->roc_list, list) { working = true; - wk->sdata->vif.bss_conf.idle = false; + roc->sdata->vif.bss_conf.idle = false; } } diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 779ac613ee57..d81c178c7712 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -625,8 +625,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work); - ieee80211_work_init(local); - INIT_WORK(&local->restart_work, ieee80211_restart_work); INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); @@ -669,7 +667,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, ieee80211_led_names(local); - ieee80211_hw_roc_setup(local); + ieee80211_roc_setup(local); return &local->hw; } @@ -1016,12 +1014,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) rtnl_unlock(); - /* - * Now all work items will be gone, but the - * timer might still be armed, so delete it - */ - del_timer_sync(&local->work_timer); - cancel_work_sync(&local->restart_work); cancel_work_sync(&local->reconfig_filter); diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 8f482b15bc51..abb226dc4753 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -16,6 +16,7 @@ #include #include "ieee80211_i.h" #include "driver-trace.h" +#include "driver-ops.h" /* * Tell our hardware to disable PS. @@ -181,32 +182,58 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, mutex_unlock(&local->iflist_mtx); } +void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc) +{ + if (roc->notified) + return; + + if (roc->mgmt_tx_cookie) { + if (!WARN_ON(!roc->frame)) { + ieee80211_tx_skb(roc->sdata, roc->frame); + roc->frame = NULL; + } + } else { + cfg80211_ready_on_channel(roc->sdata->dev, (unsigned long)roc, + roc->chan, roc->chan_type, + roc->req_duration, GFP_KERNEL); + } + + roc->notified = true; +} + static void ieee80211_hw_roc_start(struct work_struct *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, hw_roc_start); - struct ieee80211_sub_if_data *sdata; + struct ieee80211_roc_work *roc, *dep, *tmp; mutex_lock(&local->mtx); - if (!local->hw_roc_channel) { - mutex_unlock(&local->mtx); - return; - } + if (list_empty(&local->roc_list)) + goto out_unlock; - if (local->hw_roc_skb) { - sdata = IEEE80211_DEV_TO_SUB_IF(local->hw_roc_dev); - ieee80211_tx_skb(sdata, local->hw_roc_skb); - local->hw_roc_skb = NULL; - } else { - cfg80211_ready_on_channel(local->hw_roc_dev, - local->hw_roc_cookie, - local->hw_roc_channel, - local->hw_roc_channel_type, - local->hw_roc_duration, - GFP_KERNEL); - } + roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work, + list); + + if (!roc->started) + goto out_unlock; + + roc->hw_begun = true; + roc->hw_start_time = local->hw_roc_start_time; + ieee80211_handle_roc_started(roc); + list_for_each_entry_safe(dep, tmp, &roc->dependents, list) { + ieee80211_handle_roc_started(dep); + + if (dep->duration > roc->duration) { + u32 dur = dep->duration; + dep->duration = dur - roc->duration; + roc->duration = dur; + list_del(&dep->list); + list_add(&dep->list, &roc->list); + } + } + out_unlock: mutex_unlock(&local->mtx); } @@ -214,50 +241,179 @@ void ieee80211_ready_on_channel(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); + local->hw_roc_start_time = jiffies; + trace_api_ready_on_channel(local); ieee80211_queue_work(hw, &local->hw_roc_start); } EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel); -static void ieee80211_hw_roc_done(struct work_struct *work) +void ieee80211_start_next_roc(struct ieee80211_local *local) { - struct ieee80211_local *local = - container_of(work, struct ieee80211_local, hw_roc_done); + struct ieee80211_roc_work *roc; - mutex_lock(&local->mtx); + lockdep_assert_held(&local->mtx); - if (!local->hw_roc_channel) { - mutex_unlock(&local->mtx); + if (list_empty(&local->roc_list)) { + ieee80211_run_deferred_scan(local); return; } - /* was never transmitted */ - if (local->hw_roc_skb) { - u64 cookie; + roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work, + list); - cookie = local->hw_roc_cookie ^ 2; + if (local->ops->remain_on_channel) { + int ret, duration = roc->duration; - cfg80211_mgmt_tx_status(local->hw_roc_dev, cookie, - local->hw_roc_skb->data, - local->hw_roc_skb->len, false, - GFP_KERNEL); + /* XXX: duplicated, see ieee80211_start_roc_work() */ + if (!duration) + duration = 10; - kfree_skb(local->hw_roc_skb); - local->hw_roc_skb = NULL; - local->hw_roc_skb_for_status = NULL; + ret = drv_remain_on_channel(local, roc->chan, + roc->chan_type, + duration); + + roc->started = true; + + if (ret) { + wiphy_warn(local->hw.wiphy, + "failed to start next HW ROC (%d)\n", ret); + /* + * queue the work struct again to avoid recursion + * when multiple failures occur + */ + ieee80211_remain_on_channel_expired(&local->hw); + } + } else { + /* delay it a bit */ + ieee80211_queue_delayed_work(&local->hw, &roc->work, + round_jiffies_relative(HZ/2)); + } +} + +void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc) +{ + struct ieee80211_roc_work *dep, *tmp; + + /* was never transmitted */ + if (roc->frame) { + cfg80211_mgmt_tx_status(roc->sdata->dev, + (unsigned long)roc->frame, + roc->frame->data, roc->frame->len, + false, GFP_KERNEL); + kfree_skb(roc->frame); } - if (!local->hw_roc_for_tx) - cfg80211_remain_on_channel_expired(local->hw_roc_dev, - local->hw_roc_cookie, - local->hw_roc_channel, - local->hw_roc_channel_type, + if (!roc->mgmt_tx_cookie) + cfg80211_remain_on_channel_expired(roc->sdata->dev, + (unsigned long)roc, + roc->chan, roc->chan_type, GFP_KERNEL); - local->hw_roc_channel = NULL; - local->hw_roc_cookie = 0; + list_for_each_entry_safe(dep, tmp, &roc->dependents, list) + ieee80211_roc_notify_destroy(dep); + + kfree(roc); +} + +void ieee80211_sw_roc_work(struct work_struct *work) +{ + struct ieee80211_roc_work *roc = + container_of(work, struct ieee80211_roc_work, work.work); + struct ieee80211_sub_if_data *sdata = roc->sdata; + struct ieee80211_local *local = sdata->local; + + mutex_lock(&local->mtx); + + if (roc->abort) + goto finish; + + if (WARN_ON(list_empty(&local->roc_list))) + goto out_unlock; + + if (WARN_ON(roc != list_first_entry(&local->roc_list, + struct ieee80211_roc_work, + list))) + goto out_unlock; + + if (!roc->started) { + struct ieee80211_roc_work *dep; + + /* start this ROC */ + /* switch channel etc */ + ieee80211_recalc_idle(local); + + local->tmp_channel = roc->chan; + local->tmp_channel_type = roc->chan_type; + ieee80211_hw_config(local, 0); + + /* tell userspace or send frame */ + ieee80211_handle_roc_started(roc); + list_for_each_entry(dep, &roc->dependents, list) + ieee80211_handle_roc_started(dep); + + /* if it was pure TX, just finish right away */ + if (!roc->duration) + goto finish; + + roc->started = true; + ieee80211_queue_delayed_work(&local->hw, &roc->work, + msecs_to_jiffies(roc->duration)); + } else { + /* finish this ROC */ + finish: + list_del(&roc->list); + ieee80211_roc_notify_destroy(roc); + + if (roc->started) { + drv_flush(local, false); + + local->tmp_channel = NULL; + ieee80211_hw_config(local, 0); + + ieee80211_offchannel_return(local, true); + } + + ieee80211_recalc_idle(local); + + ieee80211_start_next_roc(local); + ieee80211_run_deferred_scan(local); + } + + out_unlock: + mutex_unlock(&local->mtx); +} + +static void ieee80211_hw_roc_done(struct work_struct *work) +{ + struct ieee80211_local *local = + container_of(work, struct ieee80211_local, hw_roc_done); + struct ieee80211_roc_work *roc; + + mutex_lock(&local->mtx); + + if (list_empty(&local->roc_list)) + goto out_unlock; + + roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work, + list); + + if (!roc->started) + goto out_unlock; + + list_del(&roc->list); + + ieee80211_roc_notify_destroy(roc); + + /* if there's another roc, start it now */ + ieee80211_start_next_roc(local); + + /* or scan maybe */ + ieee80211_run_deferred_scan(local); + + out_unlock: mutex_unlock(&local->mtx); } @@ -271,8 +427,48 @@ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw) } EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired); -void ieee80211_hw_roc_setup(struct ieee80211_local *local) +void ieee80211_roc_setup(struct ieee80211_local *local) { INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start); INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done); + INIT_LIST_HEAD(&local->roc_list); +} + +void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_roc_work *roc, *tmp; + LIST_HEAD(tmp_list); + + mutex_lock(&local->mtx); + list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { + if (roc->sdata != sdata) + continue; + + if (roc->started && local->ops->remain_on_channel) { + /* can race, so ignore return value */ + drv_cancel_remain_on_channel(local); + } + + list_move_tail(&roc->list, &tmp_list); + roc->abort = true; + } + + ieee80211_start_next_roc(local); + ieee80211_run_deferred_scan(local); + mutex_unlock(&local->mtx); + + list_for_each_entry_safe(roc, tmp, &tmp_list, list) { + if (local->ops->remain_on_channel) { + list_del(&roc->list); + ieee80211_roc_notify_destroy(roc); + } else { + ieee80211_queue_delayed_work(&local->hw, &roc->work, 0); + + /* work will clean up etc */ + flush_delayed_work(&roc->work); + } + } + + WARN_ON_ONCE(!list_empty(&tmp_list)); } diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 169da0742c81..379f178eab5f 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -323,7 +323,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, ieee80211_mlme_notify_scan_completed(local); ieee80211_ibss_notify_scan_completed(local); ieee80211_mesh_notify_scan_completed(local); - ieee80211_queue_work(&local->hw, &local->work_work); + ieee80211_start_next_roc(local); } void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) @@ -376,7 +376,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) static bool ieee80211_can_scan(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { - if (!list_empty(&local->work_list)) + if (!list_empty(&local->roc_list)) return false; if (sdata->vif.type == NL80211_IFTYPE_STATION && diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 63a769015068..6b4f42527887 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -520,36 +520,16 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { u64 cookie = (unsigned long)skb; + acked = info->flags & IEEE80211_TX_STAT_ACK; if (ieee80211_is_nullfunc(hdr->frame_control) || - ieee80211_is_qos_nullfunc(hdr->frame_control)) { - acked = info->flags & IEEE80211_TX_STAT_ACK; - + ieee80211_is_qos_nullfunc(hdr->frame_control)) cfg80211_probe_status(skb->dev, hdr->addr1, cookie, acked, GFP_ATOMIC); - } else { - struct ieee80211_work *wk; - - rcu_read_lock(); - list_for_each_entry_rcu(wk, &local->work_list, list) { - if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX) - continue; - if (wk->offchan_tx.frame != skb) - continue; - wk->offchan_tx.status = true; - break; - } - rcu_read_unlock(); - if (local->hw_roc_skb_for_status == skb) { - cookie = local->hw_roc_cookie ^ 2; - local->hw_roc_skb_for_status = NULL; - } - + else cfg80211_mgmt_tx_status( skb->dev, cookie, skb->data, skb->len, - !!(info->flags & IEEE80211_TX_STAT_ACK), - GFP_ATOMIC); - } + acked, GFP_ATOMIC); } if (unlikely(info->ack_frame_id)) { diff --git a/net/mac80211/work.c b/net/mac80211/work.c deleted file mode 100644 index b2650a9d45ff..000000000000 --- a/net/mac80211/work.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * mac80211 work implementation - * - * Copyright 2003-2008, Jouni Malinen - * Copyright 2004, Instant802 Networks, Inc. - * Copyright 2005, Devicescape Software, Inc. - * Copyright 2006-2007 Jiri Benc - * Copyright 2007, Michael Wu - * Copyright 2009, Johannes Berg - * - * 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 - -#include "ieee80211_i.h" -#include "rate.h" -#include "driver-ops.h" - -enum work_action { - WORK_ACT_NONE, - WORK_ACT_TIMEOUT, -}; - - -/* utils */ -static inline void ASSERT_WORK_MTX(struct ieee80211_local *local) -{ - lockdep_assert_held(&local->mtx); -} - -/* - * We can have multiple work items (and connection probing) - * scheduling this timer, but we need to take care to only - * reschedule it when it should fire _earlier_ than it was - * asked for before, or if it's not pending right now. This - * function ensures that. Note that it then is required to - * run this function for all timeouts after the first one - * has happened -- the work that runs from this timer will - * do that. - */ -static void run_again(struct ieee80211_local *local, - unsigned long timeout) -{ - ASSERT_WORK_MTX(local); - - if (!timer_pending(&local->work_timer) || - time_before(timeout, local->work_timer.expires)) - mod_timer(&local->work_timer, timeout); -} - -void free_work(struct ieee80211_work *wk) -{ - kfree_rcu(wk, rcu_head); -} - -static enum work_action __must_check -ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk) -{ - /* - * First time we run, do nothing -- the generic code will - * have switched to the right channel etc. - */ - if (!wk->started) { - wk->timeout = jiffies + msecs_to_jiffies(wk->remain.duration); - - cfg80211_ready_on_channel(wk->sdata->dev, (unsigned long) wk, - wk->chan, wk->chan_type, - wk->remain.duration, GFP_KERNEL); - - return WORK_ACT_NONE; - } - - return WORK_ACT_TIMEOUT; -} - -static enum work_action __must_check -ieee80211_offchannel_tx(struct ieee80211_work *wk) -{ - if (!wk->started) { - wk->timeout = jiffies + msecs_to_jiffies(wk->offchan_tx.wait); - - /* - * After this, offchan_tx.frame remains but now is no - * longer a valid pointer -- we still need it as the - * cookie for canceling this work/status matching. - */ - ieee80211_tx_skb(wk->sdata, wk->offchan_tx.frame); - - return WORK_ACT_NONE; - } - - return WORK_ACT_TIMEOUT; -} - -static void ieee80211_work_timer(unsigned long data) -{ - struct ieee80211_local *local = (void *) data; - - if (local->quiescing) - return; - - ieee80211_queue_work(&local->hw, &local->work_work); -} - -static void ieee80211_work_work(struct work_struct *work) -{ - struct ieee80211_local *local = - container_of(work, struct ieee80211_local, work_work); - struct ieee80211_work *wk, *tmp; - LIST_HEAD(free_work); - enum work_action rma; - bool remain_off_channel = false; - - /* - * ieee80211_queue_work() should have picked up most cases, - * here we'll pick the rest. - */ - if (WARN(local->suspended, "work scheduled while going to suspend\n")) - return; - - mutex_lock(&local->mtx); - - if (local->scanning) { - mutex_unlock(&local->mtx); - return; - } - - ieee80211_recalc_idle(local); - - list_for_each_entry_safe(wk, tmp, &local->work_list, list) { - bool started = wk->started; - - /* mark work as started if it's on the current off-channel */ - if (!started && local->tmp_channel && - wk->chan == local->tmp_channel && - wk->chan_type == local->tmp_channel_type) { - started = true; - wk->timeout = jiffies; - } - - if (!started && !local->tmp_channel) { - ieee80211_offchannel_stop_vifs(local, true); - - local->tmp_channel = wk->chan; - local->tmp_channel_type = wk->chan_type; - - ieee80211_hw_config(local, 0); - - started = true; - wk->timeout = jiffies; - } - - /* don't try to work with items that aren't started */ - if (!started) - continue; - - if (time_is_after_jiffies(wk->timeout)) { - /* - * This work item isn't supposed to be worked on - * right now, but take care to adjust the timer - * properly. - */ - run_again(local, wk->timeout); - continue; - } - - switch (wk->type) { - default: - WARN_ON(1); - /* nothing */ - rma = WORK_ACT_NONE; - break; - case IEEE80211_WORK_ABORT: - rma = WORK_ACT_TIMEOUT; - break; - case IEEE80211_WORK_REMAIN_ON_CHANNEL: - rma = ieee80211_remain_on_channel_timeout(wk); - break; - case IEEE80211_WORK_OFFCHANNEL_TX: - rma = ieee80211_offchannel_tx(wk); - break; - } - - wk->started = started; - - switch (rma) { - case WORK_ACT_NONE: - /* might have changed the timeout */ - run_again(local, wk->timeout); - break; - case WORK_ACT_TIMEOUT: - list_del_rcu(&wk->list); - synchronize_rcu(); - list_add(&wk->list, &free_work); - break; - default: - WARN(1, "unexpected: %d", rma); - } - } - - list_for_each_entry(wk, &local->work_list, list) { - if (!wk->started) - continue; - if (wk->chan != local->tmp_channel || - wk->chan_type != local->tmp_channel_type) - continue; - remain_off_channel = true; - } - - if (!remain_off_channel && local->tmp_channel) { - local->tmp_channel = NULL; - ieee80211_hw_config(local, 0); - - ieee80211_offchannel_return(local, true); - - /* give connection some time to breathe */ - run_again(local, jiffies + HZ/2); - } - - ieee80211_recalc_idle(local); - ieee80211_run_deferred_scan(local); - - mutex_unlock(&local->mtx); - - list_for_each_entry_safe(wk, tmp, &free_work, list) { - wk->done(wk, NULL); - list_del(&wk->list); - kfree(wk); - } -} - -void ieee80211_add_work(struct ieee80211_work *wk) -{ - struct ieee80211_local *local; - - if (WARN_ON(!wk->chan)) - return; - - if (WARN_ON(!wk->sdata)) - return; - - if (WARN_ON(!wk->done)) - return; - - if (WARN_ON(!ieee80211_sdata_running(wk->sdata))) - return; - - wk->started = false; - - local = wk->sdata->local; - mutex_lock(&local->mtx); - list_add_tail(&wk->list, &local->work_list); - mutex_unlock(&local->mtx); - - ieee80211_queue_work(&local->hw, &local->work_work); -} - -void ieee80211_work_init(struct ieee80211_local *local) -{ - INIT_LIST_HEAD(&local->work_list); - setup_timer(&local->work_timer, ieee80211_work_timer, - (unsigned long)local); - INIT_WORK(&local->work_work, ieee80211_work_work); -} - -void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_work *wk; - bool cleanup = false; - - mutex_lock(&local->mtx); - list_for_each_entry(wk, &local->work_list, list) { - if (wk->sdata != sdata) - continue; - cleanup = true; - wk->type = IEEE80211_WORK_ABORT; - wk->started = true; - wk->timeout = jiffies; - } - mutex_unlock(&local->mtx); - - /* run cleanups etc. */ - if (cleanup) - ieee80211_work_work(&local->work_work); - - mutex_lock(&local->mtx); - list_for_each_entry(wk, &local->work_list, list) { - if (wk->sdata != sdata) - continue; - WARN_ON(1); - break; - } - mutex_unlock(&local->mtx); -} - -static enum work_done_result ieee80211_remain_done(struct ieee80211_work *wk, - struct sk_buff *skb) -{ - /* - * We are done serving the remain-on-channel command. - */ - cfg80211_remain_on_channel_expired(wk->sdata->dev, (unsigned long) wk, - wk->chan, wk->chan_type, - GFP_KERNEL); - - return WORK_DONE_DESTROY; -} - -int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, - unsigned int duration, u64 *cookie) -{ - struct ieee80211_work *wk; - - wk = kzalloc(sizeof(*wk), GFP_KERNEL); - if (!wk) - return -ENOMEM; - - wk->type = IEEE80211_WORK_REMAIN_ON_CHANNEL; - wk->chan = chan; - wk->chan_type = channel_type; - wk->sdata = sdata; - wk->done = ieee80211_remain_done; - - wk->remain.duration = duration; - - *cookie = (unsigned long) wk; - - ieee80211_add_work(wk); - - return 0; -} - -int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata, - u64 cookie) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_work *wk, *tmp; - bool found = false; - - mutex_lock(&local->mtx); - list_for_each_entry_safe(wk, tmp, &local->work_list, list) { - if ((unsigned long) wk == cookie) { - wk->timeout = jiffies; - found = true; - break; - } - } - mutex_unlock(&local->mtx); - - if (!found) - return -ENOENT; - - ieee80211_queue_work(&local->hw, &local->work_work); - - return 0; -} -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 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') 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 From 2c352f444ccfa966a1aa4fd8e9ee29381c467448 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 28 May 2012 21:04:09 +0000 Subject: netfilter: nf_conntrack: prepare namespace support for l4 protocol trackers This patch prepares the namespace support for layer 4 protocol trackers. Basically, this modifies the following interfaces: * nf_ct_[un]register_sysctl * nf_conntrack_l4proto_[un]register to include the namespace parameter. We still use init_net in this patch to prepare the ground for follow-up patches for each layer 4 protocol tracker. We add a new net_id field to struct nf_conntrack_l4proto that is used to store the pernet_operations id for each layer 4 protocol tracker. Note that AF_INET6's protocols do not need to do sysctl compat. Thus, we only register compat sysctl when l4proto.l3proto != AF_INET6. Acked-by: Eric W. Biederman Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 11 +- include/net/netns/conntrack.h | 12 +++ net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 18 ++-- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 18 ++-- net/netfilter/nf_conntrack_proto.c | 143 ++++++++++++++++++------- net/netfilter/nf_conntrack_proto_dccp.c | 10 +- net/netfilter/nf_conntrack_proto_gre.c | 6 +- net/netfilter/nf_conntrack_proto_sctp.c | 10 +- net/netfilter/nf_conntrack_proto_udplite.c | 10 +- 9 files changed, 159 insertions(+), 79 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 3b572bb20aa2..d621c91de5c8 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -12,6 +12,7 @@ #include #include #include +#include struct seq_file; @@ -103,6 +104,10 @@ struct nf_conntrack_l4proto { struct ctl_table *ctl_compat_table; #endif #endif + int *net_id; + /* Init l4proto pernet data */ + int (*init_net)(struct net *net); + /* Protocol name */ const char *name; @@ -123,8 +128,10 @@ nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto); extern void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p); /* Protocol registration. */ -extern int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *proto); -extern void nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *proto); +extern int nf_conntrack_l4proto_register(struct net *net, + struct nf_conntrack_l4proto *proto); +extern void nf_conntrack_l4proto_unregister(struct net *net, + struct nf_conntrack_l4proto *proto); /* Generic netlink helpers */ extern int nf_ct_port_tuple_to_nlattr(struct sk_buff *skb, diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index a053a19870cf..1f53038b0d1b 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -8,6 +8,18 @@ struct ctl_table_header; struct nf_conntrack_ecache; +struct nf_proto_net { +#ifdef CONFIG_SYSCTL + struct ctl_table_header *ctl_table_header; + struct ctl_table *ctl_table; +#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT + struct ctl_table_header *ctl_compat_header; + struct ctl_table *ctl_compat_table; +#endif +#endif + unsigned int users; +}; + struct netns_ct { atomic_t count; unsigned int expect_count; diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 91747d4ebc26..46ec515db129 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -391,19 +391,19 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) return ret; } - ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_tcp4); + ret = nf_conntrack_l4proto_register(&init_net, &nf_conntrack_l4proto_tcp4); if (ret < 0) { pr_err("nf_conntrack_ipv4: can't register tcp.\n"); goto cleanup_sockopt; } - ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udp4); + ret = nf_conntrack_l4proto_register(&init_net, &nf_conntrack_l4proto_udp4); if (ret < 0) { pr_err("nf_conntrack_ipv4: can't register udp.\n"); goto cleanup_tcp; } - ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_icmp); + ret = nf_conntrack_l4proto_register(&init_net, &nf_conntrack_l4proto_icmp); if (ret < 0) { pr_err("nf_conntrack_ipv4: can't register icmp.\n"); goto cleanup_udp; @@ -434,11 +434,11 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) cleanup_ipv4: nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv4); cleanup_icmp: - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmp); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_icmp); cleanup_udp: - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp4); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_udp4); cleanup_tcp: - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp4); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_tcp4); cleanup_sockopt: nf_unregister_sockopt(&so_getorigdst); return ret; @@ -452,9 +452,9 @@ static void __exit nf_conntrack_l3proto_ipv4_fini(void) #endif nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv4); - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmp); - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp4); - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp4); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_icmp); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_udp4); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_tcp4); nf_unregister_sockopt(&so_getorigdst); } diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 3224ef90a21a..51ad9f104421 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -340,19 +340,19 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) need_conntrack(); nf_defrag_ipv6_enable(); - ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_tcp6); + ret = nf_conntrack_l4proto_register(&init_net, &nf_conntrack_l4proto_tcp6); if (ret < 0) { pr_err("nf_conntrack_ipv6: can't register tcp.\n"); return ret; } - ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udp6); + ret = nf_conntrack_l4proto_register(&init_net, &nf_conntrack_l4proto_udp6); if (ret < 0) { pr_err("nf_conntrack_ipv6: can't register udp.\n"); goto cleanup_tcp; } - ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_icmpv6); + ret = nf_conntrack_l4proto_register(&init_net, &nf_conntrack_l4proto_icmpv6); if (ret < 0) { pr_err("nf_conntrack_ipv6: can't register icmpv6.\n"); goto cleanup_udp; @@ -376,11 +376,11 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) cleanup_ipv6: nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6); cleanup_icmpv6: - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmpv6); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_icmpv6); cleanup_udp: - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp6); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_udp6); cleanup_tcp: - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp6); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_tcp6); return ret; } @@ -389,9 +389,9 @@ static void __exit nf_conntrack_l3proto_ipv6_fini(void) synchronize_net(); nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6); - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmpv6); - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp6); - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp6); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_icmpv6); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_udp6); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_tcp6); } module_init(nf_conntrack_l3proto_ipv6_init); diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 8b631b07a645..7ee31ac0a12c 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -36,28 +36,35 @@ static DEFINE_MUTEX(nf_ct_proto_mutex); #ifdef CONFIG_SYSCTL static int -nf_ct_register_sysctl(struct ctl_table_header **header, const char *path, - struct ctl_table *table, unsigned int *users) +nf_ct_register_sysctl(struct net *net, + struct ctl_table_header **header, + const char *path, + struct ctl_table *table, + unsigned int *users) { if (*header == NULL) { - *header = register_net_sysctl(&init_net, path, table); + *header = register_net_sysctl(net, path, table); if (*header == NULL) return -ENOMEM; } if (users != NULL) (*users)++; + return 0; } static void nf_ct_unregister_sysctl(struct ctl_table_header **header, - struct ctl_table *table, unsigned int *users) + struct ctl_table **table, + unsigned int *users) { if (users != NULL && --*users > 0) return; unregister_net_sysctl_table(*header); + kfree(*table); *header = NULL; + *table = NULL; } #endif @@ -167,7 +174,8 @@ static int nf_ct_l3proto_register_sysctl(struct nf_conntrack_l3proto *l3proto) #ifdef CONFIG_SYSCTL if (l3proto->ctl_table != NULL) { - err = nf_ct_register_sysctl(&l3proto->ctl_table_header, + err = nf_ct_register_sysctl(&init_net, + &l3proto->ctl_table_header, l3proto->ctl_table_path, l3proto->ctl_table, NULL); } @@ -180,7 +188,7 @@ static void nf_ct_l3proto_unregister_sysctl(struct nf_conntrack_l3proto *l3proto #ifdef CONFIG_SYSCTL if (l3proto->ctl_table_header != NULL) nf_ct_unregister_sysctl(&l3proto->ctl_table_header, - l3proto->ctl_table, NULL); + &l3proto->ctl_table, NULL); #endif } @@ -243,29 +251,54 @@ void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto) } EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister); -static int nf_ct_l4proto_register_sysctl(struct nf_conntrack_l4proto *l4proto) +static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, + struct nf_conntrack_l4proto *l4proto) +{ + if (l4proto->net_id) + return net_generic(net, *l4proto->net_id); + else + return NULL; +} + +static +int nf_ct_l4proto_register_sysctl(struct net *net, + struct nf_conntrack_l4proto *l4proto) { int err = 0; + struct nf_proto_net *pn = nf_ct_l4proto_net(net, l4proto); + if (pn == NULL) + return 0; #ifdef CONFIG_SYSCTL - if (l4proto->ctl_table != NULL) { - err = nf_ct_register_sysctl(l4proto->ctl_table_header, + if (pn->ctl_table != NULL) { + err = nf_ct_register_sysctl(net, + &pn->ctl_table_header, "net/netfilter", - l4proto->ctl_table, - l4proto->ctl_table_users); - if (err < 0) + pn->ctl_table, + &pn->users); + if (err < 0) { + if (!pn->users) { + kfree(pn->ctl_table); + pn->ctl_table = NULL; + } goto out; + } } #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT - if (l4proto->ctl_compat_table != NULL) { - err = nf_ct_register_sysctl(&l4proto->ctl_compat_table_header, + if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_table != NULL) { + err = nf_ct_register_sysctl(net, + &pn->ctl_compat_header, "net/ipv4/netfilter", - l4proto->ctl_compat_table, NULL); + pn->ctl_compat_table, + NULL); if (err == 0) goto out; - nf_ct_unregister_sysctl(l4proto->ctl_table_header, - l4proto->ctl_table, - l4proto->ctl_table_users); + + kfree(pn->ctl_compat_table); + pn->ctl_compat_table = NULL; + nf_ct_unregister_sysctl(&pn->ctl_table_header, + &pn->ctl_table, + &pn->users); } #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ out: @@ -273,25 +306,34 @@ out: return err; } -static void nf_ct_l4proto_unregister_sysctl(struct nf_conntrack_l4proto *l4proto) +static +void nf_ct_l4proto_unregister_sysctl(struct net *net, + struct nf_conntrack_l4proto *l4proto) { + struct nf_proto_net *pn = nf_ct_l4proto_net(net, l4proto); + if (pn == NULL) + return; #ifdef CONFIG_SYSCTL - if (l4proto->ctl_table_header != NULL && - *l4proto->ctl_table_header != NULL) - nf_ct_unregister_sysctl(l4proto->ctl_table_header, - l4proto->ctl_table, - l4proto->ctl_table_users); + if (pn->ctl_table_header != NULL) + nf_ct_unregister_sysctl(&pn->ctl_table_header, + &pn->ctl_table, + &pn->users); + #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT - if (l4proto->ctl_compat_table_header != NULL) - nf_ct_unregister_sysctl(&l4proto->ctl_compat_table_header, - l4proto->ctl_compat_table, NULL); + if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_header != NULL) + nf_ct_unregister_sysctl(&pn->ctl_compat_header, + &pn->ctl_compat_table, + NULL); #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ +#else + pn->users--; #endif /* CONFIG_SYSCTL */ } /* FIXME: Allow NULL functions and sub in pointers to generic for them. --RR */ -int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto) +static int +nf_conntrack_l4proto_register_net(struct nf_conntrack_l4proto *l4proto) { int ret = 0; @@ -333,10 +375,6 @@ int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto) goto out_unlock; } - ret = nf_ct_l4proto_register_sysctl(l4proto); - if (ret < 0) - goto out_unlock; - l4proto->nla_size = 0; if (l4proto->nlattr_size) l4proto->nla_size += l4proto->nlattr_size(); @@ -345,17 +383,34 @@ int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto) rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], l4proto); - out_unlock: mutex_unlock(&nf_ct_proto_mutex); return ret; } -EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register); -void nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *l4proto) +int nf_conntrack_l4proto_register(struct net *net, + struct nf_conntrack_l4proto *l4proto) { - struct net *net; + int ret = 0; + if (net == &init_net) + ret = nf_conntrack_l4proto_register_net(l4proto); + + if (ret < 0) + return ret; + + if (l4proto->init_net) + ret = l4proto->init_net(net); + if (ret < 0) + return ret; + + return nf_ct_l4proto_register_sysctl(net, l4proto); +} +EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register); + +static void +nf_conntrack_l4proto_unregister_net(struct nf_conntrack_l4proto *l4proto) +{ BUG_ON(l4proto->l3proto >= PF_MAX); mutex_lock(&nf_ct_proto_mutex); @@ -365,15 +420,21 @@ void nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *l4proto) ) != l4proto); rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], &nf_conntrack_l4proto_generic); - nf_ct_l4proto_unregister_sysctl(l4proto); mutex_unlock(&nf_ct_proto_mutex); synchronize_rcu(); +} +void nf_conntrack_l4proto_unregister(struct net *net, + struct nf_conntrack_l4proto *l4proto) +{ + if (net == &init_net) + nf_conntrack_l4proto_unregister_net(l4proto); + + nf_ct_l4proto_unregister_sysctl(net, l4proto); /* Remove all contrack entries for this protocol */ rtnl_lock(); - for_each_net(net) - nf_ct_iterate_cleanup(net, kill_l4proto, l4proto); + nf_ct_iterate_cleanup(net, kill_l4proto, l4proto); rtnl_unlock(); } EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister); @@ -383,7 +444,7 @@ int nf_conntrack_proto_init(void) unsigned int i; int err; - err = nf_ct_l4proto_register_sysctl(&nf_conntrack_l4proto_generic); + err = nf_ct_l4proto_register_sysctl(&init_net, &nf_conntrack_l4proto_generic); if (err < 0) return err; @@ -397,7 +458,7 @@ void nf_conntrack_proto_fini(void) { unsigned int i; - nf_ct_l4proto_unregister_sysctl(&nf_conntrack_l4proto_generic); + nf_ct_l4proto_unregister_sysctl(&init_net, &nf_conntrack_l4proto_generic); /* free l3proto protocol tables */ for (i = 0; i < PF_MAX; i++) diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index ef706a485be1..5a8e03724289 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -945,17 +945,17 @@ static int __init nf_conntrack_proto_dccp_init(void) if (err < 0) goto err1; - err = nf_conntrack_l4proto_register(&dccp_proto4); + err = nf_conntrack_l4proto_register(&init_net, &dccp_proto4); if (err < 0) goto err2; - err = nf_conntrack_l4proto_register(&dccp_proto6); + err = nf_conntrack_l4proto_register(&init_net, &dccp_proto6); if (err < 0) goto err3; return 0; err3: - nf_conntrack_l4proto_unregister(&dccp_proto4); + nf_conntrack_l4proto_unregister(&init_net, &dccp_proto4); err2: unregister_pernet_subsys(&dccp_net_ops); err1: @@ -965,8 +965,8 @@ err1: static void __exit nf_conntrack_proto_dccp_fini(void) { unregister_pernet_subsys(&dccp_net_ops); - nf_conntrack_l4proto_unregister(&dccp_proto6); - nf_conntrack_l4proto_unregister(&dccp_proto4); + nf_conntrack_l4proto_unregister(&init_net, &dccp_proto6); + nf_conntrack_l4proto_unregister(&init_net, &dccp_proto4); } module_init(nf_conntrack_proto_dccp_init); diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 4bf6b4e4b776..132f0d2d82cc 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -396,18 +396,18 @@ static int __init nf_ct_proto_gre_init(void) { int rv; - rv = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_gre4); + rv = nf_conntrack_l4proto_register(&init_net, &nf_conntrack_l4proto_gre4); if (rv < 0) return rv; rv = register_pernet_subsys(&proto_gre_net_ops); if (rv < 0) - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_gre4); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_gre4); return rv; } static void __exit nf_ct_proto_gre_fini(void) { - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_gre4); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_gre4); unregister_pernet_subsys(&proto_gre_net_ops); } diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 996db2fa21f7..97bbc20e1a2b 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -791,12 +791,12 @@ static int __init nf_conntrack_proto_sctp_init(void) { int ret; - ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_sctp4); + ret = nf_conntrack_l4proto_register(&init_net, &nf_conntrack_l4proto_sctp4); if (ret) { pr_err("nf_conntrack_l4proto_sctp4: protocol register failed\n"); goto out; } - ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_sctp6); + ret = nf_conntrack_l4proto_register(&init_net, &nf_conntrack_l4proto_sctp6); if (ret) { pr_err("nf_conntrack_l4proto_sctp6: protocol register failed\n"); goto cleanup_sctp4; @@ -805,15 +805,15 @@ static int __init nf_conntrack_proto_sctp_init(void) return ret; cleanup_sctp4: - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_sctp4); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_sctp4); out: return ret; } static void __exit nf_conntrack_proto_sctp_fini(void) { - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_sctp6); - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_sctp4); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_sctp6); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_sctp4); } module_init(nf_conntrack_proto_sctp_init); diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c index 4d60a5376aa6..fa142a81496c 100644 --- a/net/netfilter/nf_conntrack_proto_udplite.c +++ b/net/netfilter/nf_conntrack_proto_udplite.c @@ -299,23 +299,23 @@ static int __init nf_conntrack_proto_udplite_init(void) { int err; - err = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udplite4); + err = nf_conntrack_l4proto_register(&init_net, &nf_conntrack_l4proto_udplite4); if (err < 0) goto err1; - err = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udplite6); + err = nf_conntrack_l4proto_register(&init_net, &nf_conntrack_l4proto_udplite6); if (err < 0) goto err2; return 0; err2: - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udplite4); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_udplite4); err1: return err; } static void __exit nf_conntrack_proto_udplite_exit(void) { - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udplite6); - nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udplite4); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_udplite6); + nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_udplite4); } module_init(nf_conntrack_proto_udplite_init); -- cgit v1.2.3 From 524a53e5ad5f34f64ed34281e8b0eca19437db5b Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 28 May 2012 21:04:10 +0000 Subject: netfilter: nf_conntrack: prepare namespace support for l3 protocol trackers This patch prepares the namespace support for layer 3 protocol trackers. Basically, this modifies the following interfaces: * nf_ct_l3proto_[un]register_sysctl. * nf_conntrack_l3proto_[un]register. We add a new nf_ct_l3proto_net is used to get the pernet data of l3proto. This adds rhe new struct nf_ip_net that is used to store the sysctl header and l3proto_ipv4,l4proto_tcp(6),l4proto_udp(6),l4proto_icmp(v6) because the protos such tcp and tcp6 use the same data,so making nf_ip_net as a field of netns_ct is the easiest way to manager it. This patch also adds init_net to struct nf_conntrack_l3proto to initial the layer 3 protocol pernet data. Acked-by: Eric W. Biederman Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l3proto.h | 9 ++- include/net/netns/conntrack.h | 8 +++ net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 6 +- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 6 +- net/netfilter/nf_conntrack_proto.c | 92 ++++++++++++++++++++------ 5 files changed, 91 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index 9699c028b74b..d6df8c71a7fe 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -69,6 +69,9 @@ struct nf_conntrack_l3proto { struct ctl_table *ctl_table; #endif /* CONFIG_SYSCTL */ + /* Init l3proto pernet data */ + int (*init_net)(struct net *net); + /* Module (if any) which this is connected to. */ struct module *me; }; @@ -76,8 +79,10 @@ struct nf_conntrack_l3proto { extern struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX]; /* Protocol registration. */ -extern int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto); -extern void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto); +extern int nf_conntrack_l3proto_register(struct net *net, + struct nf_conntrack_l3proto *proto); +extern void nf_conntrack_l3proto_unregister(struct net *net, + struct nf_conntrack_l3proto *proto); extern struct nf_conntrack_l3proto *nf_ct_l3proto_find_get(u_int16_t l3proto); extern void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p); diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index 1f53038b0d1b..b2dbcc5cd813 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -20,6 +20,13 @@ struct nf_proto_net { unsigned int users; }; +struct nf_ip_net { +#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) + struct ctl_table_header *ctl_table_header; + struct ctl_table *ctl_table; +#endif +}; + struct netns_ct { atomic_t count; unsigned int expect_count; @@ -40,6 +47,7 @@ struct netns_ct { unsigned int sysctl_log_invalid; /* Log invalid packets */ int sysctl_auto_assign_helper; bool auto_assign_helper_warned; + struct nf_ip_net nf_ct_proto; #ifdef CONFIG_SYSCTL struct ctl_table_header *sysctl_header; struct ctl_table_header *acct_sysctl_header; diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 46ec515db129..0c0fb906c19d 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -409,7 +409,7 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) goto cleanup_udp; } - ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv4); + ret = nf_conntrack_l3proto_register(&init_net, &nf_conntrack_l3proto_ipv4); if (ret < 0) { pr_err("nf_conntrack_ipv4: can't register ipv4\n"); goto cleanup_icmp; @@ -432,7 +432,7 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); #endif cleanup_ipv4: - nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv4); + nf_conntrack_l3proto_unregister(&init_net, &nf_conntrack_l3proto_ipv4); cleanup_icmp: nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_icmp); cleanup_udp: @@ -451,7 +451,7 @@ static void __exit nf_conntrack_l3proto_ipv4_fini(void) nf_conntrack_ipv4_compat_fini(); #endif nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); - nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv4); + nf_conntrack_l3proto_unregister(&init_net, &nf_conntrack_l3proto_ipv4); nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_icmp); nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_udp4); nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_tcp4); diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 51ad9f104421..7334cbfd6003 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -358,7 +358,7 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) goto cleanup_udp; } - ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv6); + ret = nf_conntrack_l3proto_register(&init_net, &nf_conntrack_l3proto_ipv6); if (ret < 0) { pr_err("nf_conntrack_ipv6: can't register ipv6\n"); goto cleanup_icmpv6; @@ -374,7 +374,7 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) return ret; cleanup_ipv6: - nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6); + nf_conntrack_l3proto_unregister(&init_net, &nf_conntrack_l3proto_ipv6); cleanup_icmpv6: nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_icmpv6); cleanup_udp: @@ -388,7 +388,7 @@ static void __exit nf_conntrack_l3proto_ipv6_fini(void) { synchronize_net(); nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); - nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6); + nf_conntrack_l3proto_unregister(&init_net, &nf_conntrack_l3proto_ipv6); nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_icmpv6); nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_udp6); nf_conntrack_l4proto_unregister(&init_net, &nf_conntrack_l4proto_tcp6); diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 7ee31ac0a12c..a8daf0faadb7 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -168,31 +168,57 @@ static int kill_l4proto(struct nf_conn *i, void *data) nf_ct_l3num(i) == l4proto->l3proto; } -static int nf_ct_l3proto_register_sysctl(struct nf_conntrack_l3proto *l3proto) +static struct nf_ip_net *nf_ct_l3proto_net(struct net *net, + struct nf_conntrack_l3proto *l3proto) +{ + if (l3proto->l3proto == PF_INET) + return &net->ct.nf_ct_proto; + else + return NULL; +} + +static int nf_ct_l3proto_register_sysctl(struct net *net, + struct nf_conntrack_l3proto *l3proto) { int err = 0; + struct nf_ip_net *in = nf_ct_l3proto_net(net, l3proto); + /* nf_conntrack_l3proto_ipv6 doesn't support sysctl */ + if (in == NULL) + return 0; -#ifdef CONFIG_SYSCTL - if (l3proto->ctl_table != NULL) { - err = nf_ct_register_sysctl(&init_net, - &l3proto->ctl_table_header, +#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) + if (in->ctl_table != NULL) { + err = nf_ct_register_sysctl(net, + &in->ctl_table_header, l3proto->ctl_table_path, - l3proto->ctl_table, NULL); + in->ctl_table, + NULL); + if (err < 0) { + kfree(in->ctl_table); + in->ctl_table = NULL; + } } #endif return err; } -static void nf_ct_l3proto_unregister_sysctl(struct nf_conntrack_l3proto *l3proto) +static void nf_ct_l3proto_unregister_sysctl(struct net *net, + struct nf_conntrack_l3proto *l3proto) { -#ifdef CONFIG_SYSCTL - if (l3proto->ctl_table_header != NULL) - nf_ct_unregister_sysctl(&l3proto->ctl_table_header, - &l3proto->ctl_table, NULL); + struct nf_ip_net *in = nf_ct_l3proto_net(net, l3proto); + + if (in == NULL) + return; +#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) + if (in->ctl_table_header != NULL) + nf_ct_unregister_sysctl(&in->ctl_table_header, + &in->ctl_table, + NULL); #endif } -int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto) +static int +nf_conntrack_l3proto_register_net(struct nf_conntrack_l3proto *proto) { int ret = 0; struct nf_conntrack_l3proto *old; @@ -211,10 +237,6 @@ int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto) goto out_unlock; } - ret = nf_ct_l3proto_register_sysctl(proto); - if (ret < 0) - goto out_unlock; - if (proto->nlattr_tuple_size) proto->nla_size = 3 * proto->nlattr_tuple_size(); @@ -223,13 +245,32 @@ int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto) out_unlock: mutex_unlock(&nf_ct_proto_mutex); return ret; + } -EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register); -void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto) +int nf_conntrack_l3proto_register(struct net *net, + struct nf_conntrack_l3proto *proto) { - struct net *net; + int ret = 0; + + if (net == &init_net) + ret = nf_conntrack_l3proto_register_net(proto); + if (ret < 0) + return ret; + + if (proto->init_net) { + ret = proto->init_net(net); + if (ret < 0) + return ret; + } + return nf_ct_l3proto_register_sysctl(net, proto); +} +EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register); + +static void +nf_conntrack_l3proto_unregister_net(struct nf_conntrack_l3proto *proto) +{ BUG_ON(proto->l3proto >= AF_MAX); mutex_lock(&nf_ct_proto_mutex); @@ -238,15 +279,22 @@ void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto) ) != proto); rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], &nf_conntrack_l3proto_generic); - nf_ct_l3proto_unregister_sysctl(proto); mutex_unlock(&nf_ct_proto_mutex); synchronize_rcu(); +} + +void nf_conntrack_l3proto_unregister(struct net *net, + struct nf_conntrack_l3proto *proto) +{ + if (net == &init_net) + nf_conntrack_l3proto_unregister_net(proto); + + nf_ct_l3proto_unregister_sysctl(net, proto); /* Remove all contrack entries for this protocol */ rtnl_lock(); - for_each_net(net) - nf_ct_iterate_cleanup(net, kill_l3proto, proto); + nf_ct_iterate_cleanup(net, kill_l3proto, proto); rtnl_unlock(); } EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister); -- cgit v1.2.3 From 15f585bd76b6bd2974b23c9e69ff038a0826a0be Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 28 May 2012 21:04:11 +0000 Subject: netfilter: nf_ct_generic: add namespace support This patch adds namespace support for the generic layer 4 protocol tracker. Acked-by: Eric W. Biederman Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_core.h | 4 +-- include/net/netns/conntrack.h | 6 ++++ net/netfilter/nf_conntrack_core.c | 17 ++++------- net/netfilter/nf_conntrack_proto.c | 46 +++++++++++++++++++----------- net/netfilter/nf_conntrack_proto_generic.c | 38 ++++++++++++++++++++++-- 5 files changed, 78 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h index aced085132e7..d8f5b9f52169 100644 --- a/include/net/netfilter/nf_conntrack_core.h +++ b/include/net/netfilter/nf_conntrack_core.h @@ -28,8 +28,8 @@ extern unsigned int nf_conntrack_in(struct net *net, extern int nf_conntrack_init(struct net *net); extern void nf_conntrack_cleanup(struct net *net); -extern int nf_conntrack_proto_init(void); -extern void nf_conntrack_proto_fini(void); +extern int nf_conntrack_proto_init(struct net *net); +extern void nf_conntrack_proto_fini(struct net *net); extern bool nf_ct_get_tuple(const struct sk_buff *skb, diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index b2dbcc5cd813..0ef8592d48bf 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -20,7 +20,13 @@ struct nf_proto_net { unsigned int users; }; +struct nf_generic_net { + struct nf_proto_net pn; + unsigned int timeout; +}; + struct nf_ip_net { + struct nf_generic_net generic; #if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) struct ctl_table_header *ctl_table_header; struct ctl_table *ctl_table; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index ac3af97cc468..068f2e0ec58e 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1333,7 +1333,6 @@ static void nf_conntrack_cleanup_init_net(void) while (untrack_refs() > 0) schedule(); - nf_conntrack_proto_fini(); #ifdef CONFIG_NF_CONNTRACK_ZONES nf_ct_extend_unregister(&nf_ct_zone_extend); #endif @@ -1372,7 +1371,7 @@ void nf_conntrack_cleanup(struct net *net) netfilter framework. Roll on, two-stage module delete... */ synchronize_net(); - + nf_conntrack_proto_fini(net); nf_conntrack_cleanup_net(net); if (net_eq(net, &init_net)) { @@ -1496,11 +1495,6 @@ static int nf_conntrack_init_init_net(void) printk(KERN_INFO "nf_conntrack version %s (%u buckets, %d max)\n", NF_CONNTRACK_VERSION, nf_conntrack_htable_size, nf_conntrack_max); - - ret = nf_conntrack_proto_init(); - if (ret < 0) - goto err_proto; - #ifdef CONFIG_NF_CONNTRACK_ZONES ret = nf_ct_extend_register(&nf_ct_zone_extend); if (ret < 0) @@ -1518,9 +1512,7 @@ static int nf_conntrack_init_init_net(void) #ifdef CONFIG_NF_CONNTRACK_ZONES err_extend: - nf_conntrack_proto_fini(); #endif -err_proto: return ret; } @@ -1583,9 +1575,7 @@ static int nf_conntrack_init_net(struct net *net) ret = nf_conntrack_helper_init(net); if (ret < 0) goto err_helper; - return 0; - err_helper: nf_conntrack_timeout_fini(net); err_timeout: @@ -1622,6 +1612,9 @@ int nf_conntrack_init(struct net *net) if (ret < 0) goto out_init_net; } + ret = nf_conntrack_proto_init(net); + if (ret < 0) + goto out_proto; ret = nf_conntrack_init_net(net); if (ret < 0) goto out_net; @@ -1637,6 +1630,8 @@ int nf_conntrack_init(struct net *net) return 0; out_net: + nf_conntrack_proto_fini(net); +out_proto: if (net_eq(net, &init_net)) nf_conntrack_cleanup_init_net(); out_init_net: diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index a8daf0faadb7..b095b4aefd7c 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -302,10 +302,16 @@ EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister); static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, struct nf_conntrack_l4proto *l4proto) { - if (l4proto->net_id) - return net_generic(net, *l4proto->net_id); - else - return NULL; + switch (l4proto->l4proto) { + case 255: /* l4proto_generic */ + return (struct nf_proto_net *)&net->ct.nf_ct_proto.generic; + default: + if (l4proto->net_id) + return net_generic(net, *l4proto->net_id); + else + return NULL; + } + return NULL; } static @@ -487,28 +493,34 @@ void nf_conntrack_l4proto_unregister(struct net *net, } EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister); -int nf_conntrack_proto_init(void) +int nf_conntrack_proto_init(struct net *net) { unsigned int i; int err; - - err = nf_ct_l4proto_register_sysctl(&init_net, &nf_conntrack_l4proto_generic); + err = nf_conntrack_l4proto_generic.init_net(net); + if (err < 0) + return err; + err = nf_ct_l4proto_register_sysctl(net, + &nf_conntrack_l4proto_generic); if (err < 0) return err; - for (i = 0; i < AF_MAX; i++) - rcu_assign_pointer(nf_ct_l3protos[i], - &nf_conntrack_l3proto_generic); + if (net == &init_net) { + for (i = 0; i < AF_MAX; i++) + rcu_assign_pointer(nf_ct_l3protos[i], + &nf_conntrack_l3proto_generic); + } return 0; } -void nf_conntrack_proto_fini(void) +void nf_conntrack_proto_fini(struct net *net) { unsigned int i; - - nf_ct_l4proto_unregister_sysctl(&init_net, &nf_conntrack_l4proto_generic); - - /* free l3proto protocol tables */ - for (i = 0; i < PF_MAX; i++) - kfree(nf_ct_protos[i]); + nf_ct_l4proto_unregister_sysctl(net, + &nf_conntrack_l4proto_generic); + if (net == &init_net) { + /* free l3proto protocol tables */ + for (i = 0; i < PF_MAX; i++) + kfree(nf_ct_protos[i]); + } } diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index d8923d54b358..19bc880eb4e2 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -14,6 +14,11 @@ static unsigned int nf_ct_generic_timeout __read_mostly = 600*HZ; +static inline struct nf_generic_net *generic_pernet(struct net *net) +{ + return &net->ct.nf_ct_proto.generic; +} + static bool generic_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, struct nf_conntrack_tuple *tuple) @@ -42,7 +47,7 @@ static int generic_print_tuple(struct seq_file *s, static unsigned int *generic_get_timeouts(struct net *net) { - return &nf_ct_generic_timeout; + return &(generic_pernet(net)->timeout); } /* Returns verdict for packet, or -1 for invalid. */ @@ -110,7 +115,6 @@ static struct ctl_table_header *generic_sysctl_header; static struct ctl_table generic_sysctl_table[] = { { .procname = "nf_conntrack_generic_timeout", - .data = &nf_ct_generic_timeout, .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, @@ -121,7 +125,6 @@ static struct ctl_table generic_sysctl_table[] = { static struct ctl_table generic_compat_sysctl_table[] = { { .procname = "ip_conntrack_generic_timeout", - .data = &nf_ct_generic_timeout, .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, @@ -131,6 +134,34 @@ static struct ctl_table generic_compat_sysctl_table[] = { #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ #endif /* CONFIG_SYSCTL */ +static int generic_init_net(struct net *net) +{ + struct nf_generic_net *gn = generic_pernet(net); + struct nf_proto_net *pn = (struct nf_proto_net *)gn; + gn->timeout = nf_ct_generic_timeout; +#ifdef CONFIG_SYSCTL + pn->ctl_table = kmemdup(generic_sysctl_table, + sizeof(generic_sysctl_table), + GFP_KERNEL); + if (!pn->ctl_table) + return -ENOMEM; + pn->ctl_table[0].data = &gn->timeout; + +#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT + pn->ctl_compat_table = kmemdup(generic_compat_sysctl_table, + sizeof(generic_compat_sysctl_table), + GFP_KERNEL); + if (!pn->ctl_compat_table) { + kfree(pn->ctl_table); + pn->ctl_table = NULL; + return -ENOMEM; + } + pn->ctl_compat_table[0].data = &gn->timeout; +#endif +#endif + return 0; +} + struct nf_conntrack_l4proto nf_conntrack_l4proto_generic __read_mostly = { .l3proto = PF_UNSPEC, @@ -158,4 +189,5 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_generic __read_mostly = .ctl_compat_table = generic_compat_sysctl_table, #endif #endif + .init_net = generic_init_net, }; -- cgit v1.2.3 From d2ba1fde42af44fbce361202e9af13daff9e4381 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 28 May 2012 21:04:12 +0000 Subject: netfilter: nf_ct_tcp: add namespace support This patch adds namespace support for TCP protocol tracker. Acked-by: Eric W. Biederman Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netns/conntrack.h | 10 ++ net/netfilter/nf_conntrack_proto.c | 2 + net/netfilter/nf_conntrack_proto_tcp.c | 162 +++++++++++++++++++++++++++------ 3 files changed, 145 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index 0ef8592d48bf..680d799ece8b 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -4,6 +4,7 @@ #include #include #include +#include struct ctl_table_header; struct nf_conntrack_ecache; @@ -25,8 +26,17 @@ struct nf_generic_net { unsigned int timeout; }; +struct nf_tcp_net { + struct nf_proto_net pn; + unsigned int timeouts[TCP_CONNTRACK_TIMEOUT_MAX]; + unsigned int tcp_loose; + unsigned int tcp_be_liberal; + unsigned int tcp_max_retrans; +}; + struct nf_ip_net { struct nf_generic_net generic; + struct nf_tcp_net tcp; #if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) struct ctl_table_header *ctl_table_header; struct ctl_table *ctl_table; diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index b095b4aefd7c..8a71e8bb0d6c 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -303,6 +303,8 @@ static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, struct nf_conntrack_l4proto *l4proto) { switch (l4proto->l4proto) { + case IPPROTO_TCP: + return (struct nf_proto_net *)&net->ct.nf_ct_proto.tcp; case 255: /* l4proto_generic */ return (struct nf_proto_net *)&net->ct.nf_ct_proto.generic; default: diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 21ff1a99f534..a053f6786b3c 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -270,6 +270,11 @@ static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = { } }; +static inline struct nf_tcp_net *tcp_pernet(struct net *net) +{ + return &net->ct.nf_ct_proto.tcp; +} + static bool tcp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, struct nf_conntrack_tuple *tuple) { @@ -516,6 +521,7 @@ static bool tcp_in_window(const struct nf_conn *ct, u_int8_t pf) { struct net *net = nf_ct_net(ct); + struct nf_tcp_net *tn = tcp_pernet(net); struct ip_ct_tcp_state *sender = &state->seen[dir]; struct ip_ct_tcp_state *receiver = &state->seen[!dir]; const struct nf_conntrack_tuple *tuple = &ct->tuplehash[dir].tuple; @@ -720,7 +726,7 @@ static bool tcp_in_window(const struct nf_conn *ct, } else { res = false; if (sender->flags & IP_CT_TCP_FLAG_BE_LIBERAL || - nf_ct_tcp_be_liberal) + tn->tcp_be_liberal) res = true; if (!res && LOG_INVALID(net, IPPROTO_TCP)) nf_log_packet(pf, 0, skb, NULL, NULL, NULL, @@ -828,6 +834,7 @@ static int tcp_packet(struct nf_conn *ct, unsigned int *timeouts) { struct net *net = nf_ct_net(ct); + struct nf_tcp_net *tn = tcp_pernet(net); struct nf_conntrack_tuple *tuple; enum tcp_conntrack new_state, old_state; enum ip_conntrack_dir dir; @@ -1020,7 +1027,7 @@ static int tcp_packet(struct nf_conn *ct, && new_state == TCP_CONNTRACK_FIN_WAIT) ct->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT; - if (ct->proto.tcp.retrans >= nf_ct_tcp_max_retrans && + if (ct->proto.tcp.retrans >= tn->tcp_max_retrans && timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS]) timeout = timeouts[TCP_CONNTRACK_RETRANS]; else if ((ct->proto.tcp.seen[0].flags | ct->proto.tcp.seen[1].flags) & @@ -1065,6 +1072,8 @@ static bool tcp_new(struct nf_conn *ct, const struct sk_buff *skb, enum tcp_conntrack new_state; const struct tcphdr *th; struct tcphdr _tcph; + struct net *net = nf_ct_net(ct); + struct nf_tcp_net *tn = tcp_pernet(net); const struct ip_ct_tcp_state *sender = &ct->proto.tcp.seen[0]; const struct ip_ct_tcp_state *receiver = &ct->proto.tcp.seen[1]; @@ -1093,7 +1102,7 @@ static bool tcp_new(struct nf_conn *ct, const struct sk_buff *skb, ct->proto.tcp.seen[0].td_end; tcp_options(skb, dataoff, th, &ct->proto.tcp.seen[0]); - } else if (nf_ct_tcp_loose == 0) { + } else if (tn->tcp_loose == 0) { /* Don't try to pick up connections. */ return false; } else { @@ -1360,91 +1369,78 @@ static struct ctl_table_header *tcp_sysctl_header; static struct ctl_table tcp_sysctl_table[] = { { .procname = "nf_conntrack_tcp_timeout_syn_sent", - .data = &tcp_timeouts[TCP_CONNTRACK_SYN_SENT], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "nf_conntrack_tcp_timeout_syn_recv", - .data = &tcp_timeouts[TCP_CONNTRACK_SYN_RECV], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "nf_conntrack_tcp_timeout_established", - .data = &tcp_timeouts[TCP_CONNTRACK_ESTABLISHED], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "nf_conntrack_tcp_timeout_fin_wait", - .data = &tcp_timeouts[TCP_CONNTRACK_FIN_WAIT], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "nf_conntrack_tcp_timeout_close_wait", - .data = &tcp_timeouts[TCP_CONNTRACK_CLOSE_WAIT], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "nf_conntrack_tcp_timeout_last_ack", - .data = &tcp_timeouts[TCP_CONNTRACK_LAST_ACK], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "nf_conntrack_tcp_timeout_time_wait", - .data = &tcp_timeouts[TCP_CONNTRACK_TIME_WAIT], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "nf_conntrack_tcp_timeout_close", - .data = &tcp_timeouts[TCP_CONNTRACK_CLOSE], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "nf_conntrack_tcp_timeout_max_retrans", - .data = &tcp_timeouts[TCP_CONNTRACK_RETRANS], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "nf_conntrack_tcp_timeout_unacknowledged", - .data = &tcp_timeouts[TCP_CONNTRACK_UNACK], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "nf_conntrack_tcp_loose", - .data = &nf_ct_tcp_loose, .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec, }, { .procname = "nf_conntrack_tcp_be_liberal", - .data = &nf_ct_tcp_be_liberal, .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec, }, { .procname = "nf_conntrack_tcp_max_retrans", - .data = &nf_ct_tcp_max_retrans, .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec, @@ -1456,91 +1452,78 @@ static struct ctl_table tcp_sysctl_table[] = { static struct ctl_table tcp_compat_sysctl_table[] = { { .procname = "ip_conntrack_tcp_timeout_syn_sent", - .data = &tcp_timeouts[TCP_CONNTRACK_SYN_SENT], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "ip_conntrack_tcp_timeout_syn_sent2", - .data = &tcp_timeouts[TCP_CONNTRACK_SYN_SENT2], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "ip_conntrack_tcp_timeout_syn_recv", - .data = &tcp_timeouts[TCP_CONNTRACK_SYN_RECV], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "ip_conntrack_tcp_timeout_established", - .data = &tcp_timeouts[TCP_CONNTRACK_ESTABLISHED], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "ip_conntrack_tcp_timeout_fin_wait", - .data = &tcp_timeouts[TCP_CONNTRACK_FIN_WAIT], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "ip_conntrack_tcp_timeout_close_wait", - .data = &tcp_timeouts[TCP_CONNTRACK_CLOSE_WAIT], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "ip_conntrack_tcp_timeout_last_ack", - .data = &tcp_timeouts[TCP_CONNTRACK_LAST_ACK], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "ip_conntrack_tcp_timeout_time_wait", - .data = &tcp_timeouts[TCP_CONNTRACK_TIME_WAIT], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "ip_conntrack_tcp_timeout_close", - .data = &tcp_timeouts[TCP_CONNTRACK_CLOSE], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "ip_conntrack_tcp_timeout_max_retrans", - .data = &tcp_timeouts[TCP_CONNTRACK_RETRANS], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "ip_conntrack_tcp_loose", - .data = &nf_ct_tcp_loose, .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec, }, { .procname = "ip_conntrack_tcp_be_liberal", - .data = &nf_ct_tcp_be_liberal, .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec, }, { .procname = "ip_conntrack_tcp_max_retrans", - .data = &nf_ct_tcp_max_retrans, .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec, @@ -1550,6 +1533,125 @@ static struct ctl_table tcp_compat_sysctl_table[] = { #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ #endif /* CONFIG_SYSCTL */ +static int tcp_kmemdup_sysctl_table(struct nf_proto_net *pn) +{ +#ifdef CONFIG_SYSCTL + struct nf_tcp_net *tn = (struct nf_tcp_net *)pn; + + if (pn->ctl_table) + return 0; + + pn->ctl_table = kmemdup(tcp_sysctl_table, + sizeof(tcp_sysctl_table), + GFP_KERNEL); + if (!pn->ctl_table) + return -ENOMEM; + + pn->ctl_table[0].data = &tn->timeouts[TCP_CONNTRACK_SYN_SENT]; + pn->ctl_table[1].data = &tn->timeouts[TCP_CONNTRACK_SYN_RECV]; + pn->ctl_table[2].data = &tn->timeouts[TCP_CONNTRACK_ESTABLISHED]; + pn->ctl_table[3].data = &tn->timeouts[TCP_CONNTRACK_FIN_WAIT]; + pn->ctl_table[4].data = &tn->timeouts[TCP_CONNTRACK_CLOSE_WAIT]; + pn->ctl_table[5].data = &tn->timeouts[TCP_CONNTRACK_LAST_ACK]; + pn->ctl_table[6].data = &tn->timeouts[TCP_CONNTRACK_TIME_WAIT]; + pn->ctl_table[7].data = &tn->timeouts[TCP_CONNTRACK_CLOSE]; + pn->ctl_table[8].data = &tn->timeouts[TCP_CONNTRACK_RETRANS]; + pn->ctl_table[9].data = &tn->timeouts[TCP_CONNTRACK_UNACK]; + pn->ctl_table[10].data = &tn->tcp_loose; + pn->ctl_table[11].data = &tn->tcp_be_liberal; + pn->ctl_table[12].data = &tn->tcp_max_retrans; +#endif + return 0; +} + +static int tcp_kmemdup_compat_sysctl_table(struct nf_proto_net *pn) +{ +#ifdef CONFIG_SYSCTL +#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT + struct nf_tcp_net *tn = (struct nf_tcp_net *)pn; + pn->ctl_compat_table = kmemdup(tcp_compat_sysctl_table, + sizeof(tcp_compat_sysctl_table), + GFP_KERNEL); + if (!pn->ctl_compat_table) + return -ENOMEM; + + pn->ctl_compat_table[0].data = &tn->timeouts[TCP_CONNTRACK_SYN_SENT]; + pn->ctl_compat_table[1].data = &tn->timeouts[TCP_CONNTRACK_SYN_SENT2]; + pn->ctl_compat_table[2].data = &tn->timeouts[TCP_CONNTRACK_SYN_RECV]; + pn->ctl_compat_table[3].data = &tn->timeouts[TCP_CONNTRACK_ESTABLISHED]; + pn->ctl_compat_table[4].data = &tn->timeouts[TCP_CONNTRACK_FIN_WAIT]; + pn->ctl_compat_table[5].data = &tn->timeouts[TCP_CONNTRACK_CLOSE_WAIT]; + pn->ctl_compat_table[6].data = &tn->timeouts[TCP_CONNTRACK_LAST_ACK]; + pn->ctl_compat_table[7].data = &tn->timeouts[TCP_CONNTRACK_TIME_WAIT]; + pn->ctl_compat_table[8].data = &tn->timeouts[TCP_CONNTRACK_CLOSE]; + pn->ctl_compat_table[9].data = &tn->timeouts[TCP_CONNTRACK_RETRANS]; + pn->ctl_compat_table[10].data = &tn->tcp_loose; + pn->ctl_compat_table[11].data = &tn->tcp_be_liberal; + pn->ctl_compat_table[12].data = &tn->tcp_max_retrans; +#endif +#endif + return 0; +} + +static int tcpv4_init_net(struct net *net) +{ + int i; + int ret = 0; + struct nf_tcp_net *tn = tcp_pernet(net); + struct nf_proto_net *pn = (struct nf_proto_net *)tn; + +#ifdef CONFIG_SYSCTL + if (!pn->ctl_table) { +#else + if (!pn->user++) { +#endif + for (i = 0; i < TCP_CONNTRACK_TIMEOUT_MAX; i++) + tn->timeouts[i] = tcp_timeouts[i]; + + tn->tcp_loose = nf_ct_tcp_loose; + tn->tcp_be_liberal = nf_ct_tcp_be_liberal; + tn->tcp_max_retrans = nf_ct_tcp_max_retrans; + } + + ret = tcp_kmemdup_compat_sysctl_table(pn); + + if (ret < 0) + return ret; + + ret = tcp_kmemdup_sysctl_table(pn); + +#ifdef CONFIG_SYSCTL +#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT + if (ret < 0) { + kfree(pn->ctl_compat_table); + pn->ctl_compat_table = NULL; + } +#endif +#endif + return ret; +} + +static int tcpv6_init_net(struct net *net) +{ + int i; + struct nf_tcp_net *tn = tcp_pernet(net); + struct nf_proto_net *pn = (struct nf_proto_net *)tn; + +#ifdef CONFIG_SYSCTL + if (!pn->ctl_table) { +#else + if (!pn->user++) { +#endif + for (i = 0; i < TCP_CONNTRACK_TIMEOUT_MAX; i++) + tn->timeouts[i] = tcp_timeouts[i]; + tn->tcp_loose = nf_ct_tcp_loose; + tn->tcp_be_liberal = nf_ct_tcp_be_liberal; + tn->tcp_max_retrans = nf_ct_tcp_max_retrans; + } + + return tcp_kmemdup_sysctl_table(pn); +} + struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly = { .l3proto = PF_INET, @@ -1590,6 +1692,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly = .ctl_compat_table = tcp_compat_sysctl_table, #endif #endif + .init_net = tcpv4_init_net, }; EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_tcp4); @@ -1630,5 +1733,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 __read_mostly = .ctl_table_header = &tcp_sysctl_header, .ctl_table = tcp_sysctl_table, #endif + .init_net = tcpv6_init_net, }; EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_tcp6); -- cgit v1.2.3 From 0ce490ad4387a67ee8ca5253476272d508fc0b6f Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 28 May 2012 21:04:13 +0000 Subject: netfilter: nf_ct_udp: add namespace support This patch adds namespace support for UDP protocol tracker. Acked-by: Eric W. Biederman Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netns/conntrack.h | 12 ++++ net/netfilter/nf_conntrack_proto.c | 2 + net/netfilter/nf_conntrack_proto_udp.c | 100 +++++++++++++++++++++++++++++---- 3 files changed, 103 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index 680d799ece8b..7bd14ab8ce1c 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -34,9 +34,21 @@ struct nf_tcp_net { unsigned int tcp_max_retrans; }; +enum udp_conntrack { + UDP_CT_UNREPLIED, + UDP_CT_REPLIED, + UDP_CT_MAX +}; + +struct nf_udp_net { + struct nf_proto_net pn; + unsigned int timeouts[UDP_CT_MAX]; +}; + struct nf_ip_net { struct nf_generic_net generic; struct nf_tcp_net tcp; + struct nf_udp_net udp; #if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) struct ctl_table_header *ctl_table_header; struct ctl_table *ctl_table; diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 8a71e8bb0d6c..9c6aee51dea2 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -305,6 +305,8 @@ static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, switch (l4proto->l4proto) { case IPPROTO_TCP: return (struct nf_proto_net *)&net->ct.nf_ct_proto.tcp; + case IPPROTO_UDP: + return (struct nf_proto_net *)&net->ct.nf_ct_proto.udp; case 255: /* l4proto_generic */ return (struct nf_proto_net *)&net->ct.nf_ct_proto.generic; default: diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index 7259a6bdeb49..f56c8905ddfb 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -25,17 +25,16 @@ #include #include -enum udp_conntrack { - UDP_CT_UNREPLIED, - UDP_CT_REPLIED, - UDP_CT_MAX -}; - static unsigned int udp_timeouts[UDP_CT_MAX] = { [UDP_CT_UNREPLIED] = 30*HZ, [UDP_CT_REPLIED] = 180*HZ, }; +static inline struct nf_udp_net *udp_pernet(struct net *net) +{ + return &net->ct.nf_ct_proto.udp; +} + static bool udp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, struct nf_conntrack_tuple *tuple) @@ -73,7 +72,7 @@ static int udp_print_tuple(struct seq_file *s, static unsigned int *udp_get_timeouts(struct net *net) { - return udp_timeouts; + return udp_pernet(net)->timeouts; } /* Returns verdict for packet, and may modify conntracktype */ @@ -205,14 +204,12 @@ static struct ctl_table_header *udp_sysctl_header; static struct ctl_table udp_sysctl_table[] = { { .procname = "nf_conntrack_udp_timeout", - .data = &udp_timeouts[UDP_CT_UNREPLIED], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "nf_conntrack_udp_timeout_stream", - .data = &udp_timeouts[UDP_CT_REPLIED], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, @@ -223,14 +220,12 @@ static struct ctl_table udp_sysctl_table[] = { static struct ctl_table udp_compat_sysctl_table[] = { { .procname = "ip_conntrack_udp_timeout", - .data = &udp_timeouts[UDP_CT_UNREPLIED], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "ip_conntrack_udp_timeout_stream", - .data = &udp_timeouts[UDP_CT_REPLIED], .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, @@ -240,6 +235,87 @@ static struct ctl_table udp_compat_sysctl_table[] = { #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ #endif /* CONFIG_SYSCTL */ +static int udp_kmemdup_sysctl_table(struct nf_proto_net *pn) +{ +#ifdef CONFIG_SYSCTL + struct nf_udp_net *un = (struct nf_udp_net *)pn; + if (pn->ctl_table) + return 0; + pn->ctl_table = kmemdup(udp_sysctl_table, + sizeof(udp_sysctl_table), + GFP_KERNEL); + if (!pn->ctl_table) + return -ENOMEM; + pn->ctl_table[0].data = &un->timeouts[UDP_CT_UNREPLIED]; + pn->ctl_table[1].data = &un->timeouts[UDP_CT_REPLIED]; +#endif + return 0; +} + +static int udp_kmemdup_compat_sysctl_table(struct nf_proto_net *pn) +{ +#ifdef CONFIG_SYSCTL +#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT + struct nf_udp_net *un = (struct nf_udp_net *)pn; + pn->ctl_compat_table = kmemdup(udp_compat_sysctl_table, + sizeof(udp_compat_sysctl_table), + GFP_KERNEL); + if (!pn->ctl_compat_table) + return -ENOMEM; + + pn->ctl_compat_table[0].data = &un->timeouts[UDP_CT_UNREPLIED]; + pn->ctl_compat_table[1].data = &un->timeouts[UDP_CT_REPLIED]; +#endif +#endif + return 0; +} + +static void udp_init_net_data(struct nf_udp_net *un) +{ + int i; +#ifdef CONFIG_SYSCTL + if (!un->pn.ctl_table) { +#else + if (!un->pn.user++) { +#endif + for (i = 0; i < UDP_CT_MAX; i++) + un->timeouts[i] = udp_timeouts[i]; + } +} + +static int udpv4_init_net(struct net *net) +{ + int ret; + struct nf_udp_net *un = udp_pernet(net); + struct nf_proto_net *pn = (struct nf_proto_net *)un; + + udp_init_net_data(un); + + ret = udp_kmemdup_compat_sysctl_table(pn); + if (ret < 0) + return ret; + + ret = udp_kmemdup_sysctl_table(pn); +#ifdef CONFIG_SYSCTL +#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT + if (ret < 0) { + kfree(pn->ctl_compat_table); + pn->ctl_compat_table = NULL; + } +#endif +#endif + return ret; +} + +static int udpv6_init_net(struct net *net) +{ + struct nf_udp_net *un = udp_pernet(net); + struct nf_proto_net *pn = (struct nf_proto_net *)un; + + udp_init_net_data(un); + return udp_kmemdup_sysctl_table(pn); +} + struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly = { .l3proto = PF_INET, @@ -275,6 +351,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly = .ctl_compat_table = udp_compat_sysctl_table, #endif #endif + .init_net = udpv4_init_net, }; EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udp4); @@ -310,5 +387,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6 __read_mostly = .ctl_table_header = &udp_sysctl_header, .ctl_table = udp_sysctl_table, #endif + .init_net = udpv6_init_net, }; EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udp6); -- cgit v1.2.3 From 4b626b9c5d35b4f99b073dc5d6457abddcbcf429 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 28 May 2012 21:04:14 +0000 Subject: netfilter: nf_ct_icmp: add namespace support This patch adds namespace support for ICMP protocol tracker. Acked-by: Eric W. Biederman Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netns/conntrack.h | 6 +++++ net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 38 +++++++++++++++++++++++++--- net/netfilter/nf_conntrack_proto.c | 2 ++ 3 files changed, 43 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index 7bd14ab8ce1c..3d8e9e3b08a6 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -45,10 +45,16 @@ struct nf_udp_net { unsigned int timeouts[UDP_CT_MAX]; }; +struct nf_icmp_net { + struct nf_proto_net pn; + unsigned int timeout; +}; + struct nf_ip_net { struct nf_generic_net generic; struct nf_tcp_net tcp; struct nf_udp_net udp; + struct nf_icmp_net icmp; #if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) struct ctl_table_header *ctl_table_header; struct ctl_table *ctl_table; diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index 0847e373d33c..a0eabeb36b9f 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -23,6 +23,11 @@ static unsigned int nf_ct_icmp_timeout __read_mostly = 30*HZ; +static inline struct nf_icmp_net *icmp_pernet(struct net *net) +{ + return &net->ct.nf_ct_proto.icmp; +} + static bool icmp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, struct nf_conntrack_tuple *tuple) { @@ -77,7 +82,7 @@ static int icmp_print_tuple(struct seq_file *s, static unsigned int *icmp_get_timeouts(struct net *net) { - return &nf_ct_icmp_timeout; + return &icmp_pernet(net)->timeout; } /* Returns verdict for packet, or -1 for invalid. */ @@ -312,7 +317,6 @@ static struct ctl_table_header *icmp_sysctl_header; static struct ctl_table icmp_sysctl_table[] = { { .procname = "nf_conntrack_icmp_timeout", - .data = &nf_ct_icmp_timeout, .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, @@ -323,7 +327,6 @@ static struct ctl_table icmp_sysctl_table[] = { static struct ctl_table icmp_compat_sysctl_table[] = { { .procname = "ip_conntrack_icmp_timeout", - .data = &nf_ct_icmp_timeout, .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, @@ -333,6 +336,34 @@ static struct ctl_table icmp_compat_sysctl_table[] = { #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ #endif /* CONFIG_SYSCTL */ +static int icmp_init_net(struct net *net) +{ + struct nf_icmp_net *in = icmp_pernet(net); + struct nf_proto_net *pn = (struct nf_proto_net *)in; + in->timeout = nf_ct_icmp_timeout; + +#ifdef CONFIG_SYSCTL + pn->ctl_table = kmemdup(icmp_sysctl_table, + sizeof(icmp_sysctl_table), + GFP_KERNEL); + if (!pn->ctl_table) + return -ENOMEM; + pn->ctl_table[0].data = &in->timeout; +#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT + pn->ctl_compat_table = kmemdup(icmp_compat_sysctl_table, + sizeof(icmp_compat_sysctl_table), + GFP_KERNEL); + if (!pn->ctl_compat_table) { + kfree(pn->ctl_table); + pn->ctl_table = NULL; + return -ENOMEM; + } + pn->ctl_compat_table[0].data = &in->timeout; +#endif +#endif + return 0; +} + struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly = { .l3proto = PF_INET, @@ -369,4 +400,5 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly = .ctl_compat_table = icmp_compat_sysctl_table, #endif #endif + .init_net = icmp_init_net, }; diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 9c6aee51dea2..dbade5f2b1d3 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -307,6 +307,8 @@ static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, return (struct nf_proto_net *)&net->ct.nf_ct_proto.tcp; case IPPROTO_UDP: return (struct nf_proto_net *)&net->ct.nf_ct_proto.udp; + case IPPROTO_ICMP: + return (struct nf_proto_net *)&net->ct.nf_ct_proto.icmp; case 255: /* l4proto_generic */ return (struct nf_proto_net *)&net->ct.nf_ct_proto.generic; default: -- cgit v1.2.3 From 7080ba0955438ecd2885c1b73fbd9760b1594a41 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 28 May 2012 21:04:15 +0000 Subject: netfilter: nf_ct_icmp: add namespace support This patch adds namespace support for ICMPv6 protocol tracker. Acked-by: Eric W. Biederman Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netns/conntrack.h | 1 + net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 25 +++++++++++++++++++++++-- net/netfilter/nf_conntrack_proto.c | 2 ++ 3 files changed, 26 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index 3d8e9e3b08a6..3aecdc7a84fb 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -55,6 +55,7 @@ struct nf_ip_net { struct nf_tcp_net tcp; struct nf_udp_net udp; struct nf_icmp_net icmp; + struct nf_icmp_net icmpv6; #if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) struct ctl_table_header *ctl_table_header; struct ctl_table *ctl_table; diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index 3e81904fbbcd..f606355200d8 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -29,6 +29,11 @@ static unsigned int nf_ct_icmpv6_timeout __read_mostly = 30*HZ; +static inline struct nf_icmp_net *icmpv6_pernet(struct net *net) +{ + return &net->ct.nf_ct_proto.icmpv6; +} + static bool icmpv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, struct nf_conntrack_tuple *tuple) @@ -90,7 +95,7 @@ static int icmpv6_print_tuple(struct seq_file *s, static unsigned int *icmpv6_get_timeouts(struct net *net) { - return &nf_ct_icmpv6_timeout; + return &icmpv6_pernet(net)->timeout; } /* Returns verdict for packet, or -1 for invalid. */ @@ -319,7 +324,6 @@ static struct ctl_table_header *icmpv6_sysctl_header; static struct ctl_table icmpv6_sysctl_table[] = { { .procname = "nf_conntrack_icmpv6_timeout", - .data = &nf_ct_icmpv6_timeout, .maxlen = sizeof(unsigned int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, @@ -328,6 +332,22 @@ static struct ctl_table icmpv6_sysctl_table[] = { }; #endif /* CONFIG_SYSCTL */ +static int icmpv6_init_net(struct net *net) +{ + struct nf_icmp_net *in = icmpv6_pernet(net); + struct nf_proto_net *pn = (struct nf_proto_net *)in; + in->timeout = nf_ct_icmpv6_timeout; +#ifdef CONFIG_SYSCTL + pn->ctl_table = kmemdup(icmpv6_sysctl_table, + sizeof(icmpv6_sysctl_table), + GFP_KERNEL); + if (!pn->ctl_table) + return -ENOMEM; + pn->ctl_table[0].data = &in->timeout; +#endif + return 0; +} + struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 __read_mostly = { .l3proto = PF_INET6, @@ -359,4 +379,5 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 __read_mostly = .ctl_table_header = &icmpv6_sysctl_header, .ctl_table = icmpv6_sysctl_table, #endif + .init_net = icmpv6_init_net, }; diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index dbade5f2b1d3..1ea919450fc3 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -309,6 +309,8 @@ static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, return (struct nf_proto_net *)&net->ct.nf_ct_proto.udp; case IPPROTO_ICMP: return (struct nf_proto_net *)&net->ct.nf_ct_proto.icmp; + case IPPROTO_ICMPV6: + return (struct nf_proto_net *)&net->ct.nf_ct_proto.icmpv6; case 255: /* l4proto_generic */ return (struct nf_proto_net *)&net->ct.nf_ct_proto.generic; default: -- cgit v1.2.3 From e76d0af5e45f4152e3fdcc103b753a8aff93fcb5 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 5 Jun 2012 18:35:48 +0200 Subject: netfilter: nf_conntrack: remove now unused sysctl for nf_conntrack_l[3|4]proto Since the sysctl data for l[3|4]proto now resides in pernet nf_proto_net. We can now remove this unused fields from struct nf_contrack_l[3,4]proto. Acked-by: Eric W. Biederman Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l3proto.h | 2 -- include/net/netfilter/nf_conntrack_l4proto.h | 10 ---------- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 1 - net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 8 -------- net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 5 ----- net/netfilter/nf_conntrack_proto_generic.c | 8 -------- net/netfilter/nf_conntrack_proto_sctp.c | 15 --------------- net/netfilter/nf_conntrack_proto_tcp.c | 15 --------------- net/netfilter/nf_conntrack_proto_udp.c | 15 --------------- net/netfilter/nf_conntrack_proto_udplite.c | 12 ------------ 10 files changed, 91 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index d6df8c71a7fe..6f7c13f4ac03 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -64,9 +64,7 @@ struct nf_conntrack_l3proto { size_t nla_size; #ifdef CONFIG_SYSCTL - struct ctl_table_header *ctl_table_header; const char *ctl_table_path; - struct ctl_table *ctl_table; #endif /* CONFIG_SYSCTL */ /* Init l3proto pernet data */ diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index d621c91de5c8..cfa2f89b031d 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -93,16 +93,6 @@ struct nf_conntrack_l4proto { unsigned int nlattr_max; const struct nla_policy *nla_policy; } ctnl_timeout; -#endif - -#ifdef CONFIG_SYSCTL - struct ctl_table_header **ctl_table_header; - struct ctl_table *ctl_table; - unsigned int *ctl_table_users; -#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT - struct ctl_table_header *ctl_compat_table_header; - struct ctl_table *ctl_compat_table; -#endif #endif int *net_id; /* Init l4proto pernet data */ diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 5c66203af51c..d79b961a8009 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -380,7 +380,6 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = { #endif #if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) .ctl_table_path = "net/ipv4/netfilter", - .ctl_table = ip_ct_sysctl_table, #endif .init_net = ipv4_init_net, .me = THIS_MODULE, diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index a0eabeb36b9f..2bca7a5e422b 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -313,7 +313,6 @@ icmp_timeout_nla_policy[CTA_TIMEOUT_ICMP_MAX+1] = { #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ #ifdef CONFIG_SYSCTL -static struct ctl_table_header *icmp_sysctl_header; static struct ctl_table icmp_sysctl_table[] = { { .procname = "nf_conntrack_icmp_timeout", @@ -393,12 +392,5 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly = .nla_policy = icmp_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ -#ifdef CONFIG_SYSCTL - .ctl_table_header = &icmp_sysctl_header, - .ctl_table = icmp_sysctl_table, -#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT - .ctl_compat_table = icmp_compat_sysctl_table, -#endif -#endif .init_net = icmp_init_net, }; diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index f606355200d8..1b7818f15f3d 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -320,7 +320,6 @@ icmpv6_timeout_nla_policy[CTA_TIMEOUT_ICMPV6_MAX+1] = { #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ #ifdef CONFIG_SYSCTL -static struct ctl_table_header *icmpv6_sysctl_header; static struct ctl_table icmpv6_sysctl_table[] = { { .procname = "nf_conntrack_icmpv6_timeout", @@ -375,9 +374,5 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 __read_mostly = .nla_policy = icmpv6_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ -#ifdef CONFIG_SYSCTL - .ctl_table_header = &icmpv6_sysctl_header, - .ctl_table = icmpv6_sysctl_table, -#endif .init_net = icmpv6_init_net, }; diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index 19bc880eb4e2..e4e2d2a38d3f 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -111,7 +111,6 @@ generic_timeout_nla_policy[CTA_TIMEOUT_GENERIC_MAX+1] = { #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ #ifdef CONFIG_SYSCTL -static struct ctl_table_header *generic_sysctl_header; static struct ctl_table generic_sysctl_table[] = { { .procname = "nf_conntrack_generic_timeout", @@ -182,12 +181,5 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_generic __read_mostly = .nla_policy = generic_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ -#ifdef CONFIG_SYSCTL - .ctl_table_header = &generic_sysctl_header, - .ctl_table = generic_sysctl_table, -#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT - .ctl_compat_table = generic_compat_sysctl_table, -#endif -#endif .init_net = generic_init_net, }; diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 9e5738db34df..d785f2c4182b 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -610,8 +610,6 @@ sctp_timeout_nla_policy[CTA_TIMEOUT_SCTP_MAX+1] = { #ifdef CONFIG_SYSCTL -static unsigned int sctp_sysctl_table_users; -static struct ctl_table_header *sctp_sysctl_header; static struct ctl_table sctp_sysctl_table[] = { { .procname = "nf_conntrack_sctp_timeout_closed", @@ -832,14 +830,6 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 __read_mostly = { .nla_policy = sctp_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ -#ifdef CONFIG_SYSCTL - .ctl_table_users = &sctp_sysctl_table_users, - .ctl_table_header = &sctp_sysctl_header, - .ctl_table = sctp_sysctl_table, -#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT - .ctl_compat_table = sctp_compat_sysctl_table, -#endif -#endif .net_id = &sctp_net_id, .init_net = sctpv4_init_net, }; @@ -873,11 +863,6 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 __read_mostly = { .nla_policy = sctp_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ -#endif -#ifdef CONFIG_SYSCTL - .ctl_table_users = &sctp_sysctl_table_users, - .ctl_table_header = &sctp_sysctl_header, - .ctl_table = sctp_sysctl_table, #endif .net_id = &sctp_net_id, .init_net = sctpv6_init_net, diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index a053f6786b3c..e57f2a888dae 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -1364,8 +1364,6 @@ static const struct nla_policy tcp_timeout_nla_policy[CTA_TIMEOUT_TCP_MAX+1] = { #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ #ifdef CONFIG_SYSCTL -static unsigned int tcp_sysctl_table_users; -static struct ctl_table_header *tcp_sysctl_header; static struct ctl_table tcp_sysctl_table[] = { { .procname = "nf_conntrack_tcp_timeout_syn_sent", @@ -1684,14 +1682,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly = .nla_policy = tcp_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ -#ifdef CONFIG_SYSCTL - .ctl_table_users = &tcp_sysctl_table_users, - .ctl_table_header = &tcp_sysctl_header, - .ctl_table = tcp_sysctl_table, -#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT - .ctl_compat_table = tcp_compat_sysctl_table, -#endif -#endif .init_net = tcpv4_init_net, }; EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_tcp4); @@ -1728,11 +1718,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 __read_mostly = .nla_policy = tcp_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ -#ifdef CONFIG_SYSCTL - .ctl_table_users = &tcp_sysctl_table_users, - .ctl_table_header = &tcp_sysctl_header, - .ctl_table = tcp_sysctl_table, -#endif .init_net = tcpv6_init_net, }; EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_tcp6); diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index f56c8905ddfb..db7abad44bc5 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -199,8 +199,6 @@ udp_timeout_nla_policy[CTA_TIMEOUT_UDP_MAX+1] = { #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ #ifdef CONFIG_SYSCTL -static unsigned int udp_sysctl_table_users; -static struct ctl_table_header *udp_sysctl_header; static struct ctl_table udp_sysctl_table[] = { { .procname = "nf_conntrack_udp_timeout", @@ -343,14 +341,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly = .nla_policy = udp_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ -#ifdef CONFIG_SYSCTL - .ctl_table_users = &udp_sysctl_table_users, - .ctl_table_header = &udp_sysctl_header, - .ctl_table = udp_sysctl_table, -#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT - .ctl_compat_table = udp_compat_sysctl_table, -#endif -#endif .init_net = udpv4_init_net, }; EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udp4); @@ -382,11 +372,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6 __read_mostly = .nla_policy = udp_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ -#ifdef CONFIG_SYSCTL - .ctl_table_users = &udp_sysctl_table_users, - .ctl_table_header = &udp_sysctl_header, - .ctl_table = udp_sysctl_table, -#endif .init_net = udpv6_init_net, }; EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udp6); diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c index 7f85b0850a44..2e25e985e8cf 100644 --- a/net/netfilter/nf_conntrack_proto_udplite.c +++ b/net/netfilter/nf_conntrack_proto_udplite.c @@ -215,8 +215,6 @@ udplite_timeout_nla_policy[CTA_TIMEOUT_UDPLITE_MAX+1] = { #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ #ifdef CONFIG_SYSCTL -static unsigned int udplite_sysctl_table_users; -static struct ctl_table_header *udplite_sysctl_header; static struct ctl_table udplite_sysctl_table[] = { { .procname = "nf_conntrack_udplite_timeout", @@ -287,11 +285,6 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4 __read_mostly = .nla_policy = udplite_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ -#ifdef CONFIG_SYSCTL - .ctl_table_users = &udplite_sysctl_table_users, - .ctl_table_header = &udplite_sysctl_header, - .ctl_table = udplite_sysctl_table, -#endif .net_id = &udplite_net_id, .init_net = udplite_init_net, }; @@ -324,11 +317,6 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6 __read_mostly = .nla_policy = udplite_timeout_nla_policy, }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ -#ifdef CONFIG_SYSCTL - .ctl_table_users = &udplite_sysctl_table_users, - .ctl_table_header = &udplite_sysctl_header, - .ctl_table = udplite_sysctl_table, -#endif .net_id = &udplite_net_id, .init_net = udplite_init_net, }; -- cgit v1.2.3 From 8264deb81853462da5cbcfb19b54c4fd9f3d88ba Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 28 May 2012 21:04:23 +0000 Subject: netfilter: nf_conntrack: add namespace support for cttimeout This patch adds namespace support for cttimeout. Acked-by: Eric W. Biederman Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 3 ++- net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 6 ++++-- net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 6 ++++-- net/netfilter/nf_conntrack_proto_dccp.c | 5 +++-- net/netfilter/nf_conntrack_proto_generic.c | 6 ++++-- net/netfilter/nf_conntrack_proto_gre.c | 8 +++++--- net/netfilter/nf_conntrack_proto_sctp.c | 6 ++++-- net/netfilter/nf_conntrack_proto_tcp.c | 6 ++++-- net/netfilter/nf_conntrack_proto_udp.c | 8 +++++--- net/netfilter/nf_conntrack_proto_udplite.c | 8 +++++--- net/netfilter/nfnetlink_cttimeout.c | 13 ++++++++----- 11 files changed, 48 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index cfa2f89b031d..81c52b5205f2 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -87,7 +87,8 @@ struct nf_conntrack_l4proto { #if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) struct { size_t obj_size; - int (*nlattr_to_obj)(struct nlattr *tb[], void *data); + int (*nlattr_to_obj)(struct nlattr *tb[], + struct net *net, void *data); int (*obj_to_nlattr)(struct sk_buff *skb, const void *data); unsigned int nlattr_max; diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index 2bca7a5e422b..041923cb67ad 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -279,16 +279,18 @@ static int icmp_nlattr_tuple_size(void) #include #include -static int icmp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +static int icmp_timeout_nlattr_to_obj(struct nlattr *tb[], + struct net *net, void *data) { unsigned int *timeout = data; + struct nf_icmp_net *in = icmp_pernet(net); if (tb[CTA_TIMEOUT_ICMP_TIMEOUT]) { *timeout = ntohl(nla_get_be32(tb[CTA_TIMEOUT_ICMP_TIMEOUT])) * HZ; } else { /* Set default ICMP timeout. */ - *timeout = nf_ct_icmp_timeout; + *timeout = in->timeout; } return 0; } diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index 1b7818f15f3d..63ed0121836c 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -286,16 +286,18 @@ static int icmpv6_nlattr_tuple_size(void) #include #include -static int icmpv6_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +static int icmpv6_timeout_nlattr_to_obj(struct nlattr *tb[], + struct net *net, void *data) { unsigned int *timeout = data; + struct nf_icmp_net *in = icmpv6_pernet(net); if (tb[CTA_TIMEOUT_ICMPV6_TIMEOUT]) { *timeout = ntohl(nla_get_be32(tb[CTA_TIMEOUT_ICMPV6_TIMEOUT])) * HZ; } else { /* Set default ICMPv6 timeout. */ - *timeout = nf_ct_icmpv6_timeout; + *timeout = in->timeout; } return 0; } diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index 8d798a613e3f..c33f76af913f 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -712,9 +712,10 @@ static int dccp_nlattr_size(void) #include #include -static int dccp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +static int dccp_timeout_nlattr_to_obj(struct nlattr *tb[], + struct net *net, void *data) { - struct dccp_net *dn = dccp_pernet(&init_net); + struct dccp_net *dn = dccp_pernet(net); unsigned int *timeouts = data; int i; diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index e4e2d2a38d3f..bb0e74fe0fae 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -75,16 +75,18 @@ static bool generic_new(struct nf_conn *ct, const struct sk_buff *skb, #include #include -static int generic_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +static int generic_timeout_nlattr_to_obj(struct nlattr *tb[], + struct net *net, void *data) { unsigned int *timeout = data; + struct nf_generic_net *gn = generic_pernet(net); if (tb[CTA_TIMEOUT_GENERIC_TIMEOUT]) *timeout = ntohl(nla_get_be32(tb[CTA_TIMEOUT_GENERIC_TIMEOUT])) * HZ; else { /* Set default generic timeout. */ - *timeout = nf_ct_generic_timeout; + *timeout = gn->timeout; } return 0; diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index e36973f9ef59..25ba5a2f5edc 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -304,13 +304,15 @@ static void gre_destroy(struct nf_conn *ct) #include #include -static int gre_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +static int gre_timeout_nlattr_to_obj(struct nlattr *tb[], + struct net *net, void *data) { unsigned int *timeouts = data; + struct netns_proto_gre *net_gre = gre_pernet(net); /* set default timeouts for GRE. */ - timeouts[GRE_CT_UNREPLIED] = gre_timeouts[GRE_CT_UNREPLIED]; - timeouts[GRE_CT_REPLIED] = gre_timeouts[GRE_CT_REPLIED]; + timeouts[GRE_CT_UNREPLIED] = net_gre->gre_timeouts[GRE_CT_UNREPLIED]; + timeouts[GRE_CT_REPLIED] = net_gre->gre_timeouts[GRE_CT_REPLIED]; if (tb[CTA_TIMEOUT_GRE_UNREPLIED]) { timeouts[GRE_CT_UNREPLIED] = diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index d785f2c4182b..8fb0582ad397 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -562,14 +562,16 @@ static int sctp_nlattr_size(void) #include #include -static int sctp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +static int sctp_timeout_nlattr_to_obj(struct nlattr *tb[], + struct net *net, void *data) { unsigned int *timeouts = data; + struct sctp_net *sn = sctp_pernet(net); int i; /* set default SCTP timeouts. */ for (i=0; itimeouts[i]; /* there's a 1:1 mapping between attributes and protocol states. */ for (i=CTA_TIMEOUT_SCTP_UNSPEC+1; i #include -static int tcp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +static int tcp_timeout_nlattr_to_obj(struct nlattr *tb[], + struct net *net, void *data) { unsigned int *timeouts = data; + struct nf_tcp_net *tn = tcp_pernet(net); int i; /* set default TCP timeouts. */ for (i=0; itimeouts[i]; if (tb[CTA_TIMEOUT_TCP_SYN_SENT]) { timeouts[TCP_CONNTRACK_SYN_SENT] = diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index db7abad44bc5..360565a95de4 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -156,13 +156,15 @@ static int udp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, #include #include -static int udp_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +static int udp_timeout_nlattr_to_obj(struct nlattr *tb[], + struct net *net, void *data) { unsigned int *timeouts = data; + struct nf_udp_net *un = udp_pernet(net); /* set default timeouts for UDP. */ - timeouts[UDP_CT_UNREPLIED] = udp_timeouts[UDP_CT_UNREPLIED]; - timeouts[UDP_CT_REPLIED] = udp_timeouts[UDP_CT_REPLIED]; + timeouts[UDP_CT_UNREPLIED] = un->timeouts[UDP_CT_UNREPLIED]; + timeouts[UDP_CT_REPLIED] = un->timeouts[UDP_CT_REPLIED]; if (tb[CTA_TIMEOUT_UDP_UNREPLIED]) { timeouts[UDP_CT_UNREPLIED] = diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c index 2e25e985e8cf..b32e700f8dde 100644 --- a/net/netfilter/nf_conntrack_proto_udplite.c +++ b/net/netfilter/nf_conntrack_proto_udplite.c @@ -172,13 +172,15 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl, #include #include -static int udplite_timeout_nlattr_to_obj(struct nlattr *tb[], void *data) +static int udplite_timeout_nlattr_to_obj(struct nlattr *tb[], + struct net *net, void *data) { unsigned int *timeouts = data; + struct udplite_net *un = udplite_pernet(net); /* set default timeouts for UDPlite. */ - timeouts[UDPLITE_CT_UNREPLIED] = udplite_timeouts[UDPLITE_CT_UNREPLIED]; - timeouts[UDPLITE_CT_REPLIED] = udplite_timeouts[UDPLITE_CT_REPLIED]; + timeouts[UDPLITE_CT_UNREPLIED] = un->timeouts[UDPLITE_CT_UNREPLIED]; + timeouts[UDPLITE_CT_REPLIED] = un->timeouts[UDPLITE_CT_REPLIED]; if (tb[CTA_TIMEOUT_UDPLITE_UNREPLIED]) { timeouts[UDPLITE_CT_UNREPLIED] = diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index 3e655288d1d6..cdecbc8fe965 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -49,8 +49,9 @@ static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = { static int ctnl_timeout_parse_policy(struct ctnl_timeout *timeout, - struct nf_conntrack_l4proto *l4proto, - const struct nlattr *attr) + struct nf_conntrack_l4proto *l4proto, + struct net *net, + const struct nlattr *attr) { int ret = 0; @@ -60,7 +61,8 @@ ctnl_timeout_parse_policy(struct ctnl_timeout *timeout, nla_parse_nested(tb, l4proto->ctnl_timeout.nlattr_max, attr, l4proto->ctnl_timeout.nla_policy); - ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, &timeout->data); + ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net, + &timeout->data); } return ret; } @@ -74,6 +76,7 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb, __u8 l4num; struct nf_conntrack_l4proto *l4proto; struct ctnl_timeout *timeout, *matching = NULL; + struct net *net = sock_net(skb->sk); char *name; int ret; @@ -117,7 +120,7 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb, goto err_proto_put; } - ret = ctnl_timeout_parse_policy(matching, l4proto, + ret = ctnl_timeout_parse_policy(matching, l4proto, net, cda[CTA_TIMEOUT_DATA]); return ret; } @@ -132,7 +135,7 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb, goto err_proto_put; } - ret = ctnl_timeout_parse_policy(timeout, l4proto, + ret = ctnl_timeout_parse_policy(timeout, l4proto, net, cda[CTA_TIMEOUT_DATA]); if (ret < 0) goto err; -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 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') 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 From c8a627ed06d6d49bf65015a2185c519335c4c83f Mon Sep 17 00:00:00 2001 From: Gao feng Date: Fri, 8 Jun 2012 01:20:41 +0000 Subject: inetpeer: add namespace support for inetpeer now inetpeer doesn't support namespace,the information will be leaking across namespace. this patch move the global vars v4_peers and v6_peers to netns_ipv4 and netns_ipv6 as a field peers. add struct pernet_operations inetpeer_ops to initial pernet inetpeer data. and change family_to_base and inet_getpeer to support namespace. Signed-off-by: Gao feng Signed-off-by: David S. Miller --- include/net/inetpeer.h | 10 ++++--- include/net/netns/ipv4.h | 2 +- include/net/netns/ipv6.h | 1 + net/ipv4/inetpeer.c | 68 +++++++++++++++++++++++++++++++++++------------- net/ipv4/route.c | 2 +- 5 files changed, 59 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index 2040bff945d4..fef9dfab5d45 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -75,7 +75,9 @@ static inline bool inet_metrics_new(const struct inet_peer *p) } /* can be called with or without local BH being disabled */ -struct inet_peer *inet_getpeer(const struct inetpeer_addr *daddr, int create); +struct inet_peer *inet_getpeer(struct net *net, + const struct inetpeer_addr *daddr, + int create); static inline struct inet_peer *inet_getpeer_v4(__be32 v4daddr, int create) { @@ -83,7 +85,7 @@ static inline struct inet_peer *inet_getpeer_v4(__be32 v4daddr, int create) daddr.addr.a4 = v4daddr; daddr.family = AF_INET; - return inet_getpeer(&daddr, create); + return inet_getpeer(&init_net, &daddr, create); } static inline struct inet_peer *inet_getpeer_v6(const struct in6_addr *v6daddr, int create) @@ -92,14 +94,14 @@ static inline struct inet_peer *inet_getpeer_v6(const struct in6_addr *v6daddr, *(struct in6_addr *)daddr.addr.a6 = *v6daddr; daddr.family = AF_INET6; - return inet_getpeer(&daddr, create); + return inet_getpeer(&init_net, &daddr, create); } /* can be called from BH context or outside */ extern void inet_putpeer(struct inet_peer *p); extern bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout); -extern void inetpeer_invalidate_tree(int family); +extern void inetpeer_invalidate_tree(struct net *net, int family); /* * temporary check to make sure we dont access rid, ip_id_count, tcp_ts, diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index bbd023a1c9b9..227f0cd9d3f6 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -30,7 +30,7 @@ struct netns_ipv4 { struct sock **icmp_sk; struct sock *tcp_sock; - + struct inet_peer_base *peers; struct netns_frags frags; #ifdef CONFIG_NETFILTER struct xt_table *iptable_filter; diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index b42be53587ba..df0a5456a3fd 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -33,6 +33,7 @@ struct netns_ipv6 { struct netns_sysctl_ipv6 sysctl; struct ipv6_devconf *devconf_all; struct ipv6_devconf *devconf_dflt; + struct inet_peer_base *peers; struct netns_frags frags; #ifdef CONFIG_NETFILTER struct xt_table *ip6table_filter; diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index dfba343b2509..1c8527349c86 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -88,18 +88,6 @@ struct inet_peer_base { int total; }; -static struct inet_peer_base v4_peers = { - .root = peer_avl_empty_rcu, - .lock = __SEQLOCK_UNLOCKED(v4_peers.lock), - .total = 0, -}; - -static struct inet_peer_base v6_peers = { - .root = peer_avl_empty_rcu, - .lock = __SEQLOCK_UNLOCKED(v6_peers.lock), - .total = 0, -}; - #define PEER_MAXDEPTH 40 /* sufficient for about 2^27 nodes */ /* Exported for sysctl_net_ipv4. */ @@ -153,6 +141,46 @@ static void inetpeer_gc_worker(struct work_struct *work) schedule_delayed_work(&gc_work, gc_delay); } +static int __net_init inetpeer_net_init(struct net *net) +{ + net->ipv4.peers = kzalloc(sizeof(struct inet_peer_base), + GFP_KERNEL); + if (net->ipv4.peers == NULL) + return -ENOMEM; + + net->ipv4.peers->root = peer_avl_empty_rcu; + seqlock_init(&net->ipv4.peers->lock); + + net->ipv6.peers = kzalloc(sizeof(struct inet_peer_base), + GFP_KERNEL); + if (net->ipv6.peers == NULL) + goto out_ipv6; + + net->ipv6.peers->root = peer_avl_empty_rcu; + seqlock_init(&net->ipv6.peers->lock); + + return 0; +out_ipv6: + kfree(net->ipv4.peers); + return -ENOMEM; +} + +static void __net_exit inetpeer_net_exit(struct net *net) +{ + inetpeer_invalidate_tree(net, AF_INET); + kfree(net->ipv4.peers); + net->ipv4.peers = NULL; + + inetpeer_invalidate_tree(net, AF_INET6); + kfree(net->ipv6.peers); + net->ipv6.peers = NULL; +} + +static struct pernet_operations inetpeer_ops = { + .init = inetpeer_net_init, + .exit = inetpeer_net_exit, +}; + /* Called from ip_output.c:ip_init */ void __init inet_initpeers(void) { @@ -177,6 +205,7 @@ void __init inet_initpeers(void) NULL); INIT_DELAYED_WORK_DEFERRABLE(&gc_work, inetpeer_gc_worker); + register_pernet_subsys(&inetpeer_ops); } static int addr_compare(const struct inetpeer_addr *a, @@ -401,9 +430,10 @@ static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base, call_rcu(&p->rcu, inetpeer_free_rcu); } -static struct inet_peer_base *family_to_base(int family) +static struct inet_peer_base *family_to_base(struct net *net, + int family) { - return family == AF_INET ? &v4_peers : &v6_peers; + return family == AF_INET ? net->ipv4.peers : net->ipv6.peers; } /* perform garbage collect on all items stacked during a lookup */ @@ -443,10 +473,12 @@ static int inet_peer_gc(struct inet_peer_base *base, return cnt; } -struct inet_peer *inet_getpeer(const struct inetpeer_addr *daddr, int create) +struct inet_peer *inet_getpeer(struct net *net, + const struct inetpeer_addr *daddr, + int create) { struct inet_peer __rcu **stack[PEER_MAXDEPTH], ***stackptr; - struct inet_peer_base *base = family_to_base(daddr->family); + struct inet_peer_base *base = family_to_base(net, daddr->family); struct inet_peer *p; unsigned int sequence; int invalidated, gccnt = 0; @@ -571,10 +603,10 @@ static void inetpeer_inval_rcu(struct rcu_head *head) schedule_delayed_work(&gc_work, gc_delay); } -void inetpeer_invalidate_tree(int family) +void inetpeer_invalidate_tree(struct net *net, int family) { struct inet_peer *old, *new, *prev; - struct inet_peer_base *base = family_to_base(family); + struct inet_peer_base *base = family_to_base(net, family); write_seqlock_bh(&base->lock); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 98b30d08efe9..006c21cc2324 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -938,7 +938,7 @@ static void rt_cache_invalidate(struct net *net) get_random_bytes(&shuffle, sizeof(shuffle)); atomic_add(shuffle + 1U, &net->ipv4.rt_genid); - inetpeer_invalidate_tree(AF_INET); + inetpeer_invalidate_tree(net, AF_INET); } /* -- cgit v1.2.3 From 54db0cc2ba0d38166acc2d6bae21721405305537 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Fri, 8 Jun 2012 01:21:40 +0000 Subject: inetpeer: add parameter net for inet_getpeer_v4,v6 add struct net as a parameter of inet_getpeer_v[4,6], use net to replace &init_net. and modify some places to provide net for inet_getpeer_v[4,6] Signed-off-by: Gao feng Signed-off-by: David S. Miller --- include/net/inetpeer.h | 12 ++++++++---- net/ipv4/inet_fragment.c | 2 +- net/ipv4/ip_fragment.c | 6 +++++- net/ipv4/route.c | 8 +++++--- net/ipv4/tcp_ipv4.c | 6 ++++-- net/ipv6/route.c | 3 ++- net/ipv6/tcp_ipv6.c | 6 ++++-- 7 files changed, 29 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index fef9dfab5d45..20e67db69ac9 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -79,22 +79,26 @@ struct inet_peer *inet_getpeer(struct net *net, const struct inetpeer_addr *daddr, int create); -static inline struct inet_peer *inet_getpeer_v4(__be32 v4daddr, int create) +static inline struct inet_peer *inet_getpeer_v4(struct net *net, + __be32 v4daddr, + int create) { struct inetpeer_addr daddr; daddr.addr.a4 = v4daddr; daddr.family = AF_INET; - return inet_getpeer(&init_net, &daddr, create); + return inet_getpeer(net, &daddr, create); } -static inline struct inet_peer *inet_getpeer_v6(const struct in6_addr *v6daddr, int create) +static inline struct inet_peer *inet_getpeer_v6(struct net *net, + const struct in6_addr *v6daddr, + int create) { struct inetpeer_addr daddr; *(struct in6_addr *)daddr.addr.a6 = *v6daddr; daddr.family = AF_INET6; - return inet_getpeer(&init_net, &daddr, create); + return inet_getpeer(net, &daddr, create); } /* can be called from BH context or outside */ diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 5ff2a51b6d0c..85190e69297b 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -243,12 +243,12 @@ static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf, if (q == NULL) return NULL; + q->net = nf; f->constructor(q, arg); atomic_add(f->qsize, &nf->mem); setup_timer(&q->timer, f->frag_expire, (unsigned long)q); spin_lock_init(&q->lock); atomic_set(&q->refcnt, 1); - q->net = nf; return q; } diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 9dbd3dd6022d..22c6bab9717a 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -171,6 +171,10 @@ static void frag_kfree_skb(struct netns_frags *nf, struct sk_buff *skb) static void ip4_frag_init(struct inet_frag_queue *q, void *a) { struct ipq *qp = container_of(q, struct ipq, q); + struct netns_ipv4 *ipv4 = container_of(q->net, struct netns_ipv4, + frags); + struct net *net = container_of(ipv4, struct net, ipv4); + struct ip4_create_arg *arg = a; qp->protocol = arg->iph->protocol; @@ -180,7 +184,7 @@ static void ip4_frag_init(struct inet_frag_queue *q, void *a) qp->daddr = arg->iph->daddr; qp->user = arg->user; qp->peer = sysctl_ipfrag_max_dist ? - inet_getpeer_v4(arg->iph->saddr, 1) : NULL; + inet_getpeer_v4(net, arg->iph->saddr, 1) : NULL; } static __inline__ void ip4_frag_free(struct inet_frag_queue *q) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 006c21cc2324..2c9f73f3b7cc 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1328,9 +1328,10 @@ static u32 rt_peer_genid(void) void rt_bind_peer(struct rtable *rt, __be32 daddr, int create) { + struct net *net = dev_net(rt->dst.dev); struct inet_peer *peer; - peer = inet_getpeer_v4(daddr, create); + peer = inet_getpeer_v4(net, daddr, create); if (peer && cmpxchg(&rt->peer, NULL, peer) != NULL) inet_putpeer(peer); @@ -1694,7 +1695,7 @@ unsigned short ip_rt_frag_needed(struct net *net, const struct iphdr *iph, unsigned short est_mtu = 0; struct inet_peer *peer; - peer = inet_getpeer_v4(iph->daddr, 1); + peer = inet_getpeer_v4(net, iph->daddr, 1); if (peer) { unsigned short mtu = new_mtu; @@ -1935,6 +1936,7 @@ static unsigned int ipv4_mtu(const struct dst_entry *dst) static void rt_init_metrics(struct rtable *rt, const struct flowi4 *fl4, struct fib_info *fi) { + struct net *net = dev_net(rt->dst.dev); struct inet_peer *peer; int create = 0; @@ -1944,7 +1946,7 @@ static void rt_init_metrics(struct rtable *rt, const struct flowi4 *fl4, if (fl4 && (fl4->flowi4_flags & FLOWI_FLAG_PRECOW_METRICS)) create = 1; - rt->peer = peer = inet_getpeer_v4(rt->rt_dst, create); + rt->peer = peer = inet_getpeer_v4(net, rt->rt_dst, create); if (peer) { rt->rt_peer_genid = rt_peer_genid(); if (inet_metrics_new(peer)) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 3d9c1a4b8819..f485b451f928 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1824,11 +1824,12 @@ struct inet_peer *tcp_v4_get_peer(struct sock *sk, bool *release_it) { struct rtable *rt = (struct rtable *) __sk_dst_get(sk); struct inet_sock *inet = inet_sk(sk); + struct net *net = sock_net(sk); struct inet_peer *peer; if (!rt || inet->cork.fl.u.ip4.daddr != inet->inet_daddr) { - peer = inet_getpeer_v4(inet->inet_daddr, 1); + peer = inet_getpeer_v4(net, inet->inet_daddr, 1); *release_it = true; } else { if (!rt->peer) @@ -1844,8 +1845,9 @@ 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(tw->tw_daddr, 1); + return inet_getpeer_v4(net, tw->tw_daddr, 1); } EXPORT_SYMBOL(tcp_v4_tw_get_peer); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 999a982ad3fd..4eca0130cce7 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -306,9 +306,10 @@ static u32 rt6_peer_genid(void) void rt6_bind_peer(struct rt6_info *rt, int create) { + struct net *net = dev_net(rt->dst.dev); struct inet_peer *peer; - peer = inet_getpeer_v6(&rt->rt6i_dst.addr, create); + peer = inet_getpeer_v6(net, &rt->rt6i_dst.addr, create); if (peer && cmpxchg(&rt->rt6i_peer, NULL, peer) != NULL) inet_putpeer(peer); else diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 80758255556c..1a9cdd09f11c 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1736,11 +1736,12 @@ static struct inet_peer *tcp_v6_get_peer(struct sock *sk, bool *release_it) { struct rt6_info *rt = (struct rt6_info *) __sk_dst_get(sk); struct ipv6_pinfo *np = inet6_sk(sk); + struct net *net = sock_net(sk); struct inet_peer *peer; if (!rt || !ipv6_addr_equal(&np->daddr, &rt->rt6i_dst.addr)) { - peer = inet_getpeer_v6(&np->daddr, 1); + peer = inet_getpeer_v6(net, &np->daddr, 1); *release_it = true; } else { if (!rt->rt6i_peer) @@ -1756,11 +1757,12 @@ 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(&tw6->tw_v6_daddr, 1); + return inet_getpeer_v6(net, &tw6->tw_v6_daddr, 1); } static struct timewait_sock_ops tcp6_timewait_sock_ops = { -- cgit v1.2.3 From 7123aaa3a1416529ce461e98108e6b343b294643 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 8 Jun 2012 05:03:21 +0000 Subject: af_unix: speedup /proc/net/unix /proc/net/unix has quadratic behavior, and can hold unix_table_lock for a while if high number of unix sockets are alive. (90 ms for 200k sockets...) We already have a hash table, so its quite easy to use it. Problem is unbound sockets are still hashed in a single hash slot (unix_socket_table[UNIX_HASH_TABLE]) This patch also spreads unbound sockets to 256 hash slots, to speedup both /proc/net/unix and unix_diag. Time to read /proc/net/unix with 200k unix sockets : (time dd if=/proc/net/unix of=/dev/null bs=4k) before : 520 secs after : 2 secs Signed-off-by: Eric Dumazet Cc: Steven Whitehouse Cc: Pavel Emelyanov Signed-off-by: David S. Miller --- include/net/af_unix.h | 3 +- net/unix/af_unix.c | 110 +++++++++++++++++++++++++++++--------------------- net/unix/diag.c | 6 ++- 3 files changed, 70 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/include/net/af_unix.h b/include/net/af_unix.h index 2ee33da36a7a..b5f8988e4283 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -14,10 +14,11 @@ extern struct sock *unix_get_socket(struct file *filp); extern struct sock *unix_peer_get(struct sock *); #define UNIX_HASH_SIZE 256 +#define UNIX_HASH_BITS 8 extern unsigned int unix_tot_inflight; extern spinlock_t unix_table_lock; -extern struct hlist_head unix_socket_table[UNIX_HASH_SIZE + 1]; +extern struct hlist_head unix_socket_table[2 * UNIX_HASH_SIZE]; struct unix_address { atomic_t refcnt; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 641f2e47f165..cf83f6b5ac91 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -115,15 +115,24 @@ #include #include -struct hlist_head unix_socket_table[UNIX_HASH_SIZE + 1]; +struct hlist_head unix_socket_table[2 * UNIX_HASH_SIZE]; EXPORT_SYMBOL_GPL(unix_socket_table); DEFINE_SPINLOCK(unix_table_lock); EXPORT_SYMBOL_GPL(unix_table_lock); static atomic_long_t unix_nr_socks; -#define unix_sockets_unbound (&unix_socket_table[UNIX_HASH_SIZE]) -#define UNIX_ABSTRACT(sk) (unix_sk(sk)->addr->hash != UNIX_HASH_SIZE) +static struct hlist_head *unix_sockets_unbound(void *addr) +{ + unsigned long hash = (unsigned long)addr; + + hash ^= hash >> 16; + hash ^= hash >> 8; + hash %= UNIX_HASH_SIZE; + return &unix_socket_table[UNIX_HASH_SIZE + hash]; +} + +#define UNIX_ABSTRACT(sk) (unix_sk(sk)->addr->hash < UNIX_HASH_SIZE) #ifdef CONFIG_SECURITY_NETWORK static void unix_get_secdata(struct scm_cookie *scm, struct sk_buff *skb) @@ -645,7 +654,7 @@ static struct sock *unix_create1(struct net *net, struct socket *sock) INIT_LIST_HEAD(&u->link); mutex_init(&u->readlock); /* single task reading lock */ init_waitqueue_head(&u->peer_wait); - unix_insert_socket(unix_sockets_unbound, sk); + unix_insert_socket(unix_sockets_unbound(sk), sk); out: if (sk == NULL) atomic_long_dec(&unix_nr_socks); @@ -2239,47 +2248,58 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock, } #ifdef CONFIG_PROC_FS -static struct sock *first_unix_socket(int *i) -{ - for (*i = 0; *i <= UNIX_HASH_SIZE; (*i)++) { - if (!hlist_empty(&unix_socket_table[*i])) - return __sk_head(&unix_socket_table[*i]); - } - return NULL; -} -static struct sock *next_unix_socket(int *i, struct sock *s) -{ - struct sock *next = sk_next(s); - /* More in this chain? */ - if (next) - return next; - /* Look for next non-empty chain. */ - for ((*i)++; *i <= UNIX_HASH_SIZE; (*i)++) { - if (!hlist_empty(&unix_socket_table[*i])) - return __sk_head(&unix_socket_table[*i]); - } - return NULL; -} +#define BUCKET_SPACE (BITS_PER_LONG - (UNIX_HASH_BITS + 1) - 1) + +#define get_bucket(x) ((x) >> BUCKET_SPACE) +#define get_offset(x) ((x) & ((1L << BUCKET_SPACE) - 1)) +#define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o)) struct unix_iter_state { struct seq_net_private p; - int i; }; -static struct sock *unix_seq_idx(struct seq_file *seq, loff_t pos) +static struct sock *unix_from_bucket(struct seq_file *seq, loff_t *pos) { - struct unix_iter_state *iter = seq->private; - loff_t off = 0; - struct sock *s; + unsigned long offset = get_offset(*pos); + unsigned long bucket = get_bucket(*pos); + struct sock *sk; + unsigned long count = 0; - for (s = first_unix_socket(&iter->i); s; s = next_unix_socket(&iter->i, s)) { - if (sock_net(s) != seq_file_net(seq)) + for (sk = sk_head(&unix_socket_table[bucket]); sk; sk = sk_next(sk)) { + if (sock_net(sk) != seq_file_net(seq)) continue; - if (off == pos) - return s; - ++off; + if (++count == offset) + break; } + + return sk; +} + +static struct sock *unix_next_socket(struct seq_file *seq, + struct sock *sk, + loff_t *pos) +{ + unsigned long bucket; + + while (sk > (struct sock *)SEQ_START_TOKEN) { + sk = sk_next(sk); + if (!sk) + goto next_bucket; + if (sock_net(sk) == seq_file_net(seq)) + return sk; + } + + do { + sk = unix_from_bucket(seq, pos); + if (sk) + return sk; + +next_bucket: + bucket = get_bucket(*pos) + 1; + *pos = set_bucket_offset(bucket, 1); + } while (bucket < ARRAY_SIZE(unix_socket_table)); + return NULL; } @@ -2287,22 +2307,20 @@ static void *unix_seq_start(struct seq_file *seq, loff_t *pos) __acquires(unix_table_lock) { spin_lock(&unix_table_lock); - return *pos ? unix_seq_idx(seq, *pos - 1) : SEQ_START_TOKEN; + + if (!*pos) + return SEQ_START_TOKEN; + + if (get_bucket(*pos) >= ARRAY_SIZE(unix_socket_table)) + return NULL; + + return unix_next_socket(seq, NULL, pos); } static void *unix_seq_next(struct seq_file *seq, void *v, loff_t *pos) { - struct unix_iter_state *iter = seq->private; - struct sock *sk = v; ++*pos; - - if (v == SEQ_START_TOKEN) - sk = first_unix_socket(&iter->i); - else - sk = next_unix_socket(&iter->i, sk); - while (sk && (sock_net(sk) != seq_file_net(seq))) - sk = next_unix_socket(&iter->i, sk); - return sk; + return unix_next_socket(seq, v, pos); } static void unix_seq_stop(struct seq_file *seq, void *v) diff --git a/net/unix/diag.c b/net/unix/diag.c index 47d3002737f5..7e8a24bff34a 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -195,7 +195,9 @@ static int unix_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) num = s_num = cb->args[1]; spin_lock(&unix_table_lock); - for (slot = s_slot; slot <= UNIX_HASH_SIZE; s_num = 0, slot++) { + for (slot = s_slot; + slot < ARRAY_SIZE(unix_socket_table); + s_num = 0, slot++) { struct sock *sk; struct hlist_node *node; @@ -228,7 +230,7 @@ static struct sock *unix_lookup_by_ino(int ino) struct sock *sk; spin_lock(&unix_table_lock); - for (i = 0; i <= UNIX_HASH_SIZE; i++) { + for (i = 0; i < ARRAY_SIZE(unix_socket_table); i++) { struct hlist_node *node; sk_for_each(sk, node, &unix_socket_table[i]) -- cgit v1.2.3 From f3109a51f8dc88e8a94f620240b7474b91bed37a Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 24 May 2012 18:59:10 +0200 Subject: lib: Proportions with flexible period Implement code computing proportions of events of different type (like code in lib/proportions.c) but allowing periods to have different lengths. This allows us to have aging periods of fixed wallclock time which gives better proportion estimates given the hugely varying throughput of different devices - previous measuring of aging period by number of events has the problem that a reasonable period length for a system with low-end USB stick is not a reasonable period length for a system with high-end storage array resulting either in too slow proportion updates or too fluctuating proportion updates. Acked-by: Peter Zijlstra Signed-off-by: Jan Kara Signed-off-by: Fengguang Wu --- include/linux/flex_proportions.h | 101 +++++++++++++++ lib/Makefile | 2 +- lib/flex_proportions.c | 266 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 include/linux/flex_proportions.h create mode 100644 lib/flex_proportions.c (limited to 'include') diff --git a/include/linux/flex_proportions.h b/include/linux/flex_proportions.h new file mode 100644 index 000000000000..4ebc49fae391 --- /dev/null +++ b/include/linux/flex_proportions.h @@ -0,0 +1,101 @@ +/* + * Floating proportions with flexible aging period + * + * Copyright (C) 2011, SUSE, Jan Kara + */ + +#ifndef _LINUX_FLEX_PROPORTIONS_H +#define _LINUX_FLEX_PROPORTIONS_H + +#include +#include +#include + +/* + * When maximum proportion of some event type is specified, this is the + * precision with which we allow limitting. Note that this creates an upper + * bound on the number of events per period like + * ULLONG_MAX >> FPROP_FRAC_SHIFT. + */ +#define FPROP_FRAC_SHIFT 10 +#define FPROP_FRAC_BASE (1UL << FPROP_FRAC_SHIFT) + +/* + * ---- Global proportion definitions ---- + */ +struct fprop_global { + /* Number of events in the current period */ + struct percpu_counter events; + /* Current period */ + unsigned int period; + /* Synchronization with period transitions */ + seqcount_t sequence; +}; + +int fprop_global_init(struct fprop_global *p); +void fprop_global_destroy(struct fprop_global *p); +bool fprop_new_period(struct fprop_global *p, int periods); + +/* + * ---- SINGLE ---- + */ +struct fprop_local_single { + /* the local events counter */ + unsigned long events; + /* Period in which we last updated events */ + unsigned int period; + raw_spinlock_t lock; /* Protect period and numerator */ +}; + +#define INIT_FPROP_LOCAL_SINGLE(name) \ +{ .lock = __RAW_SPIN_LOCK_UNLOCKED(name.lock), \ +} + +int fprop_local_init_single(struct fprop_local_single *pl); +void fprop_local_destroy_single(struct fprop_local_single *pl); +void __fprop_inc_single(struct fprop_global *p, struct fprop_local_single *pl); +void fprop_fraction_single(struct fprop_global *p, + struct fprop_local_single *pl, unsigned long *numerator, + unsigned long *denominator); + +static inline +void fprop_inc_single(struct fprop_global *p, struct fprop_local_single *pl) +{ + unsigned long flags; + + local_irq_save(flags); + __fprop_inc_single(p, pl); + local_irq_restore(flags); +} + +/* + * ---- PERCPU ---- + */ +struct fprop_local_percpu { + /* the local events counter */ + struct percpu_counter events; + /* Period in which we last updated events */ + unsigned int period; + raw_spinlock_t lock; /* Protect period and numerator */ +}; + +int fprop_local_init_percpu(struct fprop_local_percpu *pl); +void fprop_local_destroy_percpu(struct fprop_local_percpu *pl); +void __fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl); +void __fprop_inc_percpu_max(struct fprop_global *p, struct fprop_local_percpu *pl, + int max_frac); +void fprop_fraction_percpu(struct fprop_global *p, + struct fprop_local_percpu *pl, unsigned long *numerator, + unsigned long *denominator); + +static inline +void fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl) +{ + unsigned long flags; + + local_irq_save(flags); + __fprop_inc_percpu(p, pl); + local_irq_restore(flags); +} + +#endif diff --git a/lib/Makefile b/lib/Makefile index 8c31a0cb75e9..ee837378b107 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -11,7 +11,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \ rbtree.o radix-tree.o dump_stack.o timerqueue.o\ idr.o int_sqrt.o extable.o prio_tree.o \ sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \ - proportions.o prio_heap.o ratelimit.o show_mem.o \ + proportions.o flex_proportions.o prio_heap.o ratelimit.o show_mem.o \ is_single_threaded.o plist.o decompress.o lib-$(CONFIG_MMU) += ioremap.o diff --git a/lib/flex_proportions.c b/lib/flex_proportions.c new file mode 100644 index 000000000000..e02a3883ae01 --- /dev/null +++ b/lib/flex_proportions.c @@ -0,0 +1,266 @@ +/* + * Floating proportions with flexible aging period + * + * Copyright (C) 2011, SUSE, Jan Kara + * + * The goal of this code is: Given different types of event, measure proportion + * of each type of event over time. The proportions are measured with + * exponentially decaying history to give smooth transitions. A formula + * expressing proportion of event of type 'j' is: + * + * p_{j} = (\Sum_{i>=0} x_{i,j}/2^{i+1})/(\Sum_{i>=0} x_i/2^{i+1}) + * + * Where x_{i,j} is j's number of events in i-th last time period and x_i is + * total number of events in i-th last time period. + * + * Note that p_{j}'s are normalised, i.e. + * + * \Sum_{j} p_{j} = 1, + * + * This formula can be straightforwardly computed by maintaing denominator + * (let's call it 'd') and for each event type its numerator (let's call it + * 'n_j'). When an event of type 'j' happens, we simply need to do: + * n_j++; d++; + * + * When a new period is declared, we could do: + * d /= 2 + * for each j + * n_j /= 2 + * + * To avoid iteration over all event types, we instead shift numerator of event + * j lazily when someone asks for a proportion of event j or when event j + * occurs. This can bit trivially implemented by remembering last period in + * which something happened with proportion of type j. + */ +#include + +int fprop_global_init(struct fprop_global *p) +{ + int err; + + p->period = 0; + /* Use 1 to avoid dealing with periods with 0 events... */ + err = percpu_counter_init(&p->events, 1); + if (err) + return err; + seqcount_init(&p->sequence); + return 0; +} + +void fprop_global_destroy(struct fprop_global *p) +{ + percpu_counter_destroy(&p->events); +} + +/* + * Declare @periods new periods. It is upto the caller to make sure period + * transitions cannot happen in parallel. + * + * The function returns true if the proportions are still defined and false + * if aging zeroed out all events. This can be used to detect whether declaring + * further periods has any effect. + */ +bool fprop_new_period(struct fprop_global *p, int periods) +{ + u64 events = percpu_counter_sum(&p->events); + + /* + * Don't do anything if there are no events. + */ + if (events <= 1) + return false; + write_seqcount_begin(&p->sequence); + if (periods < 64) + events -= events >> periods; + /* Use addition to avoid losing events happening between sum and set */ + percpu_counter_add(&p->events, -events); + p->period += periods; + write_seqcount_end(&p->sequence); + + return true; +} + +/* + * ---- SINGLE ---- + */ + +int fprop_local_init_single(struct fprop_local_single *pl) +{ + pl->events = 0; + pl->period = 0; + raw_spin_lock_init(&pl->lock); + return 0; +} + +void fprop_local_destroy_single(struct fprop_local_single *pl) +{ +} + +static void fprop_reflect_period_single(struct fprop_global *p, + struct fprop_local_single *pl) +{ + unsigned int period = p->period; + unsigned long flags; + + /* Fast path - period didn't change */ + if (pl->period == period) + return; + raw_spin_lock_irqsave(&pl->lock, flags); + /* Someone updated pl->period while we were spinning? */ + if (pl->period >= period) { + raw_spin_unlock_irqrestore(&pl->lock, flags); + return; + } + /* Aging zeroed our fraction? */ + if (period - pl->period < BITS_PER_LONG) + pl->events >>= period - pl->period; + else + pl->events = 0; + pl->period = period; + raw_spin_unlock_irqrestore(&pl->lock, flags); +} + +/* Event of type pl happened */ +void __fprop_inc_single(struct fprop_global *p, struct fprop_local_single *pl) +{ + fprop_reflect_period_single(p, pl); + pl->events++; + percpu_counter_add(&p->events, 1); +} + +/* Return fraction of events of type pl */ +void fprop_fraction_single(struct fprop_global *p, + struct fprop_local_single *pl, + unsigned long *numerator, unsigned long *denominator) +{ + unsigned int seq; + s64 num, den; + + do { + seq = read_seqcount_begin(&p->sequence); + fprop_reflect_period_single(p, pl); + num = pl->events; + den = percpu_counter_read_positive(&p->events); + } while (read_seqcount_retry(&p->sequence, seq)); + + /* + * Make fraction <= 1 and denominator > 0 even in presence of percpu + * counter errors + */ + if (den <= num) { + if (num) + den = num; + else + den = 1; + } + *denominator = den; + *numerator = num; +} + +/* + * ---- PERCPU ---- + */ +#define PROP_BATCH (8*(1+ilog2(nr_cpu_ids))) + +int fprop_local_init_percpu(struct fprop_local_percpu *pl) +{ + int err; + + err = percpu_counter_init(&pl->events, 0); + if (err) + return err; + pl->period = 0; + raw_spin_lock_init(&pl->lock); + return 0; +} + +void fprop_local_destroy_percpu(struct fprop_local_percpu *pl) +{ + percpu_counter_destroy(&pl->events); +} + +static void fprop_reflect_period_percpu(struct fprop_global *p, + struct fprop_local_percpu *pl) +{ + unsigned int period = p->period; + unsigned long flags; + + /* Fast path - period didn't change */ + if (pl->period == period) + return; + raw_spin_lock_irqsave(&pl->lock, flags); + /* Someone updated pl->period while we were spinning? */ + if (pl->period >= period) { + raw_spin_unlock_irqrestore(&pl->lock, flags); + return; + } + /* Aging zeroed our fraction? */ + if (period - pl->period < BITS_PER_LONG) { + s64 val = percpu_counter_read(&pl->events); + + if (val < (nr_cpu_ids * PROP_BATCH)) + val = percpu_counter_sum(&pl->events); + + __percpu_counter_add(&pl->events, + -val + (val >> (period-pl->period)), PROP_BATCH); + } else + percpu_counter_set(&pl->events, 0); + pl->period = period; + raw_spin_unlock_irqrestore(&pl->lock, flags); +} + +/* Event of type pl happened */ +void __fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl) +{ + fprop_reflect_period_percpu(p, pl); + __percpu_counter_add(&pl->events, 1, PROP_BATCH); + percpu_counter_add(&p->events, 1); +} + +void fprop_fraction_percpu(struct fprop_global *p, + struct fprop_local_percpu *pl, + unsigned long *numerator, unsigned long *denominator) +{ + unsigned int seq; + s64 num, den; + + do { + seq = read_seqcount_begin(&p->sequence); + fprop_reflect_period_percpu(p, pl); + num = percpu_counter_read_positive(&pl->events); + den = percpu_counter_read_positive(&p->events); + } while (read_seqcount_retry(&p->sequence, seq)); + + /* + * Make fraction <= 1 and denominator > 0 even in presence of percpu + * counter errors + */ + if (den <= num) { + if (num) + den = num; + else + den = 1; + } + *denominator = den; + *numerator = num; +} + +/* + * Like __fprop_inc_percpu() except that event is counted only if the given + * type has fraction smaller than @max_frac/FPROP_FRAC_BASE + */ +void __fprop_inc_percpu_max(struct fprop_global *p, + struct fprop_local_percpu *pl, int max_frac) +{ + if (unlikely(max_frac < FPROP_FRAC_BASE)) { + unsigned long numerator, denominator; + + fprop_fraction_percpu(p, pl, &numerator, &denominator); + if (numerator > + (((u64)denominator) * max_frac) >> FPROP_FRAC_SHIFT) + return; + } else + fprop_reflect_period_percpu(p, pl); + __percpu_counter_add(&pl->events, 1, PROP_BATCH); + percpu_counter_add(&p->events, 1); +} -- cgit v1.2.3 From eb608e3a344b3af21300360fcf868f8b4e808a8e Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 24 May 2012 18:59:11 +0200 Subject: block: Convert BDI proportion calculations to flexible proportions Convert calculations of proportion of writeback each bdi does to new flexible proportion code. That allows us to use aging period of fixed wallclock time which gives better proportion estimates given the hugely varying throughput of different devices. Acked-by: Peter Zijlstra Signed-off-by: Jan Kara Signed-off-by: Fengguang Wu --- include/linux/backing-dev.h | 4 +- mm/backing-dev.c | 6 +-- mm/page-writeback.c | 103 +++++++++++++++++++++++++++----------------- 3 files changed, 69 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index b1038bd686ac..489de625cd25 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include @@ -89,7 +89,7 @@ struct backing_dev_info { unsigned long dirty_ratelimit; unsigned long balanced_dirty_ratelimit; - struct prop_local_percpu completions; + struct fprop_local_percpu completions; int dirty_exceeded; unsigned int min_ratio; diff --git a/mm/backing-dev.c b/mm/backing-dev.c index dd8e2aafb07e..3387aea11209 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -677,7 +677,7 @@ int bdi_init(struct backing_dev_info *bdi) bdi->min_ratio = 0; bdi->max_ratio = 100; - bdi->max_prop_frac = PROP_FRAC_BASE; + bdi->max_prop_frac = FPROP_FRAC_BASE; spin_lock_init(&bdi->wb_lock); INIT_LIST_HEAD(&bdi->bdi_list); INIT_LIST_HEAD(&bdi->work_list); @@ -700,7 +700,7 @@ int bdi_init(struct backing_dev_info *bdi) bdi->write_bandwidth = INIT_BW; bdi->avg_write_bandwidth = INIT_BW; - err = prop_local_init_percpu(&bdi->completions); + err = fprop_local_init_percpu(&bdi->completions); if (err) { err: @@ -744,7 +744,7 @@ void bdi_destroy(struct backing_dev_info *bdi) for (i = 0; i < NR_BDI_STAT_ITEMS; i++) percpu_counter_destroy(&bdi->bdi_stat[i]); - prop_local_destroy_percpu(&bdi->completions); + fprop_local_destroy_percpu(&bdi->completions); } EXPORT_SYMBOL(bdi_destroy); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 93d8d2f7108c..ec14419e53b5 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -34,6 +34,7 @@ #include #include /* __set_page_dirty_buffers */ #include +#include #include /* @@ -135,7 +136,20 @@ unsigned long global_dirty_limit; * measured in page writeback completions. * */ -static struct prop_descriptor vm_completions; +static struct fprop_global writeout_completions; + +static void writeout_period(unsigned long t); +/* Timer for aging of writeout_completions */ +static struct timer_list writeout_period_timer = + TIMER_DEFERRED_INITIALIZER(writeout_period, 0, 0); +static unsigned long writeout_period_time = 0; + +/* + * Length of period for aging writeout fractions of bdis. This is an + * arbitrarily chosen number. The longer the period, the slower fractions will + * reflect changes in current writeout rate. + */ +#define VM_COMPLETIONS_PERIOD_LEN (3*HZ) /* * Work out the current dirty-memory clamping and background writeout @@ -322,34 +336,6 @@ bool zone_dirty_ok(struct zone *zone) zone_page_state(zone, NR_WRITEBACK) <= limit; } -/* - * couple the period to the dirty_ratio: - * - * period/2 ~ roundup_pow_of_two(dirty limit) - */ -static int calc_period_shift(void) -{ - unsigned long dirty_total; - - if (vm_dirty_bytes) - dirty_total = vm_dirty_bytes / PAGE_SIZE; - else - dirty_total = (vm_dirty_ratio * global_dirtyable_memory()) / - 100; - return 2 + ilog2(dirty_total - 1); -} - -/* - * update the period when the dirty threshold changes. - */ -static void update_completion_period(void) -{ - int shift = calc_period_shift(); - prop_change_shift(&vm_completions, shift); - - writeback_set_ratelimit(); -} - int dirty_background_ratio_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) @@ -383,7 +369,7 @@ int dirty_ratio_handler(struct ctl_table *table, int write, ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); if (ret == 0 && write && vm_dirty_ratio != old_ratio) { - update_completion_period(); + writeback_set_ratelimit(); vm_dirty_bytes = 0; } return ret; @@ -398,12 +384,21 @@ int dirty_bytes_handler(struct ctl_table *table, int write, ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos); if (ret == 0 && write && vm_dirty_bytes != old_bytes) { - update_completion_period(); + writeback_set_ratelimit(); vm_dirty_ratio = 0; } return ret; } +static unsigned long wp_next_time(unsigned long cur_time) +{ + cur_time += VM_COMPLETIONS_PERIOD_LEN; + /* 0 has a special meaning... */ + if (!cur_time) + return 1; + return cur_time; +} + /* * Increment the BDI's writeout completion count and the global writeout * completion count. Called from test_clear_page_writeback(). @@ -411,8 +406,19 @@ int dirty_bytes_handler(struct ctl_table *table, int write, static inline void __bdi_writeout_inc(struct backing_dev_info *bdi) { __inc_bdi_stat(bdi, BDI_WRITTEN); - __prop_inc_percpu_max(&vm_completions, &bdi->completions, - bdi->max_prop_frac); + __fprop_inc_percpu_max(&writeout_completions, &bdi->completions, + bdi->max_prop_frac); + /* First event after period switching was turned off? */ + if (!unlikely(writeout_period_time)) { + /* + * We can race with other __bdi_writeout_inc calls here but + * it does not cause any harm since the resulting time when + * timer will fire and what is in writeout_period_time will be + * roughly the same. + */ + writeout_period_time = wp_next_time(jiffies); + mod_timer(&writeout_period_timer, writeout_period_time); + } } void bdi_writeout_inc(struct backing_dev_info *bdi) @@ -431,10 +437,32 @@ EXPORT_SYMBOL_GPL(bdi_writeout_inc); static void bdi_writeout_fraction(struct backing_dev_info *bdi, long *numerator, long *denominator) { - prop_fraction_percpu(&vm_completions, &bdi->completions, + fprop_fraction_percpu(&writeout_completions, &bdi->completions, numerator, denominator); } +/* + * On idle system, we can be called long after we scheduled because we use + * deferred timers so count with missed periods. + */ +static void writeout_period(unsigned long t) +{ + int miss_periods = (jiffies - writeout_period_time) / + VM_COMPLETIONS_PERIOD_LEN; + + if (fprop_new_period(&writeout_completions, miss_periods + 1)) { + writeout_period_time = wp_next_time(writeout_period_time + + miss_periods * VM_COMPLETIONS_PERIOD_LEN); + mod_timer(&writeout_period_timer, writeout_period_time); + } else { + /* + * Aging has zeroed all fractions. Stop wasting CPU on period + * updates. + */ + writeout_period_time = 0; + } +} + /* * bdi_min_ratio keeps the sum of the minimum dirty shares of all * registered backing devices, which, for obvious reasons, can not @@ -475,7 +503,7 @@ int bdi_set_max_ratio(struct backing_dev_info *bdi, unsigned max_ratio) ret = -EINVAL; } else { bdi->max_ratio = max_ratio; - bdi->max_prop_frac = (PROP_FRAC_BASE * max_ratio) / 100; + bdi->max_prop_frac = (FPROP_FRAC_BASE * max_ratio) / 100; } spin_unlock_bh(&bdi_lock); @@ -1606,13 +1634,10 @@ static struct notifier_block __cpuinitdata ratelimit_nb = { */ void __init page_writeback_init(void) { - int shift; - writeback_set_ratelimit(); register_cpu_notifier(&ratelimit_nb); - shift = calc_period_shift(); - prop_descriptor_init(&vm_completions, shift); + fprop_global_init(&writeout_completions); } /** -- cgit v1.2.3 From fbfe95a42e90b3dd079cc9019ba7d7700feee0f6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 8 Jun 2012 23:24:18 -0700 Subject: inet: Create and use rt{,6}_get_peer_create(). There's a lot of places that open-code rt{,6}_get_peer() only because they want to set 'create' to one. So add an rt{,6}_get_peer_create() for their sake. There were also a few spots open-coding plain rt{,6}_get_peer() and those are transformed here as well. Signed-off-by: David S. Miller --- include/net/ip6_route.h | 17 +++++++++++++---- include/net/route.h | 14 ++++++++++++-- net/ipv4/icmp.c | 5 ++--- net/ipv4/route.c | 35 +++++++++-------------------------- net/ipv4/tcp_ipv4.c | 4 +--- net/ipv6/icmp.c | 6 +++--- net/ipv6/ip6_output.c | 11 ++++------- net/ipv6/ndisc.c | 6 +++--- net/ipv6/route.c | 5 +---- net/ipv6/tcp_ipv6.c | 4 +--- 10 files changed, 49 insertions(+), 58 deletions(-) (limited to 'include') diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 37c1a1ed82c1..73d750288121 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -53,18 +53,27 @@ static inline unsigned int rt6_flags2srcprefs(int flags) return (flags >> 3) & 7; } -extern void rt6_bind_peer(struct rt6_info *rt, - int create); +extern void rt6_bind_peer(struct rt6_info *rt, int create); -static inline struct inet_peer *rt6_get_peer(struct rt6_info *rt) +static inline struct inet_peer *__rt6_get_peer(struct rt6_info *rt, int create) { if (rt->rt6i_peer) return rt->rt6i_peer; - rt6_bind_peer(rt, 0); + rt6_bind_peer(rt, create); return rt->rt6i_peer; } +static inline struct inet_peer *rt6_get_peer(struct rt6_info *rt) +{ + return __rt6_get_peer(rt, 0); +} + +static inline struct inet_peer *rt6_get_peer_create(struct rt6_info *rt) +{ + return __rt6_get_peer(rt, 1); +} + extern void ip6_route_input(struct sk_buff *skb); extern struct dst_entry * ip6_route_output(struct net *net, diff --git a/include/net/route.h b/include/net/route.h index ed2b78e2375d..433fc6c1d404 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -296,15 +296,25 @@ static inline struct rtable *ip_route_newports(struct flowi4 *fl4, struct rtable extern void rt_bind_peer(struct rtable *rt, __be32 daddr, int create); -static inline struct inet_peer *rt_get_peer(struct rtable *rt, __be32 daddr) +static inline struct inet_peer *__rt_get_peer(struct rtable *rt, __be32 daddr, int create) { if (rt->peer) return rt->peer; - rt_bind_peer(rt, daddr, 0); + rt_bind_peer(rt, daddr, create); return rt->peer; } +static inline struct inet_peer *rt_get_peer(struct rtable *rt, __be32 daddr) +{ + return __rt_get_peer(rt, daddr, 0); +} + +static inline struct inet_peer *rt_get_peer_create(struct rtable *rt, __be32 daddr) +{ + return __rt_get_peer(rt, daddr, 1); +} + static inline int inet_iif(const struct sk_buff *skb) { return skb_rtable(skb)->rt_iif; diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index c75efbdc71cb..0c78ef1e5dde 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -253,9 +253,8 @@ static inline bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt, /* Limit if icmp type is enabled in ratemask. */ if ((1 << type) & net->ipv4.sysctl_icmp_ratemask) { - if (!rt->peer) - rt_bind_peer(rt, fl4->daddr, 1); - rc = inet_peer_xrlim_allow(rt->peer, + struct inet_peer *peer = rt_get_peer_create(rt, fl4->daddr); + rc = inet_peer_xrlim_allow(peer, net->ipv4.sysctl_icmp_ratelimit); } out: diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 2c9f73f3b7cc..7a4d724765e6 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -162,10 +162,7 @@ static u32 *ipv4_cow_metrics(struct dst_entry *dst, unsigned long old) struct inet_peer *peer; u32 *p = NULL; - if (!rt->peer) - rt_bind_peer(rt, rt->rt_dst, 1); - - peer = rt->peer; + peer = rt_get_peer_create(rt, rt->rt_dst); if (peer) { u32 *old_p = __DST_METRICS_PTR(old); unsigned long prev, new; @@ -1364,14 +1361,13 @@ void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more) struct rtable *rt = (struct rtable *) dst; if (rt && !(rt->dst.flags & DST_NOPEER)) { - if (rt->peer == NULL) - rt_bind_peer(rt, rt->rt_dst, 1); + struct inet_peer *peer = rt_get_peer_create(rt, rt->rt_dst); /* If peer is attached to destination, it is never detached, so that we need not to grab a lock to dereference it. */ - if (rt->peer) { - iph->id = htons(inet_getid(rt->peer, more)); + if (peer) { + iph->id = htons(inet_getid(peer, more)); return; } } else if (!rt) @@ -1481,10 +1477,7 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, rt->rt_gateway != old_gw) continue; - if (!rt->peer) - rt_bind_peer(rt, rt->rt_dst, 1); - - peer = rt->peer; + peer = rt_get_peer_create(rt, rt->rt_dst); if (peer) { if (peer->redirect_learned.a4 != new_gw) { peer->redirect_learned.a4 = new_gw; @@ -1579,9 +1572,7 @@ void ip_rt_send_redirect(struct sk_buff *skb) log_martians = IN_DEV_LOG_MARTIANS(in_dev); rcu_read_unlock(); - if (!rt->peer) - rt_bind_peer(rt, rt->rt_dst, 1); - peer = rt->peer; + peer = rt_get_peer_create(rt, rt->rt_dst); if (!peer) { icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, rt->rt_gateway); return; @@ -1646,9 +1637,7 @@ static int ip_error(struct sk_buff *skb) break; } - if (!rt->peer) - rt_bind_peer(rt, rt->rt_dst, 1); - peer = rt->peer; + peer = rt_get_peer_create(rt, rt->rt_dst); send = true; if (peer) { @@ -1754,9 +1743,7 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu) dst_confirm(dst); - if (!rt->peer) - rt_bind_peer(rt, rt->rt_dst, 1); - peer = rt->peer; + peer = rt_get_peer_create(rt, rt->rt_dst); if (peer) { unsigned long pmtu_expires = ACCESS_ONCE(peer->pmtu_expires); @@ -1782,12 +1769,8 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu) static void ipv4_validate_peer(struct rtable *rt) { if (rt->rt_peer_genid != rt_peer_genid()) { - struct inet_peer *peer; - - if (!rt->peer) - rt_bind_peer(rt, rt->rt_dst, 0); + struct inet_peer *peer = rt_get_peer(rt, rt->rt_dst); - peer = rt->peer; if (peer) { check_peer_pmtu(&rt->dst, peer); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index f485b451f928..833e8d96a636 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1832,9 +1832,7 @@ struct inet_peer *tcp_v4_get_peer(struct sock *sk, bool *release_it) peer = inet_getpeer_v4(net, inet->inet_daddr, 1); *release_it = true; } else { - if (!rt->peer) - rt_bind_peer(rt, inet->inet_daddr, 1); - peer = rt->peer; + peer = rt_get_peer_create(rt, inet->inet_daddr); *release_it = false; } diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 091a2971c7b7..ed89bba745a1 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -188,14 +188,14 @@ static inline bool icmpv6_xrlim_allow(struct sock *sk, u8 type, } else { struct rt6_info *rt = (struct rt6_info *)dst; int tmo = net->ipv6.sysctl.icmpv6_time; + struct inet_peer *peer; /* Give more bandwidth to wider prefixes. */ if (rt->rt6i_dst.plen < 128) tmo >>= ((128 - rt->rt6i_dst.plen)>>5); - if (!rt->rt6i_peer) - rt6_bind_peer(rt, 1); - res = inet_peer_xrlim_allow(rt->rt6i_peer, tmo); + peer = rt6_get_peer_create(rt); + res = inet_peer_xrlim_allow(peer, tmo); } dst_release(dst); return res; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 17b8c67998bb..62fcf3e48aca 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -463,6 +463,7 @@ int ip6_forward(struct sk_buff *skb) */ if (skb->dev == dst->dev && opt->srcrt == 0 && !skb_sec_path(skb)) { struct in6_addr *target = NULL; + struct inet_peer *peer; struct rt6_info *rt; /* @@ -476,13 +477,12 @@ int ip6_forward(struct sk_buff *skb) else target = &hdr->daddr; - if (!rt->rt6i_peer) - rt6_bind_peer(rt, 1); + peer = rt6_get_peer_create(rt); /* Limit redirects both by destination (here) and by source (inside ndisc_send_redirect) */ - if (inet_peer_xrlim_allow(rt->rt6i_peer, 1*HZ)) + if (inet_peer_xrlim_allow(peer, 1*HZ)) ndisc_send_redirect(skb, target); } else { int addrtype = ipv6_addr_type(&hdr->saddr); @@ -602,11 +602,8 @@ void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt) int old, new; if (rt && !(rt->dst.flags & DST_NOPEER)) { - struct inet_peer *peer; + struct inet_peer *peer = rt6_get_peer_create(rt); - if (!rt->rt6i_peer) - rt6_bind_peer(rt, 1); - peer = rt->rt6i_peer; if (peer) { fhdr->identification = htonl(inet_getid(peer, 0)); return; diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 54f62d3b8dd6..69a6330dea91 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1472,6 +1472,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) struct net *net = dev_net(dev); struct sock *sk = net->ipv6.ndisc_sk; int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr); + struct inet_peer *peer; struct sk_buff *buff; struct icmp6hdr *icmph; struct in6_addr saddr_buf; @@ -1518,9 +1519,8 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) "Redirect: destination is not a neighbour\n"); goto release; } - if (!rt->rt6i_peer) - rt6_bind_peer(rt, 1); - if (!inet_peer_xrlim_allow(rt->rt6i_peer, 1*HZ)) + peer = rt6_get_peer_create(rt); + if (!inet_peer_xrlim_allow(peer, 1*HZ)) goto release; if (dev->addr_len) { diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 4eca0130cce7..8a986be4aeda 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -99,10 +99,7 @@ static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) if (!(rt->dst.flags & DST_HOST)) return NULL; - if (!rt->rt6i_peer) - rt6_bind_peer(rt, 1); - - peer = rt->rt6i_peer; + peer = rt6_get_peer_create(rt); if (peer) { u32 *old_p = __DST_METRICS_PTR(old); unsigned long prev, new; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 1a9cdd09f11c..218433cb9928 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1744,9 +1744,7 @@ static struct inet_peer *tcp_v6_get_peer(struct sock *sk, bool *release_it) peer = inet_getpeer_v6(net, &np->daddr, 1); *release_it = true; } else { - if (!rt->rt6i_peer) - rt6_bind_peer(rt, 1); - peer = rt->rt6i_peer; + peer = rt6_get_peer_create(rt); *release_it = false; } -- cgit v1.2.3 From 4670fd819e7f47392c7c6fc6168ea2857c66d163 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 9 Jun 2012 01:25:47 -0700 Subject: tcp: Get rid of inetpeer special cases. The get_peer method TCP uses is full of special cases that make no sense accommodating, and it also gets in the way of doing more reasonable things here. First of all, if the socket doesn't have a usable cached route, there is no sense in trying to optimize timewait recycling. Likewise for the case where we have IP options, such as SRR enabled, that make the IP header destination address (and thus the destination address of the route key) differ from that of the connection's destination address. Just return a NULL peer in these cases, and thus we're also able to get rid of the clumsy inetpeer release logic. Signed-off-by: David S. Miller --- include/net/inet_connection_sock.h | 2 +- include/net/tcp.h | 2 +- net/ipv4/tcp_ipv4.c | 21 ++++++++------------- net/ipv4/tcp_minisocks.c | 5 +---- net/ipv6/tcp_ipv6.c | 21 ++++++++------------- 5 files changed, 19 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 7d83f90f203f..e1b7734c456f 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -43,7 +43,7 @@ struct inet_connection_sock_af_ops { struct sock *(*syn_recv_sock)(struct sock *sk, struct sk_buff *skb, struct request_sock *req, struct dst_entry *dst); - struct inet_peer *(*get_peer)(struct sock *sk, bool *release_it); + struct inet_peer *(*get_peer)(struct sock *sk); u16 net_header_len; u16 net_frag_header_len; u16 sockaddr_len; diff --git a/include/net/tcp.h b/include/net/tcp.h index e79aa48d9fc1..424591866037 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -327,7 +327,7 @@ 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, bool *release_it); +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, diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 833e8d96a636..77f049d00dbb 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1820,23 +1820,18 @@ do_time_wait: goto discard_it; } -struct inet_peer *tcp_v4_get_peer(struct sock *sk, bool *release_it) +struct inet_peer *tcp_v4_get_peer(struct sock *sk) { struct rtable *rt = (struct rtable *) __sk_dst_get(sk); struct inet_sock *inet = inet_sk(sk); - struct net *net = sock_net(sk); - struct inet_peer *peer; - - if (!rt || - inet->cork.fl.u.ip4.daddr != inet->inet_daddr) { - peer = inet_getpeer_v4(net, inet->inet_daddr, 1); - *release_it = true; - } else { - peer = rt_get_peer_create(rt, inet->inet_daddr); - *release_it = false; - } - return peer; + /* If we don't have a valid cached route, or we're doing IP + * options which make the IPv4 header destination address + * different from our peer's, do not bother with this. + */ + if (!rt || inet->cork.fl.u.ip4.daddr != inet->inet_daddr) + return NULL; + return rt_get_peer_create(rt, inet->inet_daddr); } EXPORT_SYMBOL(tcp_v4_get_peer); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index b85d9fe7d663..fef9dbf3af00 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -60,9 +60,8 @@ static bool tcp_remember_stamp(struct sock *sk) const struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); struct inet_peer *peer; - bool release_it; - peer = icsk->icsk_af_ops->get_peer(sk, &release_it); + peer = icsk->icsk_af_ops->get_peer(sk); if (peer) { if ((s32)(peer->tcp_ts - tp->rx_opt.ts_recent) <= 0 || ((u32)get_seconds() - peer->tcp_ts_stamp > TCP_PAWS_MSL && @@ -70,8 +69,6 @@ static bool tcp_remember_stamp(struct sock *sk) peer->tcp_ts_stamp = (u32)tp->rx_opt.ts_recent_stamp; peer->tcp_ts = tp->rx_opt.ts_recent; } - if (release_it) - inet_putpeer(peer); return true; } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 218433cb9928..b5ecf37b61a6 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1732,23 +1732,18 @@ do_time_wait: goto discard_it; } -static struct inet_peer *tcp_v6_get_peer(struct sock *sk, bool *release_it) +static struct inet_peer *tcp_v6_get_peer(struct sock *sk) { struct rt6_info *rt = (struct rt6_info *) __sk_dst_get(sk); struct ipv6_pinfo *np = inet6_sk(sk); - struct net *net = sock_net(sk); - struct inet_peer *peer; - - if (!rt || - !ipv6_addr_equal(&np->daddr, &rt->rt6i_dst.addr)) { - peer = inet_getpeer_v6(net, &np->daddr, 1); - *release_it = true; - } else { - peer = rt6_get_peer_create(rt); - *release_it = false; - } - return peer; + /* If we don't have a valid cached route, or we're doing IP + * options which make the IPv6 header destination address + * different from our peer's, do not bother with this. + */ + if (!rt || !ipv6_addr_equal(&np->daddr, &rt->rt6i_dst.addr)) + return NULL; + return rt6_get_peer_create(rt); } static void *tcp_v6_tw_get_peer(struct sock *sk) -- cgit v1.2.3 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') 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 From c3426b47190d7c6785230c91a706fd42208b4120 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 9 Jun 2012 16:27:05 -0700 Subject: inet: Initialize per-netns inetpeer roots in net/ipv{4,6}/route.c Instead of net/ipv4/inetpeer.c Signed-off-by: David S. Miller --- include/net/inetpeer.h | 9 +++++++ net/ipv4/inetpeer.c | 64 +++++++++++++------------------------------------- net/ipv4/route.c | 25 ++++++++++++++++++++ net/ipv6/route.c | 34 ++++++++++++++++++++++++++- 4 files changed, 83 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index 20e67db69ac9..5a96c8edc526 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -65,6 +65,14 @@ struct inet_peer { atomic_t refcnt; }; +struct inet_peer_base { + struct inet_peer __rcu *root; + seqlock_t lock; + int total; +}; + +extern void inet_peer_base_init(struct inet_peer_base *); + void inet_initpeers(void) __init; #define INETPEER_METRICS_NEW (~(u32) 0) @@ -105,6 +113,7 @@ static inline struct inet_peer *inet_getpeer_v6(struct net *net, extern void inet_putpeer(struct inet_peer *p); extern bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout); +extern void __inetpeer_invalidate_tree(struct inet_peer_base *); extern void inetpeer_invalidate_tree(struct net *net, int family); /* diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index 1c8527349c86..d0d72f89b279 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -82,11 +82,13 @@ static const struct inet_peer peer_fake_node = { .avl_height = 0 }; -struct inet_peer_base { - struct inet_peer __rcu *root; - seqlock_t lock; - int total; -}; +void inet_peer_base_init(struct inet_peer_base *bp) +{ + bp->root = peer_avl_empty_rcu; + seqlock_init(&bp->lock); + bp->total = 0; +} +EXPORT_SYMBOL_GPL(inet_peer_base_init); #define PEER_MAXDEPTH 40 /* sufficient for about 2^27 nodes */ @@ -141,46 +143,6 @@ static void inetpeer_gc_worker(struct work_struct *work) schedule_delayed_work(&gc_work, gc_delay); } -static int __net_init inetpeer_net_init(struct net *net) -{ - net->ipv4.peers = kzalloc(sizeof(struct inet_peer_base), - GFP_KERNEL); - if (net->ipv4.peers == NULL) - return -ENOMEM; - - net->ipv4.peers->root = peer_avl_empty_rcu; - seqlock_init(&net->ipv4.peers->lock); - - net->ipv6.peers = kzalloc(sizeof(struct inet_peer_base), - GFP_KERNEL); - if (net->ipv6.peers == NULL) - goto out_ipv6; - - net->ipv6.peers->root = peer_avl_empty_rcu; - seqlock_init(&net->ipv6.peers->lock); - - return 0; -out_ipv6: - kfree(net->ipv4.peers); - return -ENOMEM; -} - -static void __net_exit inetpeer_net_exit(struct net *net) -{ - inetpeer_invalidate_tree(net, AF_INET); - kfree(net->ipv4.peers); - net->ipv4.peers = NULL; - - inetpeer_invalidate_tree(net, AF_INET6); - kfree(net->ipv6.peers); - net->ipv6.peers = NULL; -} - -static struct pernet_operations inetpeer_ops = { - .init = inetpeer_net_init, - .exit = inetpeer_net_exit, -}; - /* Called from ip_output.c:ip_init */ void __init inet_initpeers(void) { @@ -205,7 +167,6 @@ void __init inet_initpeers(void) NULL); INIT_DELAYED_WORK_DEFERRABLE(&gc_work, inetpeer_gc_worker); - register_pernet_subsys(&inetpeer_ops); } static int addr_compare(const struct inetpeer_addr *a, @@ -603,10 +564,9 @@ static void inetpeer_inval_rcu(struct rcu_head *head) schedule_delayed_work(&gc_work, gc_delay); } -void inetpeer_invalidate_tree(struct net *net, int family) +void __inetpeer_invalidate_tree(struct inet_peer_base *base) { struct inet_peer *old, *new, *prev; - struct inet_peer_base *base = family_to_base(net, family); write_seqlock_bh(&base->lock); @@ -625,4 +585,12 @@ void inetpeer_invalidate_tree(struct net *net, int family) out: write_sequnlock_bh(&base->lock); } +EXPORT_SYMBOL(__inetpeer_invalidate_tree); + +void inetpeer_invalidate_tree(struct net *net, int family) +{ + struct inet_peer_base *base = family_to_base(net, family); + + __inetpeer_invalidate_tree(base); +} EXPORT_SYMBOL(inetpeer_invalidate_tree); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 7a4d724765e6..4cd35c3904d4 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -3385,6 +3385,30 @@ static __net_initdata struct pernet_operations rt_genid_ops = { .init = rt_genid_init, }; +static int __net_init ipv4_inetpeer_init(struct net *net) +{ + struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); + + if (!bp) + return -ENOMEM; + inet_peer_base_init(bp); + net->ipv4.peers = bp; + return 0; +} + +static void __net_exit ipv4_inetpeer_exit(struct net *net) +{ + struct inet_peer_base *bp = net->ipv4.peers; + + net->ipv4.peers = NULL; + __inetpeer_invalidate_tree(bp); + kfree(bp); +} + +static __net_initdata struct pernet_operations ipv4_inetpeer_ops = { + .init = ipv4_inetpeer_init, + .exit = ipv4_inetpeer_exit, +}; #ifdef CONFIG_IP_ROUTE_CLASSID struct ip_rt_acct __percpu *ip_rt_acct __read_mostly; @@ -3465,6 +3489,7 @@ int __init ip_rt_init(void) register_pernet_subsys(&sysctl_route_ops); #endif register_pernet_subsys(&rt_genid_ops); + register_pernet_subsys(&ipv4_inetpeer_ops); return rc; } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 8a986be4aeda..3e1e4e0da096 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2996,6 +2996,31 @@ static struct pernet_operations ip6_route_net_ops = { .exit = ip6_route_net_exit, }; +static int __net_init ipv6_inetpeer_init(struct net *net) +{ + struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); + + if (!bp) + return -ENOMEM; + inet_peer_base_init(bp); + net->ipv6.peers = bp; + return 0; +} + +static void __net_exit ipv6_inetpeer_exit(struct net *net) +{ + struct inet_peer_base *bp = net->ipv6.peers; + + net->ipv6.peers = NULL; + __inetpeer_invalidate_tree(bp); + kfree(bp); +} + +static __net_initdata struct pernet_operations ipv6_inetpeer_ops = { + .init = ipv6_inetpeer_init, + .exit = ipv6_inetpeer_exit, +}; + static struct notifier_block ip6_route_dev_notifier = { .notifier_call = ip6_route_dev_notify, .priority = 0, @@ -3020,6 +3045,10 @@ int __init ip6_route_init(void) if (ret) goto out_dst_entries; + ret = register_pernet_subsys(&ipv6_inetpeer_ops); + if (ret) + goto out_register_subsys; + ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops_template.kmem_cachep; /* Registering of the loopback is done before this portion of code, @@ -3035,7 +3064,7 @@ int __init ip6_route_init(void) #endif ret = fib6_init(); if (ret) - goto out_register_subsys; + goto out_register_inetpeer; ret = xfrm6_init(); if (ret) @@ -3064,6 +3093,8 @@ xfrm6_init: xfrm6_fini(); out_fib6_init: fib6_gc_cleanup(); +out_register_inetpeer: + unregister_pernet_subsys(&ipv6_inetpeer_ops); out_register_subsys: unregister_pernet_subsys(&ip6_route_net_ops); out_dst_entries: @@ -3079,6 +3110,7 @@ void ip6_route_cleanup(void) fib6_rules_cleanup(); xfrm6_fini(); fib6_gc_cleanup(); + unregister_pernet_subsys(&ipv6_inetpeer_ops); unregister_pernet_subsys(&ip6_route_net_ops); dst_entries_destroy(&ip6_dst_blackhole_ops); kmem_cache_destroy(ip6_dst_ops_template.kmem_cachep); -- cgit v1.2.3 From 56a6b248eb345c1948ee60bf426de1ff7dd81509 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 9 Jun 2012 16:32:41 -0700 Subject: inet: Consolidate inetpeer_invalidate_tree() interfaces. We only need one interface for this operation, since we always know which inetpeer root we want to flush. Signed-off-by: David S. Miller --- include/net/inetpeer.h | 3 +-- net/ipv4/inetpeer.c | 10 +--------- net/ipv4/route.c | 4 ++-- net/ipv6/route.c | 2 +- 4 files changed, 5 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index 5a96c8edc526..733edc641b76 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -113,8 +113,7 @@ static inline struct inet_peer *inet_getpeer_v6(struct net *net, extern void inet_putpeer(struct inet_peer *p); extern bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout); -extern void __inetpeer_invalidate_tree(struct inet_peer_base *); -extern void inetpeer_invalidate_tree(struct net *net, int family); +extern void inetpeer_invalidate_tree(struct inet_peer_base *); /* * temporary check to make sure we dont access rid, ip_id_count, tcp_ts, diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index d0d72f89b279..9d89a381f0e1 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -564,7 +564,7 @@ static void inetpeer_inval_rcu(struct rcu_head *head) schedule_delayed_work(&gc_work, gc_delay); } -void __inetpeer_invalidate_tree(struct inet_peer_base *base) +void inetpeer_invalidate_tree(struct inet_peer_base *base) { struct inet_peer *old, *new, *prev; @@ -585,12 +585,4 @@ void __inetpeer_invalidate_tree(struct inet_peer_base *base) out: write_sequnlock_bh(&base->lock); } -EXPORT_SYMBOL(__inetpeer_invalidate_tree); - -void inetpeer_invalidate_tree(struct net *net, int family) -{ - struct inet_peer_base *base = family_to_base(net, family); - - __inetpeer_invalidate_tree(base); -} EXPORT_SYMBOL(inetpeer_invalidate_tree); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 4cd35c3904d4..cf78343940de 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -935,7 +935,7 @@ static void rt_cache_invalidate(struct net *net) get_random_bytes(&shuffle, sizeof(shuffle)); atomic_add(shuffle + 1U, &net->ipv4.rt_genid); - inetpeer_invalidate_tree(net, AF_INET); + inetpeer_invalidate_tree(net->ipv4.peers); } /* @@ -3401,7 +3401,7 @@ static void __net_exit ipv4_inetpeer_exit(struct net *net) struct inet_peer_base *bp = net->ipv4.peers; net->ipv4.peers = NULL; - __inetpeer_invalidate_tree(bp); + inetpeer_invalidate_tree(bp); kfree(bp); } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 3e1e4e0da096..7f346d7492d2 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3012,7 +3012,7 @@ static void __net_exit ipv6_inetpeer_exit(struct net *net) struct inet_peer_base *bp = net->ipv6.peers; net->ipv6.peers = NULL; - __inetpeer_invalidate_tree(bp); + inetpeer_invalidate_tree(bp); kfree(bp); } -- cgit v1.2.3 From c0efc887dcadbdbfe171f028acfab9c7c00e9dde Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 9 Jun 2012 19:12:36 -0700 Subject: inet: Pass inetpeer root into inet_getpeer*() interfaces. Otherwise we reference potentially non-existing members when ipv6 is disabled. Signed-off-by: David S. Miller --- include/net/inetpeer.h | 10 +++++----- net/ipv4/inetpeer.c | 9 +-------- net/ipv4/ip_fragment.c | 2 +- net/ipv4/route.c | 6 +++--- net/ipv6/route.c | 2 +- 5 files changed, 11 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index 733edc641b76..b84b32fd5df1 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -83,11 +83,11 @@ static inline bool inet_metrics_new(const struct inet_peer *p) } /* can be called with or without local BH being disabled */ -struct inet_peer *inet_getpeer(struct net *net, +struct inet_peer *inet_getpeer(struct inet_peer_base *base, const struct inetpeer_addr *daddr, int create); -static inline struct inet_peer *inet_getpeer_v4(struct net *net, +static inline struct inet_peer *inet_getpeer_v4(struct inet_peer_base *base, __be32 v4daddr, int create) { @@ -95,10 +95,10 @@ static inline struct inet_peer *inet_getpeer_v4(struct net *net, daddr.addr.a4 = v4daddr; daddr.family = AF_INET; - return inet_getpeer(net, &daddr, create); + return inet_getpeer(base, &daddr, create); } -static inline struct inet_peer *inet_getpeer_v6(struct net *net, +static inline struct inet_peer *inet_getpeer_v6(struct inet_peer_base *base, const struct in6_addr *v6daddr, int create) { @@ -106,7 +106,7 @@ static inline struct inet_peer *inet_getpeer_v6(struct net *net, *(struct in6_addr *)daddr.addr.a6 = *v6daddr; daddr.family = AF_INET6; - return inet_getpeer(net, &daddr, create); + return inet_getpeer(base, &daddr, create); } /* can be called from BH context or outside */ diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index 9d89a381f0e1..e4cba56a5349 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -391,12 +391,6 @@ static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base, call_rcu(&p->rcu, inetpeer_free_rcu); } -static struct inet_peer_base *family_to_base(struct net *net, - int family) -{ - return family == AF_INET ? net->ipv4.peers : net->ipv6.peers; -} - /* perform garbage collect on all items stacked during a lookup */ static int inet_peer_gc(struct inet_peer_base *base, struct inet_peer __rcu **stack[PEER_MAXDEPTH], @@ -434,12 +428,11 @@ static int inet_peer_gc(struct inet_peer_base *base, return cnt; } -struct inet_peer *inet_getpeer(struct net *net, +struct inet_peer *inet_getpeer(struct inet_peer_base *base, const struct inetpeer_addr *daddr, int create) { struct inet_peer __rcu **stack[PEER_MAXDEPTH], ***stackptr; - struct inet_peer_base *base = family_to_base(net, daddr->family); struct inet_peer *p; unsigned int sequence; int invalidated, gccnt = 0; diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 22c6bab9717a..8d07c973409c 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -184,7 +184,7 @@ static void ip4_frag_init(struct inet_frag_queue *q, void *a) qp->daddr = arg->iph->daddr; qp->user = arg->user; qp->peer = sysctl_ipfrag_max_dist ? - inet_getpeer_v4(net, arg->iph->saddr, 1) : NULL; + inet_getpeer_v4(net->ipv4.peers, arg->iph->saddr, 1) : NULL; } static __inline__ void ip4_frag_free(struct inet_frag_queue *q) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index cf78343940de..2aa663a6ae9e 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1328,7 +1328,7 @@ void rt_bind_peer(struct rtable *rt, __be32 daddr, int create) struct net *net = dev_net(rt->dst.dev); struct inet_peer *peer; - peer = inet_getpeer_v4(net, daddr, create); + peer = inet_getpeer_v4(net->ipv4.peers, daddr, create); if (peer && cmpxchg(&rt->peer, NULL, peer) != NULL) inet_putpeer(peer); @@ -1684,7 +1684,7 @@ unsigned short ip_rt_frag_needed(struct net *net, const struct iphdr *iph, unsigned short est_mtu = 0; struct inet_peer *peer; - peer = inet_getpeer_v4(net, iph->daddr, 1); + peer = inet_getpeer_v4(net->ipv4.peers, iph->daddr, 1); if (peer) { unsigned short mtu = new_mtu; @@ -1929,7 +1929,7 @@ static void rt_init_metrics(struct rtable *rt, const struct flowi4 *fl4, if (fl4 && (fl4->flowi4_flags & FLOWI_FLAG_PRECOW_METRICS)) create = 1; - rt->peer = peer = inet_getpeer_v4(net, rt->rt_dst, create); + rt->peer = peer = inet_getpeer_v4(net->ipv4.peers, rt->rt_dst, create); if (peer) { rt->rt_peer_genid = rt_peer_genid(); if (inet_metrics_new(peer)) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 9586c27e069c..8fc41d502bbd 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -306,7 +306,7 @@ void rt6_bind_peer(struct rt6_info *rt, int create) struct net *net = dev_net(rt->dst.dev); struct inet_peer *peer; - peer = inet_getpeer_v6(net, &rt->rt6i_dst.addr, create); + peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, create); if (peer && cmpxchg(&rt->rt6i_peer, NULL, peer) != NULL) inet_putpeer(peer); else -- cgit v1.2.3 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') 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 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') 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 From 3ddd53f392c4f8e19e1110d1bdef770008b128b8 Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Mon, 11 Jun 2012 11:59:10 +0800 Subject: cfg80211: add missing kernel-doc for mesh configuration structure Add the missing kernel-doc for mesh configuration parameters as pointed out by Johannes Berg. Signed-off-by: Chun-Yeow Yeoh Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 59 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7319f25250b6..a129ee2f5483 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -790,26 +790,69 @@ struct bss_parameters { int ht_opmode; }; -/* +/** * struct mesh_config - 802.11s mesh configuration * * These parameters can be changed while the mesh is active. + * + * @dot11MeshRetryTimeout: the initial retry timeout in millisecond units used + * by the Mesh Peering Open message + * @dot11MeshConfirmTimeout: the initial retry timeout in millisecond units + * used by the Mesh Peering Open message + * @dot11MeshHoldingTimeout: the confirm timeout in millisecond units used by + * the mesh peering management to close a mesh peering + * @dot11MeshMaxPeerLinks: the maximum number of peer links allowed on this + * mesh interface + * @dot11MeshMaxRetries: the maximum number of peer link open retries that can + * be sent to establish a new peer link instance in a mesh + * @dot11MeshTTL: the value of TTL field set at a source mesh STA + * @element_ttl: the value of TTL field set at a mesh STA for path selection + * elements + * @auto_open_plinks: whether we should automatically open peer links when we + * detect compatible mesh peers + * @dot11MeshNbrOffsetMaxNeighbor: the maximum number of neighbors to + * synchronize to for 11s default synchronization method + * @dot11MeshHWMPmaxPREQretries: the number of action frames containing a PREQ + * that an originator mesh STA can send to a particular path target + * @path_refresh_time: how frequently to refresh mesh paths in milliseconds + * @min_discovery_timeout: the minimum length of time to wait until giving up on + * a path discovery in milliseconds + * @dot11MeshHWMPactivePathTimeout: the time (in TUs) for which mesh STAs + * receiving a PREQ shall consider the forwarding information from the + * root to be valid. (TU = time unit) + * @dot11MeshHWMPpreqMinInterval: the minimum interval of time (in TUs) during + * which a mesh STA can send only one action frame containing a PREQ + * element + * @dot11MeshHWMPperrMinInterval: the minimum interval of time (in TUs) during + * which a mesh STA can send only one Action frame containing a PERR + * element + * @dot11MeshHWMPnetDiameterTraversalTime: the interval of time (in TUs) that + * it takes for an HWMP information element to propagate across the mesh + * @dot11MeshHWMPRootMode: the configuration of a mesh STA as root mesh STA + * @dot11MeshHWMPRannInterval: the interval of time (in TUs) between root + * announcements are transmitted + * @dot11MeshGateAnnouncementProtocol: whether to advertise that this mesh + * station has access to a broader network beyond the MBSS. (This is + * missnamed in draft 12.0: dot11MeshGateAnnouncementProtocol set to true + * only means that the station will announce others it's a mesh gate, but + * not necessarily using the gate announcement protocol. Still keeping the + * same nomenclature to be in sync with the spec) + * @dot11MeshForwarding: whether the Mesh STA is forwarding or non-forwarding + * entity (default is TRUE - forwarding entity) + * @rssi_threshold: the threshold for average signal strength of candidate + * station to establish a peer link + * @ht_opmode: mesh HT protection mode */ struct mesh_config { - /* Timeouts in ms */ - /* Mesh plink management parameters */ u16 dot11MeshRetryTimeout; u16 dot11MeshConfirmTimeout; u16 dot11MeshHoldingTimeout; u16 dot11MeshMaxPeerLinks; u8 dot11MeshMaxRetries; u8 dot11MeshTTL; - /* ttl used in path selection information elements */ u8 element_ttl; bool auto_open_plinks; - /* neighbor offset synchronization */ u32 dot11MeshNbrOffsetMaxNeighbor; - /* HWMP parameters */ u8 dot11MeshHWMPmaxPREQretries; u32 path_refresh_time; u16 min_discovery_timeout; @@ -819,10 +862,6 @@ struct mesh_config { u16 dot11MeshHWMPnetDiameterTraversalTime; u8 dot11MeshHWMPRootMode; u16 dot11MeshHWMPRannInterval; - /* This is missnamed in draft 12.0: dot11MeshGateAnnouncementProtocol - * set to true only means that the station will announce others it's a - * mesh gate, but not necessarily using the gate announcement protocol. - * Still keeping the same nomenclature to be in sync with the spec. */ bool dot11MeshGateAnnouncementProtocol; bool dot11MeshForwarding; s32 rssi_threshold; -- cgit v1.2.3 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') 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 From 97bab73f987e2781129cd6f4b6379bf44d808cc6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 9 Jun 2012 22:36:36 -0700 Subject: inet: Hide route peer accesses behind helpers. We encode the pointer(s) into an unsigned long with one state bit. The state bit is used so we can store the inetpeer tree root to use when resolving the peer later. Later the peer roots will be per-FIB table, and this change works to facilitate that. Signed-off-by: David S. Miller --- include/net/inetpeer.h | 54 +++++++++++++++++++++++++++++++++++++++++++++++ include/net/ip6_fib.h | 32 +++++++++++++++++++++++++++- include/net/ip6_route.h | 6 +++--- include/net/route.h | 42 +++++++++++++++++++++++++++++++++---- net/ipv4/route.c | 56 +++++++++++++++++++++++++++++-------------------- net/ipv4/xfrm4_policy.c | 10 ++++----- net/ipv6/route.c | 42 +++++++++++++++++++++---------------- net/ipv6/xfrm6_policy.c | 10 ++++----- 8 files changed, 193 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index b84b32fd5df1..d432489e7109 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -71,6 +71,60 @@ struct inet_peer_base { int total; }; +#define INETPEER_BASE_BIT 0x1UL + +static inline struct inet_peer *inetpeer_ptr(unsigned long val) +{ + BUG_ON(val & INETPEER_BASE_BIT); + return (struct inet_peer *) val; +} + +static inline struct inet_peer_base *inetpeer_base_ptr(unsigned long val) +{ + if (!(val & INETPEER_BASE_BIT)) + return NULL; + val &= ~INETPEER_BASE_BIT; + return (struct inet_peer_base *) val; +} + +static inline bool inetpeer_ptr_is_peer(unsigned long val) +{ + return !(val & INETPEER_BASE_BIT); +} + +static inline void __inetpeer_ptr_set_peer(unsigned long *val, struct inet_peer *peer) +{ + /* This implicitly clears INETPEER_BASE_BIT */ + *val = (unsigned long) peer; +} + +static inline bool inetpeer_ptr_set_peer(unsigned long *ptr, struct inet_peer *peer) +{ + unsigned long val = (unsigned long) peer; + unsigned long orig = *ptr; + + if (!(orig & INETPEER_BASE_BIT) || !val || + cmpxchg(ptr, orig, val) != orig) + return false; + return true; +} + +static inline void inetpeer_init_ptr(unsigned long *ptr, struct inet_peer_base *base) +{ + *ptr = (unsigned long) base | INETPEER_BASE_BIT; +} + +static inline void inetpeer_transfer_peer(unsigned long *to, unsigned long *from) +{ + unsigned long val = *from; + + *to = val; + if (inetpeer_ptr_is_peer(val)) { + struct inet_peer *peer = inetpeer_ptr(val); + atomic_inc(&peer->refcnt); + } +} + extern void inet_peer_base_init(struct inet_peer_base *); void inet_initpeers(void) __init; diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 0ae759a6c76e..3ac5f155c690 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -107,7 +107,7 @@ struct rt6_info { u32 rt6i_peer_genid; struct inet6_dev *rt6i_idev; - struct inet_peer *rt6i_peer; + unsigned long _rt6i_peer; #ifdef CONFIG_XFRM u32 rt6i_flow_cache_genid; @@ -118,6 +118,36 @@ struct rt6_info { u8 rt6i_protocol; }; +static inline struct inet_peer *rt6_peer_ptr(struct rt6_info *rt) +{ + return inetpeer_ptr(rt->_rt6i_peer); +} + +static inline bool rt6_has_peer(struct rt6_info *rt) +{ + return inetpeer_ptr_is_peer(rt->_rt6i_peer); +} + +static inline void __rt6_set_peer(struct rt6_info *rt, struct inet_peer *peer) +{ + __inetpeer_ptr_set_peer(&rt->_rt6i_peer, peer); +} + +static inline bool rt6_set_peer(struct rt6_info *rt, struct inet_peer *peer) +{ + return inetpeer_ptr_set_peer(&rt->_rt6i_peer, peer); +} + +static inline void rt6_init_peer(struct rt6_info *rt, struct inet_peer_base *base) +{ + inetpeer_init_ptr(&rt->_rt6i_peer, base); +} + +static inline void rt6_transfer_peer(struct rt6_info *rt, struct rt6_info *ort) +{ + inetpeer_transfer_peer(&rt->_rt6i_peer, &ort->_rt6i_peer); +} + static inline struct inet6_dev *ip6_dst_idev(struct dst_entry *dst) { return ((struct rt6_info *)dst)->rt6i_idev; diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 73d750288121..f88a85cf31c3 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -57,11 +57,11 @@ extern void rt6_bind_peer(struct rt6_info *rt, int create); static inline struct inet_peer *__rt6_get_peer(struct rt6_info *rt, int create) { - if (rt->rt6i_peer) - return rt->rt6i_peer; + if (rt6_has_peer(rt)) + return rt6_peer_ptr(rt); rt6_bind_peer(rt, create); - return rt->rt6i_peer; + return rt6_peer_ptr(rt); } static inline struct inet_peer *rt6_get_peer(struct rt6_info *rt) diff --git a/include/net/route.h b/include/net/route.h index 433fc6c1d404..6340c37677fc 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -67,10 +67,44 @@ struct rtable { /* Miscellaneous cached information */ __be32 rt_spec_dst; /* RFC1122 specific destination */ u32 rt_peer_genid; - struct inet_peer *peer; /* long-living peer info */ + unsigned long _peer; /* long-living peer info */ struct fib_info *fi; /* for client ref to shared metrics */ }; +static inline struct inet_peer *rt_peer_ptr(struct rtable *rt) +{ + return inetpeer_ptr(rt->_peer); +} + +static inline bool rt_has_peer(struct rtable *rt) +{ + return inetpeer_ptr_is_peer(rt->_peer); +} + +static inline void __rt_set_peer(struct rtable *rt, struct inet_peer *peer) +{ + __inetpeer_ptr_set_peer(&rt->_peer, peer); +} + +static inline bool rt_set_peer(struct rtable *rt, struct inet_peer *peer) +{ + return inetpeer_ptr_set_peer(&rt->_peer, peer); +} + +static inline void rt_init_peer(struct rtable *rt, struct inet_peer_base *base) +{ + inetpeer_init_ptr(&rt->_peer, base); +} + +static inline void rt_transfer_peer(struct rtable *rt, struct rtable *ort) +{ + rt->_peer = ort->_peer; + if (rt_has_peer(ort)) { + struct inet_peer *peer = rt_peer_ptr(ort); + atomic_inc(&peer->refcnt); + } +} + static inline bool rt_is_input_route(const struct rtable *rt) { return rt->rt_route_iif != 0; @@ -298,11 +332,11 @@ extern void rt_bind_peer(struct rtable *rt, __be32 daddr, int create); static inline struct inet_peer *__rt_get_peer(struct rtable *rt, __be32 daddr, int create) { - if (rt->peer) - return rt->peer; + if (rt_has_peer(rt)) + return rt_peer_ptr(rt); rt_bind_peer(rt, daddr, create); - return rt->peer; + return rt_peer_ptr(rt); } static inline struct inet_peer *rt_get_peer(struct rtable *rt, __be32 daddr) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 2aa663a6ae9e..03e5b614370e 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -677,7 +677,7 @@ static inline int rt_fast_clean(struct rtable *rth) static inline int rt_valuable(struct rtable *rth) { return (rth->rt_flags & (RTCF_REDIRECTED | RTCF_NOTIFY)) || - (rth->peer && rth->peer->pmtu_expires); + (rt_has_peer(rth) && rt_peer_ptr(rth)->pmtu_expires); } static int rt_may_expire(struct rtable *rth, unsigned long tmo1, unsigned long tmo2) @@ -1325,12 +1325,16 @@ static u32 rt_peer_genid(void) void rt_bind_peer(struct rtable *rt, __be32 daddr, int create) { - struct net *net = dev_net(rt->dst.dev); + struct inet_peer_base *base; struct inet_peer *peer; - peer = inet_getpeer_v4(net->ipv4.peers, daddr, create); + base = inetpeer_base_ptr(rt->_peer); + if (!base) + return; + + peer = inet_getpeer_v4(base, daddr, create); - if (peer && cmpxchg(&rt->peer, NULL, peer) != NULL) + if (!rt_set_peer(rt, peer)) inet_putpeer(peer); else rt->rt_peer_genid = rt_peer_genid(); @@ -1533,8 +1537,10 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst) rt_genid(dev_net(dst->dev))); rt_del(hash, rt); ret = NULL; - } else if (rt->peer && peer_pmtu_expired(rt->peer)) { - dst_metric_set(dst, RTAX_MTU, rt->peer->pmtu_orig); + } else if (rt_has_peer(rt)) { + struct inet_peer *peer = rt_peer_ptr(rt); + if (peer_pmtu_expired(peer)) + dst_metric_set(dst, RTAX_MTU, peer->pmtu_orig); } } return ret; @@ -1796,14 +1802,13 @@ static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) static void ipv4_dst_destroy(struct dst_entry *dst) { struct rtable *rt = (struct rtable *) dst; - struct inet_peer *peer = rt->peer; if (rt->fi) { fib_info_put(rt->fi); rt->fi = NULL; } - if (peer) { - rt->peer = NULL; + if (rt_has_peer(rt)) { + struct inet_peer *peer = rt_peer_ptr(rt); inet_putpeer(peer); } } @@ -1816,8 +1821,11 @@ static void ipv4_link_failure(struct sk_buff *skb) icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0); rt = skb_rtable(skb); - if (rt && rt->peer && peer_pmtu_cleaned(rt->peer)) - dst_metric_set(&rt->dst, RTAX_MTU, rt->peer->pmtu_orig); + if (rt && rt_has_peer(rt)) { + struct inet_peer *peer = rt_peer_ptr(rt); + if (peer_pmtu_cleaned(peer)) + dst_metric_set(&rt->dst, RTAX_MTU, peer->pmtu_orig); + } } static int ip_rt_bug(struct sk_buff *skb) @@ -1919,7 +1927,7 @@ static unsigned int ipv4_mtu(const struct dst_entry *dst) static void rt_init_metrics(struct rtable *rt, const struct flowi4 *fl4, struct fib_info *fi) { - struct net *net = dev_net(rt->dst.dev); + struct inet_peer_base *base; struct inet_peer *peer; int create = 0; @@ -1929,8 +1937,12 @@ static void rt_init_metrics(struct rtable *rt, const struct flowi4 *fl4, if (fl4 && (fl4->flowi4_flags & FLOWI_FLAG_PRECOW_METRICS)) create = 1; - rt->peer = peer = inet_getpeer_v4(net->ipv4.peers, rt->rt_dst, create); + base = inetpeer_base_ptr(rt->_peer); + BUG_ON(!base); + + peer = inet_getpeer_v4(base, rt->rt_dst, create); if (peer) { + __rt_set_peer(rt, peer); rt->rt_peer_genid = rt_peer_genid(); if (inet_metrics_new(peer)) memcpy(peer->metrics, fi->fib_metrics, @@ -2046,7 +2058,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; rth->rt_peer_genid = 0; - rth->peer = NULL; + rt_init_peer(rth, dev_net(dev)->ipv4.peers); rth->fi = NULL; if (our) { rth->dst.input= ip_local_deliver; @@ -2174,7 +2186,7 @@ static int __mkroute_input(struct sk_buff *skb, rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; rth->rt_peer_genid = 0; - rth->peer = NULL; + rt_init_peer(rth, dev_net(rth->dst.dev)->ipv4.peers); rth->fi = NULL; rth->dst.input = ip_forward; @@ -2357,7 +2369,7 @@ local_input: rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; rth->rt_peer_genid = 0; - rth->peer = NULL; + rt_init_peer(rth, net->ipv4.peers); rth->fi = NULL; if (res.type == RTN_UNREACHABLE) { rth->dst.input= ip_error; @@ -2561,7 +2573,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res, rth->rt_gateway = fl4->daddr; rth->rt_spec_dst= fl4->saddr; rth->rt_peer_genid = 0; - rth->peer = NULL; + rt_init_peer(rth, dev_net(dev_out)->ipv4.peers); rth->fi = NULL; RT_CACHE_STAT_INC(out_slow_tot); @@ -2898,9 +2910,7 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or rt->rt_src = ort->rt_src; rt->rt_gateway = ort->rt_gateway; rt->rt_spec_dst = ort->rt_spec_dst; - rt->peer = ort->peer; - if (rt->peer) - atomic_inc(&rt->peer->refcnt); + rt_transfer_peer(rt, ort); rt->fi = ort->fi; if (rt->fi) atomic_inc(&rt->fi->fib_clntref); @@ -2938,7 +2948,6 @@ static int rt_fill_info(struct net *net, struct rtmsg *r; struct nlmsghdr *nlh; unsigned long expires = 0; - const struct inet_peer *peer = rt->peer; u32 id = 0, ts = 0, tsage = 0, error; nlh = nlmsg_put(skb, pid, seq, event, sizeof(*r), flags); @@ -2994,8 +3003,9 @@ static int rt_fill_info(struct net *net, goto nla_put_failure; error = rt->dst.error; - if (peer) { - inet_peer_refcheck(rt->peer); + if (rt_has_peer(rt)) { + const struct inet_peer *peer = rt_peer_ptr(rt); + inet_peer_refcheck(peer); id = atomic_read(&peer->ip_id_count) & 0xffff; if (peer->tcp_ts_stamp) { ts = peer->tcp_ts; diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 0d3426cb5c4f..8855d8268552 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -90,9 +90,7 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, xdst->u.dst.dev = dev; dev_hold(dev); - xdst->u.rt.peer = rt->peer; - if (rt->peer) - atomic_inc(&rt->peer->refcnt); + rt_transfer_peer(&xdst->u.rt, rt); /* Sheit... I remember I did this right. Apparently, * it was magically lost, so this code needs audit */ @@ -212,8 +210,10 @@ static void xfrm4_dst_destroy(struct dst_entry *dst) dst_destroy_metrics_generic(dst); - if (likely(xdst->u.rt.peer)) - inet_putpeer(xdst->u.rt.peer); + if (rt_has_peer(&xdst->u.rt)) { + struct inet_peer *peer = rt_peer_ptr(&xdst->u.rt); + inet_putpeer(peer); + } xfrm_dst_destroy(xdst); } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 8fc41d502bbd..17a9b8687f29 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -258,16 +258,18 @@ static struct rt6_info ip6_blk_hole_entry_template = { #endif /* allocate dst with ip6_dst_ops */ -static inline struct rt6_info *ip6_dst_alloc(struct dst_ops *ops, +static inline struct rt6_info *ip6_dst_alloc(struct net *net, struct net_device *dev, int flags) { - struct rt6_info *rt = dst_alloc(ops, dev, 0, 0, flags); + struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, + 0, 0, flags); - if (rt) + if (rt) { memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry)); - + rt6_init_peer(rt, net->ipv6.peers); + } return rt; } @@ -275,7 +277,6 @@ static void ip6_dst_destroy(struct dst_entry *dst) { struct rt6_info *rt = (struct rt6_info *)dst; struct inet6_dev *idev = rt->rt6i_idev; - struct inet_peer *peer = rt->rt6i_peer; if (!(rt->dst.flags & DST_HOST)) dst_destroy_metrics_generic(dst); @@ -288,8 +289,8 @@ static void ip6_dst_destroy(struct dst_entry *dst) if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from) dst_release(dst->from); - if (peer) { - rt->rt6i_peer = NULL; + if (rt6_has_peer(rt)) { + struct inet_peer *peer = rt6_peer_ptr(rt); inet_putpeer(peer); } } @@ -303,11 +304,15 @@ static u32 rt6_peer_genid(void) void rt6_bind_peer(struct rt6_info *rt, int create) { - struct net *net = dev_net(rt->dst.dev); + struct inet_peer_base *base; struct inet_peer *peer; - peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, create); - if (peer && cmpxchg(&rt->rt6i_peer, NULL, peer) != NULL) + base = inetpeer_base_ptr(rt->_rt6i_peer); + if (!base) + return; + + peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create); + if (!rt6_set_peer(rt, peer)) inet_putpeer(peer); else rt->rt6i_peer_genid = rt6_peer_genid(); @@ -950,6 +955,7 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori rt = dst_alloc(&ip6_dst_blackhole_ops, ort->dst.dev, 1, 0, 0); if (rt) { memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry)); + rt6_init_peer(rt, net->ipv6.peers); new = &rt->dst; @@ -994,7 +1000,7 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) { if (rt->rt6i_peer_genid != rt6_peer_genid()) { - if (!rt->rt6i_peer) + if (!rt6_has_peer(rt)) rt6_bind_peer(rt, 0); rt->rt6i_peer_genid = rt6_peer_genid(); } @@ -1108,7 +1114,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, if (unlikely(!idev)) return ERR_PTR(-ENODEV); - rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, dev, 0); + rt = ip6_dst_alloc(net, dev, 0); if (unlikely(!rt)) { in6_dev_put(idev); dst = ERR_PTR(-ENOMEM); @@ -1290,7 +1296,7 @@ int ip6_route_add(struct fib6_config *cfg) if (!table) goto out; - rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, NULL, DST_NOCOUNT); + rt = ip6_dst_alloc(net, NULL, DST_NOCOUNT); if (!rt) { err = -ENOMEM; @@ -1812,8 +1818,7 @@ static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, const struct in6_addr *dest) { struct net *net = dev_net(ort->dst.dev); - struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, - ort->dst.dev, 0); + struct rt6_info *rt = ip6_dst_alloc(net, ort->dst.dev, 0); if (rt) { rt->dst.input = ort->dst.input; @@ -2097,8 +2102,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, bool anycast) { struct net *net = dev_net(idev->dev); - struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops, - net->loopback_dev, 0); + struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, 0); int err; if (!rt) { @@ -2519,7 +2523,9 @@ static int rt6_fill_node(struct net *net, else expires = INT_MAX; - peer = rt->rt6i_peer; + peer = NULL; + if (rt6_has_peer(rt)) + peer = rt6_peer_ptr(rt); ts = tsage = 0; if (peer && peer->tcp_ts_stamp) { ts = peer->tcp_ts; diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 8625fba96db9..d7494845efbf 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -99,9 +99,7 @@ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, if (!xdst->u.rt6.rt6i_idev) return -ENODEV; - xdst->u.rt6.rt6i_peer = rt->rt6i_peer; - if (rt->rt6i_peer) - atomic_inc(&rt->rt6i_peer->refcnt); + rt6_transfer_peer(&xdst->u.rt6, rt); /* Sheit... I remember I did this right. Apparently, * it was magically lost, so this code needs audit */ @@ -223,8 +221,10 @@ static void xfrm6_dst_destroy(struct dst_entry *dst) if (likely(xdst->u.rt6.rt6i_idev)) in6_dev_put(xdst->u.rt6.rt6i_idev); dst_destroy_metrics_generic(dst); - if (likely(xdst->u.rt6.rt6i_peer)) - inet_putpeer(xdst->u.rt6.rt6i_peer); + if (rt6_has_peer(&xdst->u.rt6)) { + struct inet_peer *peer = rt6_peer_ptr(&xdst->u.rt6); + inet_putpeer(peer); + } xfrm_dst_destroy(xdst); } -- cgit v1.2.3 From 46517008e1168dc926cf2c47d529efc07eca85c0 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 10 Jun 2012 00:04:12 -0700 Subject: ipv4: Kill ip_rt_frag_needed(). There is zero point to this function. It's only real substance is to perform an extremely outdated BSD4.2 ICMP check, which we can safely remove. If you really have a MTU limited link being routed by a BSD4.2 derived system, here's a nickel go buy yourself a real router. The other actions of ip_rt_frag_needed(), checking and conditionally updating the peer, are done by the per-protocol handlers of the ICMP event. TCP, UDP, et al. have a handler which will receive this event and transmit it back into the associated route via dst_ops->update_pmtu(). This simplification is important, because it eliminates the one place where we do not have a proper route context in which to make an inetpeer lookup. Signed-off-by: David S. Miller --- include/net/route.h | 2 -- net/ipv4/icmp.c | 4 +--- net/ipv4/route.c | 61 ---------------------------------------------------- net/rxrpc/ar-error.c | 4 ---- 4 files changed, 1 insertion(+), 70 deletions(-) (limited to 'include') diff --git a/include/net/route.h b/include/net/route.h index 6340c37677fc..cc693a5bb20d 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -215,8 +215,6 @@ static inline int ip_route_input_noref(struct sk_buff *skb, __be32 dst, __be32 s return ip_route_input_common(skb, dst, src, tos, devin, true); } -extern unsigned short ip_rt_frag_needed(struct net *net, const struct iphdr *iph, - unsigned short new_mtu, struct net_device *dev); extern void ip_rt_send_redirect(struct sk_buff *skb); extern unsigned int inet_addr_type(struct net *net, __be32 addr); diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 0c78ef1e5dde..e1caa1abe5d1 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -673,9 +673,7 @@ static void icmp_unreach(struct sk_buff *skb) LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: fragmentation needed and DF set\n"), &iph->daddr); } else { - info = ip_rt_frag_needed(net, iph, - ntohs(icmph->un.frag.mtu), - skb->dev); + info = ntohs(icmph->un.frag.mtu); if (!info) goto out; } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 03e5b614370e..4f5834c4a667 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1664,67 +1664,6 @@ out: kfree_skb(skb); return 0; } -/* - * The last two values are not from the RFC but - * are needed for AMPRnet AX.25 paths. - */ - -static const unsigned short mtu_plateau[] = -{32000, 17914, 8166, 4352, 2002, 1492, 576, 296, 216, 128 }; - -static inline unsigned short guess_mtu(unsigned short old_mtu) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(mtu_plateau); i++) - if (old_mtu > mtu_plateau[i]) - return mtu_plateau[i]; - return 68; -} - -unsigned short ip_rt_frag_needed(struct net *net, const struct iphdr *iph, - unsigned short new_mtu, - struct net_device *dev) -{ - unsigned short old_mtu = ntohs(iph->tot_len); - unsigned short est_mtu = 0; - struct inet_peer *peer; - - peer = inet_getpeer_v4(net->ipv4.peers, iph->daddr, 1); - if (peer) { - unsigned short mtu = new_mtu; - - if (new_mtu < 68 || new_mtu >= old_mtu) { - /* BSD 4.2 derived systems incorrectly adjust - * tot_len by the IP header length, and report - * a zero MTU in the ICMP message. - */ - if (mtu == 0 && - old_mtu >= 68 + (iph->ihl << 2)) - old_mtu -= iph->ihl << 2; - mtu = guess_mtu(old_mtu); - } - - if (mtu < ip_rt_min_pmtu) - mtu = ip_rt_min_pmtu; - if (!peer->pmtu_expires || mtu < peer->pmtu_learned) { - unsigned long pmtu_expires; - - pmtu_expires = jiffies + ip_rt_mtu_expires; - if (!pmtu_expires) - pmtu_expires = 1UL; - - est_mtu = mtu; - peer->pmtu_learned = mtu; - peer->pmtu_expires = pmtu_expires; - atomic_inc(&__rt_peer_genid); - } - - inet_putpeer(peer); - } - return est_mtu ? : new_mtu; -} - static void check_peer_pmtu(struct dst_entry *dst, struct inet_peer *peer) { unsigned long expires = ACCESS_ONCE(peer->pmtu_expires); diff --git a/net/rxrpc/ar-error.c b/net/rxrpc/ar-error.c index 5d6b572a6704..a9206087b4d7 100644 --- a/net/rxrpc/ar-error.c +++ b/net/rxrpc/ar-error.c @@ -81,10 +81,6 @@ void rxrpc_UDP_error_report(struct sock *sk) _net("I/F MTU %u", mtu); } - /* ip_rt_frag_needed() may have eaten the info */ - if (mtu == 0) - mtu = ntohs(icmp_hdr(skb)->un.frag.mtu); - if (mtu == 0) { /* they didn't give us a size, estimate one */ if (mtu > 1500) { -- cgit v1.2.3 From b48c80ece973e9eddb042f6685b482b261ff0d47 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 10 Jun 2012 00:24:21 -0700 Subject: inet: Add family scope inetpeer flushes. This implementation can deal with having many inetpeer roots, which is a necessary prerequisite for per-FIB table rooted peer tables. Each family (AF_INET, AF_INET6) has a sequence number which we bump when we get a family invalidation request. Each peer lookup cheaply checks whether the flush sequence of the root we are using is out of date, and if so flushes it and updates the sequence number. Signed-off-by: David S. Miller --- include/net/inetpeer.h | 2 ++ net/ipv4/inetpeer.c | 28 ++++++++++++++++++++++++++++ net/ipv4/route.c | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index d432489e7109..e15c0862a686 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -68,6 +68,7 @@ struct inet_peer { struct inet_peer_base { struct inet_peer __rcu *root; seqlock_t lock; + u32 flush_seq; int total; }; @@ -168,6 +169,7 @@ extern void inet_putpeer(struct inet_peer *p); extern bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout); extern void inetpeer_invalidate_tree(struct inet_peer_base *); +extern void inetpeer_invalidate_family(int family); /* * temporary check to make sure we dont access rid, ip_id_count, tcp_ts, diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index e4cba56a5349..cac02ad1425d 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -86,10 +86,36 @@ void inet_peer_base_init(struct inet_peer_base *bp) { bp->root = peer_avl_empty_rcu; seqlock_init(&bp->lock); + bp->flush_seq = ~0U; bp->total = 0; } EXPORT_SYMBOL_GPL(inet_peer_base_init); +static atomic_t v4_seq = ATOMIC_INIT(0); +static atomic_t v6_seq = ATOMIC_INIT(0); + +static atomic_t *inetpeer_seq_ptr(int family) +{ + return (family == AF_INET ? &v4_seq : &v6_seq); +} + +static inline void flush_check(struct inet_peer_base *base, int family) +{ + atomic_t *fp = inetpeer_seq_ptr(family); + + if (unlikely(base->flush_seq != atomic_read(fp))) { + inetpeer_invalidate_tree(base); + base->flush_seq = atomic_read(fp); + } +} + +void inetpeer_invalidate_family(int family) +{ + atomic_t *fp = inetpeer_seq_ptr(family); + + atomic_inc(fp); +} + #define PEER_MAXDEPTH 40 /* sufficient for about 2^27 nodes */ /* Exported for sysctl_net_ipv4. */ @@ -437,6 +463,8 @@ struct inet_peer *inet_getpeer(struct inet_peer_base *base, unsigned int sequence; int invalidated, gccnt = 0; + flush_check(base, daddr->family); + /* Attempt a lockless lookup first. * Because of a concurrent writer, we might not find an existing entry. */ diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 4f5834c4a667..456a9470fb54 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -935,7 +935,7 @@ static void rt_cache_invalidate(struct net *net) get_random_bytes(&shuffle, sizeof(shuffle)); atomic_add(shuffle + 1U, &net->ipv4.rt_genid); - inetpeer_invalidate_tree(net->ipv4.peers); + inetpeer_invalidate_family(AF_INET); } /* -- cgit v1.2.3 From 8e77327783c753689a1a766ab9d301b81c2529f1 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 11 Jun 2012 00:01:52 -0700 Subject: inet: Add inetpeer tree roots to the FIB tables. Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 1 + include/net/ip_fib.h | 12 +++++++----- net/ipv4/fib_trie.c | 3 +++ net/ipv6/ip6_fib.c | 5 +++++ 4 files changed, 16 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 3ac5f155c690..a192f7807659 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -237,6 +237,7 @@ struct fib6_table { u32 tb6_id; rwlock_t tb6_lock; struct fib6_node tb6_root; + struct inet_peer_base tb6_peers; }; #define RT6_TABLE_UNSPEC RT_TABLE_UNSPEC diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 78df0866cc38..4b347c0ca094 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -19,6 +19,7 @@ #include #include #include +#include struct fib_config { u8 fc_dst_len; @@ -157,11 +158,12 @@ extern __be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh); FIB_RES_SADDR(net, res)) struct fib_table { - struct hlist_node tb_hlist; - u32 tb_id; - int tb_default; - int tb_num_default; - unsigned long tb_data[0]; + struct hlist_node tb_hlist; + u32 tb_id; + int tb_default; + int tb_num_default; + struct inet_peer_base tb_peers; + unsigned long tb_data[0]; }; extern int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 18cbc15b20d5..9b0f25930fbc 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1843,6 +1843,8 @@ int fib_table_flush(struct fib_table *tb) if (ll && hlist_empty(&ll->list)) trie_leaf_remove(t, ll); + inetpeer_invalidate_tree(&tb->tb_peers); + pr_debug("trie_flush found=%d\n", found); return found; } @@ -1991,6 +1993,7 @@ struct fib_table *fib_trie_table(u32 id) tb->tb_id = id; tb->tb_default = -1; tb->tb_num_default = 0; + inet_peer_base_init(&tb->tb_peers); t = (struct trie *) tb->tb_data; memset(t, 0, sizeof(*t)); diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 0c220a416626..7ef0743f06f0 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -197,6 +197,7 @@ static struct fib6_table *fib6_alloc_table(struct net *net, u32 id) table->tb6_id = id; table->tb6_root.leaf = net->ipv6.ip6_null_entry; table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO; + inet_peer_base_init(&table->tb6_peers); } return table; @@ -1633,6 +1634,7 @@ static int __net_init fib6_net_init(struct net *net) net->ipv6.fib6_main_tbl->tb6_root.leaf = net->ipv6.ip6_null_entry; net->ipv6.fib6_main_tbl->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO; + inet_peer_base_init(&net->ipv6.fib6_main_tbl->tb6_peers); #ifdef CONFIG_IPV6_MULTIPLE_TABLES net->ipv6.fib6_local_tbl = kzalloc(sizeof(*net->ipv6.fib6_local_tbl), @@ -1643,6 +1645,7 @@ static int __net_init fib6_net_init(struct net *net) net->ipv6.fib6_local_tbl->tb6_root.leaf = net->ipv6.ip6_null_entry; net->ipv6.fib6_local_tbl->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO; + inet_peer_base_init(&net->ipv6.fib6_local_tbl->tb6_peers); #endif fib6_tables_init(net); @@ -1666,8 +1669,10 @@ static void fib6_net_exit(struct net *net) del_timer_sync(&net->ipv6.ip6_fib_timer); #ifdef CONFIG_IPV6_MULTIPLE_TABLES + inetpeer_invalidate_tree(&net->ipv6.fib6_local_tbl->tb6_peers); kfree(net->ipv6.fib6_local_tbl); #endif + inetpeer_invalidate_tree(&net->ipv6.fib6_main_tbl->tb6_peers); kfree(net->ipv6.fib6_main_tbl); kfree(net->ipv6.fib_table_hash); kfree(net->ipv6.rt6_stats); -- cgit v1.2.3 From 7b34ca2ac7063f4ebf07f85fd75253ed84d5c648 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 11 Jun 2012 04:13:57 -0700 Subject: inet: Avoid potential NULL peer dereference. We handle NULL in rt{,6}_set_peer but then our caller will try to pass that NULL pointer into inet_putpeer() which isn't ready for it. Fix this by moving the NULL check one level up, and then remove the now unnecessary NULL check from inetpeer_ptr_set_peer(). Reported-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inetpeer.h | 2 +- net/ipv4/route.c | 11 ++++++----- net/ipv6/route.c | 10 ++++++---- 3 files changed, 13 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index e15c0862a686..c27c8f10ebdc 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -104,7 +104,7 @@ static inline bool inetpeer_ptr_set_peer(unsigned long *ptr, struct inet_peer *p unsigned long val = (unsigned long) peer; unsigned long orig = *ptr; - if (!(orig & INETPEER_BASE_BIT) || !val || + if (!(orig & INETPEER_BASE_BIT) || cmpxchg(ptr, orig, val) != orig) return false; return true; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 4c33ce3000ed..842510d50453 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1333,11 +1333,12 @@ void rt_bind_peer(struct rtable *rt, __be32 daddr, int create) return; peer = inet_getpeer_v4(base, daddr, create); - - if (!rt_set_peer(rt, peer)) - inet_putpeer(peer); - else - rt->rt_peer_genid = rt_peer_genid(); + if (peer) { + if (!rt_set_peer(rt, peer)) + inet_putpeer(peer); + else + rt->rt_peer_genid = rt_peer_genid(); + } } /* diff --git a/net/ipv6/route.c b/net/ipv6/route.c index d9ba4808f26a..58a3ec23da2f 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -313,10 +313,12 @@ void rt6_bind_peer(struct rt6_info *rt, int create) return; peer = inet_getpeer_v6(base, &rt->rt6i_dst.addr, create); - if (!rt6_set_peer(rt, peer)) - inet_putpeer(peer); - else - rt->rt6i_peer_genid = rt6_peer_genid(); + if (peer) { + if (!rt6_set_peer(rt, peer)) + inet_putpeer(peer); + else + rt->rt6i_peer_genid = rt6_peer_genid(); + } } static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, -- cgit v1.2.3 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') 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 From 53f2d02898755d1b24bde1975e202815d29fdb81 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 23 Feb 2012 08:10:34 -0300 Subject: RAS: Add a tracepoint for reporting memory controller events Add a new tracepoint-based hardware events report method for reporting Memory Controller events. Part of the description bellow is shamelessly copied from Tony Luck's notes about the Hardware Error BoF during LPC 2010 [1]. Tony, thanks for your notes and discussions to generate the h/w error reporting requirements. [1] http://lwn.net/Articles/416669/ We have several subsystems & methods for reporting hardware errors: 1) EDAC ("Error Detection and Correction"). In its original form this consisted of a platform specific driver that read topology information and error counts from chipset registers and reported the results via a sysfs interface. 2) mcelog - x86 specific decoding of machine check bank registers reporting in binary form via /dev/mcelog. Recent additions make use of the APEI extensions that were documented in version 4.0a of the ACPI specification to acquire more information about errors without having to rely reading chipset registers directly. A user level programs decodes into somewhat human readable format. 3) drivers/edac/mce_amd.c - this driver hooks into the mcelog path and decodes errors reported via machine check bank registers in AMD processors to the console log using printk(); Each of these mechanisms has a band of followers ... and none of them appear to meet all the needs of all users. As part of a RAS subsystem, let's encapsulate the memory error hardware events into a trace facility. The tracepoint printk will be displayed like: mc_event: [quant] (Corrected|Uncorrected|Fatal) error:[error msg] on [label] ([location] [edac_mc detail] [driver_detail] Where: [quant] is the quantity of errors [error msg] is the driver-specific error message (e. g. "memory read", "bus error", ...); [location] is the location in terms of memory controller and branch/channel/slot, channel/slot or csrow/channel; [label] is the memory stick label; [edac_mc detail] describes the address location of the error and the syndrome; [driver detail] is driver-specifig error message details, when needed/provided (e. g. "area:DMA", ...) For example: mc_event: 1 Corrected error:memory read on memory stick DIMM_1A (mc:0 location:0:0:0 page:0x586b6e offset:0xa66 grain:32 syndrome:0x0 area:DMA) Of course, any userspace tools meant to handle errors should not parse the above data. They should, instead, use the binary fields provided by the tracepoint, mapping them directly into their Management Information Base. NOTE: The original patch was providing an additional mechanism for MCA-based trace events that also contained MCA error register data. However, as no agreement was reached so far for the MCA-based trace events, for now, let's add events only for memory errors. A latter patch is planned to change the tracepoint, for those types of event. Cc: Aristeu Rozanski Cc: Doug Thompson Cc: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_core.h | 8 ++-- drivers/edac/edac_mc.c | 72 +++++++++++++++++++++++++-------- include/ras/ras_event.h | 102 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 20 deletions(-) create mode 100644 include/ras/ras_event.h (limited to 'include') diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index f06ce9ab692c..740c7e22c023 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h @@ -463,12 +463,12 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, const unsigned long page_frame_number, const unsigned long offset_in_page, const unsigned long syndrome, - const int layer0, - const int layer1, - const int layer2, + const int top_layer, + const int mid_layer, + const int low_layer, const char *msg, const char *other_detail, - const void *mcelog); + const void *arch_log); /* * edac_device APIs diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 10f375032e96..ce25750a83f9 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -27,12 +27,17 @@ #include #include #include +#include #include #include #include #include "edac_core.h" #include "edac_module.h" +#define CREATE_TRACE_POINTS +#define TRACE_INCLUDE_PATH ../../include/ras +#include + /* lock to memory controller's control array */ static DEFINE_MUTEX(mem_ctls_mutex); static LIST_HEAD(mc_devices); @@ -384,6 +389,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, * which will perform kobj unregistration and the actual free * will occur during the kobject callback operation */ + return mci; } EXPORT_SYMBOL_GPL(edac_mc_alloc); @@ -902,19 +908,19 @@ static void edac_ce_error(struct mem_ctl_info *mci, const bool enable_per_layer_report, const unsigned long page_frame_number, const unsigned long offset_in_page, - u32 grain) + long grain) { unsigned long remapped_page; if (edac_mc_get_log_ce()) { if (other_detail && *other_detail) edac_mc_printk(mci, KERN_WARNING, - "CE %s on %s (%s%s - %s)\n", + "CE %s on %s (%s %s - %s)\n", msg, label, location, detail, other_detail); else edac_mc_printk(mci, KERN_WARNING, - "CE %s on %s (%s%s)\n", + "CE %s on %s (%s %s)\n", msg, label, location, detail); } @@ -953,12 +959,12 @@ static void edac_ue_error(struct mem_ctl_info *mci, if (edac_mc_get_log_ue()) { if (other_detail && *other_detail) edac_mc_printk(mci, KERN_WARNING, - "UE %s on %s (%s%s - %s)\n", + "UE %s on %s (%s %s - %s)\n", msg, label, location, detail, other_detail); else edac_mc_printk(mci, KERN_WARNING, - "UE %s on %s (%s%s)\n", + "UE %s on %s (%s %s)\n", msg, label, location, detail); } @@ -975,27 +981,50 @@ static void edac_ue_error(struct mem_ctl_info *mci, } #define OTHER_LABEL " or " + +/** + * edac_mc_handle_error - reports a memory event to userspace + * + * @type: severity of the error (CE/UE/Fatal) + * @mci: a struct mem_ctl_info pointer + * @page_frame_number: mem page where the error occurred + * @offset_in_page: offset of the error inside the page + * @syndrome: ECC syndrome + * @top_layer: Memory layer[0] position + * @mid_layer: Memory layer[1] position + * @low_layer: Memory layer[2] position + * @msg: Message meaningful to the end users that + * explains the event + * @other_detail: Technical details about the event that + * may help hardware manufacturers and + * EDAC developers to analyse the event + * @arch_log: Architecture-specific struct that can + * be used to add extended information to the + * tracepoint, like dumping MCE registers. + */ void edac_mc_handle_error(const enum hw_event_mc_err_type type, struct mem_ctl_info *mci, const unsigned long page_frame_number, const unsigned long offset_in_page, const unsigned long syndrome, - const int layer0, - const int layer1, - const int layer2, + const int top_layer, + const int mid_layer, + const int low_layer, const char *msg, const char *other_detail, - const void *mcelog) + const void *arch_log) { /* FIXME: too much for stack: move it to some pre-alocated area */ char detail[80], location[80]; char label[(EDAC_MC_LABEL_LEN + 1 + sizeof(OTHER_LABEL)) * mci->tot_dimms]; char *p; int row = -1, chan = -1; - int pos[EDAC_MAX_LAYERS] = { layer0, layer1, layer2 }; + int pos[EDAC_MAX_LAYERS] = { top_layer, mid_layer, low_layer }; int i; - u32 grain; + long grain; bool enable_per_layer_report = false; + u16 error_count; /* FIXME: make it a parameter */ + u8 grain_bits; debugf3("MC%d: %s()\n", mci->mc_idx, __func__); @@ -1045,11 +1074,11 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, for (i = 0; i < mci->tot_dimms; i++) { struct dimm_info *dimm = &mci->dimms[i]; - if (layer0 >= 0 && layer0 != dimm->location[0]) + if (top_layer >= 0 && top_layer != dimm->location[0]) continue; - if (layer1 >= 0 && layer1 != dimm->location[1]) + if (mid_layer >= 0 && mid_layer != dimm->location[1]) continue; - if (layer2 >= 0 && layer2 != dimm->location[2]) + if (low_layer >= 0 && low_layer != dimm->location[2]) continue; /* get the max grain, over the error match range */ @@ -1120,11 +1149,22 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, edac_layer_name[mci->layers[i].type], pos[i]); } + if (p > location) + *(p - 1) = '\0'; + + /* Report the error via the trace interface */ + + error_count = 1; /* FIXME: allow change it */ + grain_bits = fls_long(grain) + 1; + trace_mc_event(type, msg, label, error_count, + mci->mc_idx, top_layer, mid_layer, low_layer, + PAGES_TO_MiB(page_frame_number) | offset_in_page, + grain_bits, syndrome, other_detail); /* Memory type dependent details about the error */ if (type == HW_EVENT_ERR_CORRECTED) { snprintf(detail, sizeof(detail), - "page:0x%lx offset:0x%lx grain:%d syndrome:0x%lx", + "page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx", page_frame_number, offset_in_page, grain, syndrome); edac_ce_error(mci, pos, msg, location, label, detail, @@ -1132,7 +1172,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, page_frame_number, offset_in_page, grain); } else { snprintf(detail, sizeof(detail), - "page:0x%lx offset:0x%lx grain:%d", + "page:0x%lx offset:0x%lx grain:%ld", page_frame_number, offset_in_page, grain); edac_ue_error(mci, pos, msg, location, label, detail, diff --git a/include/ras/ras_event.h b/include/ras/ras_event.h new file mode 100644 index 000000000000..260470e72483 --- /dev/null +++ b/include/ras/ras_event.h @@ -0,0 +1,102 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM ras +#define TRACE_INCLUDE_FILE ras_event + +#if !defined(_TRACE_HW_EVENT_MC_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_HW_EVENT_MC_H + +#include +#include +#include + +/* + * Hardware Events Report + * + * Those events are generated when hardware detected a corrected or + * uncorrected event, and are meant to replace the current API to report + * errors defined on both EDAC and MCE subsystems. + * + * FIXME: Add events for handling memory errors originated from the + * MCE subsystem. + */ + +/* + * Hardware-independent Memory Controller specific events + */ + +/* + * Default error mechanisms for Memory Controller errors (CE and UE) + */ +TRACE_EVENT(mc_event, + + TP_PROTO(const unsigned int err_type, + const char *error_msg, + const char *label, + const int error_count, + const u8 mc_index, + const s8 top_layer, + const s8 mid_layer, + const s8 low_layer, + unsigned long address, + const u8 grain_bits, + unsigned long syndrome, + const char *driver_detail), + + TP_ARGS(err_type, error_msg, label, error_count, mc_index, + top_layer, mid_layer, low_layer, address, grain_bits, + syndrome, driver_detail), + + TP_STRUCT__entry( + __field( unsigned int, error_type ) + __string( msg, error_msg ) + __string( label, label ) + __field( u16, error_count ) + __field( u8, mc_index ) + __field( s8, top_layer ) + __field( s8, middle_layer ) + __field( s8, lower_layer ) + __field( long, address ) + __field( u8, grain_bits ) + __field( long, syndrome ) + __string( driver_detail, driver_detail ) + ), + + TP_fast_assign( + __entry->error_type = err_type; + __assign_str(msg, error_msg); + __assign_str(label, label); + __entry->error_count = error_count; + __entry->mc_index = mc_index; + __entry->top_layer = top_layer; + __entry->middle_layer = mid_layer; + __entry->lower_layer = low_layer; + __entry->address = address; + __entry->grain_bits = grain_bits; + __entry->syndrome = syndrome; + __assign_str(driver_detail, driver_detail); + ), + + TP_printk("%d %s error%s:%s%s on %s (mc:%d location:%d:%d:%d address:0x%08lx grain:%d syndrome:0x%08lx%s%s)", + __entry->error_count, + (__entry->error_type == HW_EVENT_ERR_CORRECTED) ? "Corrected" : + ((__entry->error_type == HW_EVENT_ERR_FATAL) ? + "Fatal" : "Uncorrected"), + __entry->error_count > 1 ? "s" : "", + ((char *)__get_str(msg))[0] ? " " : "", + __get_str(msg), + __get_str(label), + __entry->mc_index, + __entry->top_layer, + __entry->middle_layer, + __entry->lower_layer, + __entry->address, + 1 << __entry->grain_bits, + __entry->syndrome, + ((char *)__get_str(driver_detail))[0] ? " " : "", + __get_str(driver_detail)) +); + +#endif /* _TRACE_HW_EVENT_MC_H */ + +/* This part must be outside protection */ +#include -- cgit v1.2.3 From fd687502dc8037aa5a4b84c570ada971106574ee Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 16 Mar 2012 07:44:18 -0300 Subject: edac: Rename the parent dev to pdev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As EDAC doesn't use struct device itself, it created a parent dev pointer called as "pdev". Now that we'll be converting it to use struct device, instead of struct devsys, this needs to be fixed. No functional changes. Reviewed-by: Aristeu Rozanski Acked-by: Chris Metcalf Cc: Doug Thompson Cc: Borislav Petkov Cc: Mark Gross Cc: Jason Uhlenkott Cc: Tim Small Cc: Ranganathan Desikan Cc: "Arvind R." Cc: Olof Johansson Cc: Egor Martovetsky Cc: Michal Marek Cc: Jiri Kosina Cc: Joe Perches Cc: Dmitry Eremin-Solenikov Cc: Benjamin Herrenschmidt Cc: Hitoshi Mitake Cc: Andrew Morton Cc: "Niklas Söderlund" Cc: Shaohui Xie Cc: Josh Boyer Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/amd64_edac.c | 2 +- drivers/edac/amd76x_edac.c | 4 ++-- drivers/edac/cell_edac.c | 12 ++++++------ drivers/edac/cpc925_edac.c | 2 +- drivers/edac/e752x_edac.c | 2 +- drivers/edac/e7xxx_edac.c | 2 +- drivers/edac/edac_mc.c | 8 ++++---- drivers/edac/edac_mc_sysfs.c | 2 +- drivers/edac/i3000_edac.c | 4 ++-- drivers/edac/i3200_edac.c | 6 +++--- drivers/edac/i5000_edac.c | 2 +- drivers/edac/i5100_edac.c | 2 +- drivers/edac/i5400_edac.c | 2 +- drivers/edac/i7300_edac.c | 2 +- drivers/edac/i7core_edac.c | 4 ++-- drivers/edac/i82443bxgx_edac.c | 4 ++-- drivers/edac/i82860_edac.c | 4 ++-- drivers/edac/i82875p_edac.c | 4 ++-- drivers/edac/i82975x_edac.c | 4 ++-- drivers/edac/mpc85xx_edac.c | 4 ++-- drivers/edac/mv64x60_edac.c | 2 +- drivers/edac/pasemi_edac.c | 6 +++--- drivers/edac/ppc4xx_edac.c | 8 ++++---- drivers/edac/r82600_edac.c | 4 ++-- drivers/edac/sb_edac.c | 4 ++-- drivers/edac/tile_edac.c | 4 ++-- drivers/edac/x38_edac.c | 6 +++--- include/linux/edac.h | 2 +- 28 files changed, 56 insertions(+), 56 deletions(-) (limited to 'include') diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 7be9b7288e90..821bc2cdd2de 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -2601,7 +2601,7 @@ static int amd64_init_one_instance(struct pci_dev *F2) goto err_siblings; mci->pvt_info = pvt; - mci->dev = &pvt->F2->dev; + mci->pdev = &pvt->F2->dev; setup_mci_misc_attrs(mci, fam_type); diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c index 9774d443fa57..7439786f3bef 100644 --- a/drivers/edac/amd76x_edac.c +++ b/drivers/edac/amd76x_edac.c @@ -105,7 +105,7 @@ static void amd76x_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); pci_read_config_dword(pdev, AMD76X_ECC_MODE_STATUS, &info->ecc_mode_status); @@ -257,7 +257,7 @@ static int amd76x_probe1(struct pci_dev *pdev, int dev_idx) return -ENOMEM; debugf0("%s(): mci = %p\n", __func__, mci); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_RDDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; mci->edac_cap = ems_mode ? diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c index 69ee6aab5c71..2e5b95374dc6 100644 --- a/drivers/edac/cell_edac.c +++ b/drivers/edac/cell_edac.c @@ -36,7 +36,7 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar) struct csrow_info *csrow = &mci->csrows[0]; unsigned long address, pfn, offset, syndrome; - dev_dbg(mci->dev, "ECC CE err on node %d, channel %d, ar = 0x%016llx\n", + dev_dbg(mci->pdev, "ECC CE err on node %d, channel %d, ar = 0x%016llx\n", priv->node, chan, ar); /* Address decoding is likely a bit bogus, to dbl check */ @@ -59,7 +59,7 @@ static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar) struct csrow_info *csrow = &mci->csrows[0]; unsigned long address, pfn, offset; - dev_dbg(mci->dev, "ECC UE err on node %d, channel %d, ar = 0x%016llx\n", + dev_dbg(mci->pdev, "ECC UE err on node %d, channel %d, ar = 0x%016llx\n", priv->node, chan, ar); /* Address decoding is likely a bit bogus, to dbl check */ @@ -83,7 +83,7 @@ static void cell_edac_check(struct mem_ctl_info *mci) fir = in_be64(&priv->regs->mic_fir); #ifdef DEBUG if (fir != priv->prev_fir) { - dev_dbg(mci->dev, "fir change : 0x%016lx\n", fir); + dev_dbg(mci->pdev, "fir change : 0x%016lx\n", fir); priv->prev_fir = fir; } #endif @@ -119,7 +119,7 @@ static void cell_edac_check(struct mem_ctl_info *mci) mb(); /* sync up */ #ifdef DEBUG fir = in_be64(&priv->regs->mic_fir); - dev_dbg(mci->dev, "fir clear : 0x%016lx\n", fir); + dev_dbg(mci->pdev, "fir clear : 0x%016lx\n", fir); #endif } } @@ -155,7 +155,7 @@ static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci) dimm->edac_mode = EDAC_SECDED; dimm->nr_pages = nr_pages / csrow->nr_channels; } - dev_dbg(mci->dev, + dev_dbg(mci->pdev, "Initialized on node %d, chanmask=0x%x," " first_page=0x%lx, nr_pages=0x%x\n", priv->node, priv->chanmask, @@ -212,7 +212,7 @@ static int __devinit cell_edac_probe(struct platform_device *pdev) priv->regs = regs; priv->node = pdev->id; priv->chanmask = chanmask; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_XDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; mci->edac_cap = EDAC_FLAG_EC | EDAC_FLAG_SECDED; diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c index e22030a9de66..9488723f8138 100644 --- a/drivers/edac/cpc925_edac.c +++ b/drivers/edac/cpc925_edac.c @@ -995,7 +995,7 @@ static int __devinit cpc925_probe(struct platform_device *pdev) pdata->edac_idx = edac_mc_idx++; pdata->name = pdev->name; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; platform_set_drvdata(pdev, mci); mci->dev_name = dev_name(&pdev->dev); mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR; diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index 3186512c9739..d75660634b43 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c @@ -1308,7 +1308,7 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx) /* FIXME - what if different memory types are in different csrows? */ mci->mod_name = EDAC_MOD_STR; mci->mod_ver = E752X_REVISION; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; debugf3("%s(): init pvt\n", __func__); pvt = (struct e752x_pvt *)mci->pvt_info; diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c index 9a9c1a546797..b111266dadf3 100644 --- a/drivers/edac/e7xxx_edac.c +++ b/drivers/edac/e7xxx_edac.c @@ -458,7 +458,7 @@ static int e7xxx_probe1(struct pci_dev *pdev, int dev_idx) /* FIXME - what if different memory types are in different csrows? */ mci->mod_name = EDAC_MOD_STR; mci->mod_ver = E7XXX_REVISION; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; debugf3("%s(): init pvt\n", __func__); pvt = (struct e7xxx_pvt *)mci->pvt_info; pvt->dev_info = &e7xxx_devs[dev_idx]; diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index ce25750a83f9..811f09a38f3a 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -93,7 +93,7 @@ static void edac_mc_dump_mci(struct mem_ctl_info *mci) mci->nr_csrows, mci->csrows); debugf3("\tmci->nr_dimms = %d, dimms = %p\n", mci->tot_dimms, mci->dimms); - debugf3("\tdev = %p\n", mci->dev); + debugf3("\tdev = %p\n", mci->pdev); debugf3("\tmod_name:ctl_name = %s:%s\n", mci->mod_name, mci->ctl_name); debugf3("\tpvt_info = %p\n\n", mci->pvt_info); } @@ -428,7 +428,7 @@ struct mem_ctl_info *find_mci_by_dev(struct device *dev) list_for_each(item, &mc_devices) { mci = list_entry(item, struct mem_ctl_info, link); - if (mci->dev == dev) + if (mci->pdev == dev) return mci; } @@ -580,7 +580,7 @@ static int add_mc_to_global_list(struct mem_ctl_info *mci) insert_before = &mc_devices; - p = find_mci_by_dev(mci->dev); + p = find_mci_by_dev(mci->pdev); if (unlikely(p != NULL)) goto fail0; @@ -602,7 +602,7 @@ static int add_mc_to_global_list(struct mem_ctl_info *mci) fail0: edac_printk(KERN_WARNING, EDAC_MC, - "%s (%s) %s %s already assigned %d\n", dev_name(p->dev), + "%s (%s) %s %s already assigned %d\n", dev_name(p->pdev), edac_dev_name(mci), p->mod_name, p->ctl_name, p->mc_idx); return 1; diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index f6a29b0eedc8..595371941ef9 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -916,7 +916,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) INIT_LIST_HEAD(&mci->grp_kobj_list); /* create a symlink for the device */ - err = sysfs_create_link(kobj_mci, &mci->dev->kobj, + err = sysfs_create_link(kobj_mci, &mci->pdev->kobj, EDAC_DEVICE_SYMLINK); if (err) { debugf1("%s() failure to create symlink\n", __func__); diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c index 8ad1744faacd..d1ebd9b9ad6f 100644 --- a/drivers/edac/i3000_edac.c +++ b/drivers/edac/i3000_edac.c @@ -194,7 +194,7 @@ static void i3000_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -368,7 +368,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) debugf3("MC: %s(): init mci\n", __func__); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_SECDED; diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c index bbe43ef71823..600a05df3759 100644 --- a/drivers/edac/i3200_edac.c +++ b/drivers/edac/i3200_edac.c @@ -159,7 +159,7 @@ static void i3200_clear_error_info(struct mem_ctl_info *mci) { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * Clear any error bits. @@ -176,7 +176,7 @@ static void i3200_get_and_clear_error_info(struct mem_ctl_info *mci, struct i3200_priv *priv = mci->pvt_info; void __iomem *window = priv->window; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -354,7 +354,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx) debugf3("MC: %s(): init mci\n", __func__); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_SECDED; diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index 11ea835f155a..a69245ad5f32 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -1409,7 +1409,7 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx) kobject_get(&mci->edac_mci_kobj); debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci); - mci->dev = &pdev->dev; /* record ptr to the generic device */ + mci->pdev = &pdev->dev; /* record ptr to the generic device */ pvt = mci->pvt_info; pvt->system_address = pdev; /* Record this device in our private */ diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c index e9e7c2a29dc3..19aa2843c46a 100644 --- a/drivers/edac/i5100_edac.c +++ b/drivers/edac/i5100_edac.c @@ -943,7 +943,7 @@ static int __devinit i5100_init_one(struct pci_dev *pdev, goto bail_disable_ch1; } - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; priv = mci->pvt_info; priv->ranksperchan = ranksperch; diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c index 6640c29e1885..ba60694437bd 100644 --- a/drivers/edac/i5400_edac.c +++ b/drivers/edac/i5400_edac.c @@ -1299,7 +1299,7 @@ static int i5400_probe1(struct pci_dev *pdev, int dev_idx) debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci); - mci->dev = &pdev->dev; /* record ptr to the generic device */ + mci->pdev = &pdev->dev; /* record ptr to the generic device */ pvt = mci->pvt_info; pvt->system_address = pdev; /* Record this device in our private */ diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c index 97c22fd650ee..db84456e65d9 100644 --- a/drivers/edac/i7300_edac.c +++ b/drivers/edac/i7300_edac.c @@ -1057,7 +1057,7 @@ static int __devinit i7300_init_one(struct pci_dev *pdev, debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); - mci->dev = &pdev->dev; /* record ptr to the generic device */ + mci->pdev = &pdev->dev; /* record ptr to the generic device */ pvt = mci->pvt_info; pvt->pci_dev_16_0_fsb_ctlr = pdev; /* Record this device in our private */ diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index c05e1ada7a3d..598d215f7bd5 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -2122,7 +2122,7 @@ static void i7core_unregister_mci(struct i7core_dev *i7core_dev) i7core_pci_ctl_release(pvt); /* Remove MC sysfs nodes */ - edac_mc_del_mc(mci->dev); + edac_mc_del_mc(mci->pdev); debugf1("%s: free mci struct\n", mci->ctl_name); kfree(mci->ctl_name); @@ -2188,7 +2188,7 @@ static int i7core_register_mci(struct i7core_dev *i7core_dev) /* Get dimm basic config */ get_dimm_config(mci); /* record ptr to the generic device */ - mci->dev = &i7core_dev->pdev[0]->dev; + mci->pdev = &i7core_dev->pdev[0]->dev; /* Set the function pointer to an actual operation function */ mci->edac_check = i7core_check_error; diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c index 52072c28a8a6..65fd2e1eceb8 100644 --- a/drivers/edac/i82443bxgx_edac.c +++ b/drivers/edac/i82443bxgx_edac.c @@ -124,7 +124,7 @@ static void i82443bxgx_edacmc_get_error_info(struct mem_ctl_info *mci, *info) { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); pci_read_config_dword(pdev, I82443BXGX_EAP, &info->eap); if (info->eap & I82443BXGX_EAP_OFFSET_SBE) /* Clear error to allow next error to be reported [p.61] */ @@ -260,7 +260,7 @@ static int i82443bxgx_edacmc_probe1(struct pci_dev *pdev, int dev_idx) return -ENOMEM; debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_EDO | MEM_FLAG_SDR | MEM_FLAG_RDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; pci_read_config_byte(pdev, I82443BXGX_DRAMC, &dramc); diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c index 08045059d10b..8f3350000942 100644 --- a/drivers/edac/i82860_edac.c +++ b/drivers/edac/i82860_edac.c @@ -67,7 +67,7 @@ static void i82860_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -211,7 +211,7 @@ static int i82860_probe1(struct pci_dev *pdev, int dev_idx) return -ENOMEM; debugf3("%s(): init mci\n", __func__); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; /* I"m not sure about this but I think that all RDRAM is SECDED */ diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index b613e31c16e5..1cc682d0678d 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c @@ -189,7 +189,7 @@ static void i82875p_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -430,7 +430,7 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) kobject_get(&mci->edac_mci_kobj); debugf3("%s(): init mci\n", __func__); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; mci->edac_cap = EDAC_FLAG_UNKNOWN; diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c index 433332c7cdba..8b26401efa19 100644 --- a/drivers/edac/i82975x_edac.c +++ b/drivers/edac/i82975x_edac.c @@ -241,7 +241,7 @@ static void i82975x_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -559,7 +559,7 @@ static int i82975x_probe1(struct pci_dev *pdev, int dev_idx) } debugf3("%s(): init mci\n", __func__); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; mci->edac_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index 4c402353ba98..67fb3280f333 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c @@ -989,9 +989,9 @@ static int __devinit mpc85xx_mc_err_probe(struct platform_device *op) pdata = mci->pvt_info; pdata->name = "mpc85xx_mc_err"; pdata->irq = NO_IRQ; - mci->dev = &op->dev; + mci->pdev = &op->dev; pdata->edac_idx = edac_mc_idx++; - dev_set_drvdata(mci->dev, mci); + dev_set_drvdata(mci->pdev, mci); mci->ctl_name = pdata->name; mci->dev_name = pdata->name; diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c index b0bb5a3d2527..ff6b8e248e89 100644 --- a/drivers/edac/mv64x60_edac.c +++ b/drivers/edac/mv64x60_edac.c @@ -724,7 +724,7 @@ static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev) } pdata = mci->pvt_info; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; platform_set_drvdata(pdev, mci); pdata->name = "mv64x60_mc_err"; pdata->irq = NO_IRQ; diff --git a/drivers/edac/pasemi_edac.c b/drivers/edac/pasemi_edac.c index b095a906a994..92becaa8722a 100644 --- a/drivers/edac/pasemi_edac.c +++ b/drivers/edac/pasemi_edac.c @@ -74,7 +74,7 @@ static int system_mmc_id; static u32 pasemi_edac_get_error_info(struct mem_ctl_info *mci) { - struct pci_dev *pdev = to_pci_dev(mci->dev); + struct pci_dev *pdev = to_pci_dev(mci->pdev); u32 tmp; pci_read_config_dword(pdev, MCDEBUG_ERRSTA, @@ -95,7 +95,7 @@ static u32 pasemi_edac_get_error_info(struct mem_ctl_info *mci) static void pasemi_edac_process_error_info(struct mem_ctl_info *mci, u32 errsta) { - struct pci_dev *pdev = to_pci_dev(mci->dev); + struct pci_dev *pdev = to_pci_dev(mci->pdev); u32 errlog1a; u32 cs; @@ -225,7 +225,7 @@ static int __devinit pasemi_edac_probe(struct pci_dev *pdev, MCCFG_ERRCOR_ECC_GEN_EN | MCCFG_ERRCOR_ECC_CRR_EN; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR | MEM_FLAG_RDDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; mci->edac_cap = (errcor & MCCFG_ERRCOR_ECC_GEN_EN) ? diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c index f3f9fed06ad7..53519828cc36 100644 --- a/drivers/edac/ppc4xx_edac.c +++ b/drivers/edac/ppc4xx_edac.c @@ -1027,9 +1027,9 @@ ppc4xx_edac_mc_init(struct mem_ctl_info *mci, /* Initial driver pointers and private data */ - mci->dev = &op->dev; + mci->pdev = &op->dev; - dev_set_drvdata(mci->dev, mci); + dev_set_drvdata(mci->pdev, mci); pdata = mci->pvt_info; @@ -1334,7 +1334,7 @@ static int __devinit ppc4xx_edac_probe(struct platform_device *op) return 0; fail1: - edac_mc_del_mc(mci->dev); + edac_mc_del_mc(mci->pdev); fail: edac_mc_free(mci); @@ -1368,7 +1368,7 @@ ppc4xx_edac_remove(struct platform_device *op) dcr_unmap(pdata->dcr_host, SDRAM_DCR_RESOURCE_LEN); - edac_mc_del_mc(mci->dev); + edac_mc_del_mc(mci->pdev); edac_mc_free(mci); return 0; diff --git a/drivers/edac/r82600_edac.c b/drivers/edac/r82600_edac.c index e1cacd164f31..cf4ccbdba85d 100644 --- a/drivers/edac/r82600_edac.c +++ b/drivers/edac/r82600_edac.c @@ -140,7 +140,7 @@ static void r82600_get_error_info(struct mem_ctl_info *mci, { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); pci_read_config_dword(pdev, R82600_EAP, &info->eapr); if (info->eapr & BIT(0)) @@ -296,7 +296,7 @@ static int r82600_probe1(struct pci_dev *pdev, int dev_idx) return -ENOMEM; debugf0("%s(): mci = %p\n", __func__, mci); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR; mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; /* FIXME try to work out if the chip leads have been used for COM2 diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index e834dfd034d6..efa488357ae6 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -1607,7 +1607,7 @@ static void sbridge_unregister_mci(struct sbridge_dev *sbridge_dev) mce_unregister_decode_chain(&sbridge_mce_dec); /* Remove MC sysfs nodes */ - edac_mc_del_mc(mci->dev); + edac_mc_del_mc(mci->pdev); debugf1("%s: free mci struct\n", mci->ctl_name); kfree(mci->ctl_name); @@ -1672,7 +1672,7 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev) get_memory_layout(mci); /* record ptr to the generic device */ - mci->dev = &sbridge_dev->pdev[0]->dev; + mci->pdev = &sbridge_dev->pdev[0]->dev; /* add this new MC control structure to EDAC's list of MCs */ if (unlikely(edac_mc_add_mc(mci))) { diff --git a/drivers/edac/tile_edac.c b/drivers/edac/tile_edac.c index 7bb4614730db..604bc4df653a 100644 --- a/drivers/edac/tile_edac.c +++ b/drivers/edac/tile_edac.c @@ -69,7 +69,7 @@ static void tile_edac_check(struct mem_ctl_info *mci) /* Check if the current error count is different from the saved one. */ if (mem_error.sbe_count != priv->ce_count) { - dev_dbg(mci->dev, "ECC CE err on node %d\n", priv->node); + dev_dbg(mci->pdev, "ECC CE err on node %d\n", priv->node); priv->ce_count = mem_error.sbe_count; edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 0, 0, 0, @@ -149,7 +149,7 @@ static int __devinit tile_edac_mc_probe(struct platform_device *pdev) priv->node = pdev->id; priv->hv_devhdl = hv_devhdl; - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_SECDED; diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c index 1ac7962d63ea..f9506f26e2bf 100644 --- a/drivers/edac/x38_edac.c +++ b/drivers/edac/x38_edac.c @@ -151,7 +151,7 @@ static void x38_clear_error_info(struct mem_ctl_info *mci) { struct pci_dev *pdev; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * Clear any error bits. @@ -172,7 +172,7 @@ static void x38_get_and_clear_error_info(struct mem_ctl_info *mci, struct pci_dev *pdev; void __iomem *window = mci->pvt_info; - pdev = to_pci_dev(mci->dev); + pdev = to_pci_dev(mci->pdev); /* * This is a mess because there is no atomic way to read all the @@ -354,7 +354,7 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx) debugf3("MC: %s(): init mci\n", __func__); - mci->dev = &pdev->dev; + mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR2; mci->edac_ctl_cap = EDAC_FLAG_SECDED; diff --git a/include/linux/edac.h b/include/linux/edac.h index 91ba3bae42ee..ec1b5278b4cc 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -567,7 +567,7 @@ struct mem_ctl_info { * unique. dev pointer should be sufficiently unique, but * BUS:SLOT.FUNC numbers may not be unique. */ - struct device *dev; + struct device *pdev; const char *mod_name; const char *mod_ver; const char *ctl_name; -- cgit v1.2.3 From b0610bb82abd1c4ac97c33f0312cd7fd72eaa325 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 21 Mar 2012 16:21:07 -0300 Subject: edac: use Documentation-nano format for some data structs No functional changes. Just comment improvements. Reviewed-by: Aristeu Rozanski Cc: Doug Thompson Signed-off-by: Mauro Carvalho Chehab --- include/linux/edac.h | 82 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/linux/edac.h b/include/linux/edac.h index ec1b5278b4cc..4e32e8d31e0a 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -49,7 +49,19 @@ static inline void opstate_init(void) #define EDAC_MC_LABEL_LEN 31 #define MC_PROC_NAME_MAX_LEN 7 -/* memory devices */ +/** + * enum dev_type - describe the type of memory DRAM chips used at the stick + * @DEV_UNKNOWN: Can't be determined, or MC doesn't support detect it + * @DEV_X1: 1 bit for data + * @DEV_X2: 2 bits for data + * @DEV_X4: 4 bits for data + * @DEV_X8: 8 bits for data + * @DEV_X16: 16 bits for data + * @DEV_X32: 32 bits for data + * @DEV_X64: 64 bits for data + * + * Typical values are x4 and x8. + */ enum dev_type { DEV_UNKNOWN = 0, DEV_X1, @@ -167,18 +179,30 @@ enum mem_type { #define MEM_FLAG_DDR3 BIT(MEM_DDR3) #define MEM_FLAG_RDDR3 BIT(MEM_RDDR3) -/* chipset Error Detection and Correction capabilities and mode */ +/** + * enum edac-type - Error Detection and Correction capabilities and mode + * @EDAC_UNKNOWN: Unknown if ECC is available + * @EDAC_NONE: Doesn't support ECC + * @EDAC_RESERVED: Reserved ECC type + * @EDAC_PARITY: Detects parity errors + * @EDAC_EC: Error Checking - no correction + * @EDAC_SECDED: Single bit error correction, Double detection + * @EDAC_S2ECD2ED: Chipkill x2 devices - do these exist? + * @EDAC_S4ECD4ED: Chipkill x4 devices + * @EDAC_S8ECD8ED: Chipkill x8 devices + * @EDAC_S16ECD16ED: Chipkill x16 devices + */ enum edac_type { - EDAC_UNKNOWN = 0, /* Unknown if ECC is available */ - EDAC_NONE, /* Doesn't support ECC */ - EDAC_RESERVED, /* Reserved ECC type */ - EDAC_PARITY, /* Detects parity errors */ - EDAC_EC, /* Error Checking - no correction */ - EDAC_SECDED, /* Single bit error correction, Double detection */ - EDAC_S2ECD2ED, /* Chipkill x2 devices - do these exist? */ - EDAC_S4ECD4ED, /* Chipkill x4 devices */ - EDAC_S8ECD8ED, /* Chipkill x8 devices */ - EDAC_S16ECD16ED, /* Chipkill x16 devices */ + EDAC_UNKNOWN = 0, + EDAC_NONE, + EDAC_RESERVED, + EDAC_PARITY, + EDAC_EC, + EDAC_SECDED, + EDAC_S2ECD2ED, + EDAC_S4ECD4ED, + EDAC_S8ECD8ED, + EDAC_S16ECD16ED, }; #define EDAC_FLAG_UNKNOWN BIT(EDAC_UNKNOWN) @@ -191,18 +215,30 @@ enum edac_type { #define EDAC_FLAG_S8ECD8ED BIT(EDAC_S8ECD8ED) #define EDAC_FLAG_S16ECD16ED BIT(EDAC_S16ECD16ED) -/* scrubbing capabilities */ +/** + * enum scrub_type - scrubbing capabilities + * @SCRUB_UNKNOWN Unknown if scrubber is available + * @SCRUB_NONE: No scrubber + * @SCRUB_SW_PROG: SW progressive (sequential) scrubbing + * @SCRUB_SW_SRC: Software scrub only errors + * @SCRUB_SW_PROG_SRC: Progressive software scrub from an error + * @SCRUB_SW_TUNABLE: Software scrub frequency is tunable + * @SCRUB_HW_PROG: HW progressive (sequential) scrubbing + * @SCRUB_HW_SRC: Hardware scrub only errors + * @SCRUB_HW_PROG_SRC: Progressive hardware scrub from an error + * SCRUB_HW_TUNABLE: Hardware scrub frequency is tunable + */ enum scrub_type { - SCRUB_UNKNOWN = 0, /* Unknown if scrubber is available */ - SCRUB_NONE, /* No scrubber */ - SCRUB_SW_PROG, /* SW progressive (sequential) scrubbing */ - SCRUB_SW_SRC, /* Software scrub only errors */ - SCRUB_SW_PROG_SRC, /* Progressive software scrub from an error */ - SCRUB_SW_TUNABLE, /* Software scrub frequency is tunable */ - SCRUB_HW_PROG, /* HW progressive (sequential) scrubbing */ - SCRUB_HW_SRC, /* Hardware scrub only errors */ - SCRUB_HW_PROG_SRC, /* Progressive hardware scrub from an error */ - SCRUB_HW_TUNABLE /* Hardware scrub frequency is tunable */ + SCRUB_UNKNOWN = 0, + SCRUB_NONE, + SCRUB_SW_PROG, + SCRUB_SW_SRC, + SCRUB_SW_PROG_SRC, + SCRUB_SW_TUNABLE, + SCRUB_HW_PROG, + SCRUB_HW_SRC, + SCRUB_HW_PROG_SRC, + SCRUB_HW_TUNABLE }; #define SCRUB_FLAG_SW_PROG BIT(SCRUB_SW_PROG) -- cgit v1.2.3 From 7a623c039075e4ea21648d88133fafa6dcfd113d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 16 Apr 2012 16:41:11 -0300 Subject: edac: rewrite the sysfs code to use struct device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The EDAC subsystem uses the old struct sysdev approach, creating all nodes using the raw sysfs API. This is bad, as the API is deprecated. As we'll be changing the EDAC API, let's first port the existing code to struct device. There's one drawback on this patch: driver-specific sysfs nodes, used by mpc85xx_edac, amd64_edac and i7core_edac won't be created anymore. While it would be possible to also port the device-specific code, that would mix kobj with struct device, with is not recommended. Also, it is easier and nicer to move the code to the drivers, instead, as the core can get rid of some complex logic that just emulates what the device_add() and device_create_file() already does. The next patches will convert the driver-specific code to use the device-specific calls. Then, the remaining bits of the old sysfs API will be removed. NOTE: a per-MC bus is required, otherwise devices with more than one memory controller will hit a bug like the one below: [ 819.094946] EDAC DEBUG: find_mci_by_dev: find_mci_by_dev() [ 819.094948] EDAC DEBUG: edac_create_sysfs_mci_device: edac_create_sysfs_mci_device() idx=1 [ 819.094952] EDAC DEBUG: edac_create_sysfs_mci_device: edac_create_sysfs_mci_device(): creating device mc1 [ 819.094967] EDAC DEBUG: edac_create_sysfs_mci_device: edac_create_sysfs_mci_device creating dimm0, located at channel 0 slot 0 [ 819.094984] ------------[ cut here ]------------ [ 819.100142] WARNING: at fs/sysfs/dir.c:481 sysfs_add_one+0xc1/0xf0() [ 819.107282] Hardware name: S2600CP [ 819.111078] sysfs: cannot create duplicate filename '/bus/edac/devices/dimm0' [ 819.119062] Modules linked in: sb_edac(+) edac_core ip6table_filter ip6_tables ebtable_nat ebtables ipt_MASQUERADE iptable_nat nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_state nf_conntrack ipt_REJECT xt_CHECKSUM iptable_mangle iptable_filter ip_tables bridge stp llc sunrpc binfmt_misc dm_mirror dm_region_hash dm_log vhost_net macvtap macvlan tun kvm microcode pcspkr iTCO_wdt iTCO_vendor_support igb i2c_i801 i2c_core sg ioatdma dca sr_mod cdrom sd_mod crc_t10dif ahci libahci isci libsas libata scsi_transport_sas scsi_mod wmi dm_mod [last unloaded: scsi_wait_scan] [ 819.175748] Pid: 10902, comm: modprobe Not tainted 3.3.0-0.11.el7.v12.2.x86_64 #1 [ 819.184113] Call Trace: [ 819.186868] [] warn_slowpath_common+0x7f/0xc0 [ 819.193573] [] warn_slowpath_fmt+0x46/0x50 [ 819.200000] [] sysfs_add_one+0xc1/0xf0 [ 819.206025] [] sysfs_do_create_link+0x135/0x220 [ 819.212944] [] ? sysfs_create_group+0x13/0x20 [ 819.219656] [] sysfs_create_link+0x13/0x20 [ 819.226109] [] bus_add_device+0xe6/0x1b0 [ 819.232350] [] device_add+0x2db/0x460 [ 819.238300] [] edac_create_dimm_object+0x84/0xf0 [edac_core] [ 819.246460] [] edac_create_sysfs_mci_device+0xe8/0x290 [edac_core] [ 819.255215] [] edac_mc_add_mc+0x5a/0x2c0 [edac_core] [ 819.262611] [] sbridge_register_mci+0x1bc/0x279 [sb_edac] [ 819.270493] [] sbridge_probe+0xef/0x175 [sb_edac] [ 819.277630] [] ? pm_runtime_enable+0x58/0x90 [ 819.284268] [] local_pci_probe+0x5c/0xd0 [ 819.290508] [] __pci_device_probe+0xf1/0x100 [ 819.297117] [] pci_device_probe+0x3a/0x60 [ 819.303457] [] really_probe+0x73/0x270 [ 819.309496] [] driver_probe_device+0x4e/0xb0 [ 819.316104] [] __driver_attach+0xab/0xb0 [ 819.322337] [] ? driver_probe_device+0xb0/0xb0 [ 819.329151] [] bus_for_each_dev+0x56/0x90 [ 819.335489] [] driver_attach+0x1e/0x20 [ 819.341534] [] bus_add_driver+0x1b0/0x2a0 [ 819.347884] [] ? 0xffffffffa0346fff [ 819.353641] [] driver_register+0x76/0x140 [ 819.359980] [] ? printk+0x51/0x53 [ 819.365524] [] ? 0xffffffffa0346fff [ 819.371291] [] __pci_register_driver+0x56/0xd0 [ 819.378096] [] sbridge_init+0x54/0x1000 [sb_edac] [ 819.385231] [] do_one_initcall+0x3f/0x170 [ 819.391577] [] sys_init_module+0xbe/0x230 [ 819.397926] [] system_call_fastpath+0x16/0x1b [ 819.404633] ---[ end trace 1654fdd39556689f ]--- This happens because the bus is not being properly initialized. Instead of putting the memory sub-devices inside the memory controller, it is putting everything under the same directory: $ tree /sys/bus/edac/ /sys/bus/edac/ ├── devices │ ├── all_channel_counts -> ../../../devices/system/edac/mc/mc0/all_channel_counts │ ├── csrow0 -> ../../../devices/system/edac/mc/mc0/csrow0 │ ├── csrow1 -> ../../../devices/system/edac/mc/mc0/csrow1 │ ├── csrow2 -> ../../../devices/system/edac/mc/mc0/csrow2 │ ├── dimm0 -> ../../../devices/system/edac/mc/mc0/dimm0 │ ├── dimm1 -> ../../../devices/system/edac/mc/mc0/dimm1 │ ├── dimm3 -> ../../../devices/system/edac/mc/mc0/dimm3 │ ├── dimm6 -> ../../../devices/system/edac/mc/mc0/dimm6 │ ├── inject_addrmatch -> ../../../devices/system/edac/mc/mc0/inject_addrmatch │ ├── mc -> ../../../devices/system/edac/mc │ └── mc0 -> ../../../devices/system/edac/mc/mc0 ├── drivers ├── drivers_autoprobe ├── drivers_probe └── uevent On a multi-memory controller system, the names "csrow%d" and "dimm%d" should be under "mc%d", and not at the main hierarchy level. So, we need to create a per-MC bus, in order to have its own namespace. Reviewed-by: Aristeu Rozanski Cc: Doug Thompson Cc: Greg K H Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_mc.c | 13 +- drivers/edac/edac_mc_sysfs.c | 1074 ++++++++++++++++-------------------------- drivers/edac/edac_module.c | 13 +- drivers/edac/edac_module.h | 9 +- include/linux/edac.h | 47 +- 5 files changed, 450 insertions(+), 706 deletions(-) (limited to 'include') diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 811f09a38f3a..61ae34643b49 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -218,7 +218,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, unsigned size, tot_dimms = 1, count = 1; unsigned tot_csrows = 1, tot_channels = 1, tot_errcount = 0; void *pvt, *p, *ptr = NULL; - int i, j, err, row, chn, n, len; + int i, j, row, chn, n, len; bool per_rank = false; BUG_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0); @@ -374,15 +374,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, mci->op_state = OP_ALLOC; INIT_LIST_HEAD(&mci->grp_kobj_list); - /* - * Initialize the 'root' kobj for the edac_mc controller - */ - err = edac_mc_register_sysfs_main_kobj(mci); - if (err) { - kfree(mci); - return NULL; - } - /* at this point, the root kobj is valid, and in order to * 'free' the object, then the function: * edac_mc_unregister_sysfs_main_kobj() must be called @@ -403,7 +394,7 @@ void edac_mc_free(struct mem_ctl_info *mci) { debugf1("%s()\n", __func__); - edac_mc_unregister_sysfs_main_kobj(mci); + edac_unregister_sysfs(mci); /* free the mci instance memory here */ kfree(mci); diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 595371941ef9..7002c9cab999 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -7,17 +7,20 @@ * * Written Doug Thompson www.softwarebitmaker.com * + * (c) 2012 - Mauro Carvalho Chehab + * The entire API were re-written, and ported to use struct device + * */ #include #include #include #include +#include #include "edac_core.h" #include "edac_module.h" - /* MC EDAC Controls, setable by module parameter, and sysfs */ static int edac_mc_log_ue = 1; static int edac_mc_log_ce = 1; @@ -78,6 +81,8 @@ module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int, &edac_mc_poll_msec, 0644); MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds"); +static struct device mci_pdev; + /* * various constants for Memory Controllers */ @@ -125,308 +130,336 @@ static const char *edac_caps[] = { [EDAC_S16ECD16ED] = "S16ECD16ED" }; -/* EDAC sysfs CSROW data structures and methods +/* + * EDAC sysfs CSROW data structures and methods + */ + +#define to_csrow(k) container_of(k, struct csrow_info, dev) + +/* + * We need it to avoid namespace conflicts between the legacy API + * and the per-dimm/per-rank one */ +#define DEVICE_ATTR_LEGACY(_name, _mode, _show, _store) \ + struct device_attribute dev_attr_legacy_##_name = __ATTR(_name, _mode, _show, _store) + +struct dev_ch_attribute { + struct device_attribute attr; + int channel; +}; + +#define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \ + struct dev_ch_attribute dev_attr_legacy_##_name = \ + { __ATTR(_name, _mode, _show, _store), (_var) } + +#define to_channel(k) (container_of(k, struct dev_ch_attribute, attr)->channel) /* Set of more default csrow attribute show/store functions */ -static ssize_t csrow_ue_count_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_ue_count_show(struct device *dev, + struct device_attribute *mattr, char *data) { + struct csrow_info *csrow = to_csrow(dev); + return sprintf(data, "%u\n", csrow->ue_count); } -static ssize_t csrow_ce_count_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_ce_count_show(struct device *dev, + struct device_attribute *mattr, char *data) { + struct csrow_info *csrow = to_csrow(dev); + return sprintf(data, "%u\n", csrow->ce_count); } -static ssize_t csrow_size_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_size_show(struct device *dev, + struct device_attribute *mattr, char *data) { + struct csrow_info *csrow = to_csrow(dev); int i; u32 nr_pages = 0; for (i = 0; i < csrow->nr_channels; i++) nr_pages += csrow->channels[i].dimm->nr_pages; - return sprintf(data, "%u\n", PAGES_TO_MiB(nr_pages)); } -static ssize_t csrow_mem_type_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_mem_type_show(struct device *dev, + struct device_attribute *mattr, char *data) { + struct csrow_info *csrow = to_csrow(dev); + return sprintf(data, "%s\n", mem_types[csrow->channels[0].dimm->mtype]); } -static ssize_t csrow_dev_type_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_dev_type_show(struct device *dev, + struct device_attribute *mattr, char *data) { + struct csrow_info *csrow = to_csrow(dev); + return sprintf(data, "%s\n", dev_types[csrow->channels[0].dimm->dtype]); } -static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data, - int private) +static ssize_t csrow_edac_mode_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct csrow_info *csrow = to_csrow(dev); + return sprintf(data, "%s\n", edac_caps[csrow->channels[0].dimm->edac_mode]); } /* show/store functions for DIMM Label attributes */ -static ssize_t channel_dimm_label_show(struct csrow_info *csrow, - char *data, int channel) +static ssize_t channel_dimm_label_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct csrow_info *csrow = to_csrow(dev); + unsigned chan = to_channel(mattr); + struct rank_info *rank = &csrow->channels[chan]; + /* if field has not been initialized, there is nothing to send */ - if (!csrow->channels[channel].dimm->label[0]) + if (!rank->dimm->label[0]) return 0; return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n", - csrow->channels[channel].dimm->label); + rank->dimm->label); } -static ssize_t channel_dimm_label_store(struct csrow_info *csrow, - const char *data, - size_t count, int channel) +static ssize_t channel_dimm_label_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) { + struct csrow_info *csrow = to_csrow(dev); + unsigned chan = to_channel(mattr); + struct rank_info *rank = &csrow->channels[chan]; + ssize_t max_size = 0; max_size = min((ssize_t) count, (ssize_t) EDAC_MC_LABEL_LEN - 1); - strncpy(csrow->channels[channel].dimm->label, data, max_size); - csrow->channels[channel].dimm->label[max_size] = '\0'; + strncpy(rank->dimm->label, data, max_size); + rank->dimm->label[max_size] = '\0'; return max_size; } /* show function for dynamic chX_ce_count attribute */ -static ssize_t channel_ce_count_show(struct csrow_info *csrow, - char *data, int channel) +static ssize_t channel_ce_count_show(struct device *dev, + struct device_attribute *mattr, char *data) { - return sprintf(data, "%u\n", csrow->channels[channel].ce_count); + struct csrow_info *csrow = to_csrow(dev); + unsigned chan = to_channel(mattr); + struct rank_info *rank = &csrow->channels[chan]; + + return sprintf(data, "%u\n", rank->ce_count); } -/* csrow specific attribute structure */ -struct csrowdev_attribute { - struct attribute attr; - ssize_t(*show) (struct csrow_info *, char *, int); - ssize_t(*store) (struct csrow_info *, const char *, size_t, int); - int private; -}; +/* cwrow/attribute files */ +DEVICE_ATTR_LEGACY(size_mb, S_IRUGO, csrow_size_show, NULL); +DEVICE_ATTR_LEGACY(dev_type, S_IRUGO, csrow_dev_type_show, NULL); +DEVICE_ATTR_LEGACY(mem_type, S_IRUGO, csrow_mem_type_show, NULL); +DEVICE_ATTR_LEGACY(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL); +DEVICE_ATTR_LEGACY(ue_count, S_IRUGO, csrow_ue_count_show, NULL); +DEVICE_ATTR_LEGACY(ce_count, S_IRUGO, csrow_ce_count_show, NULL); -#define to_csrow(k) container_of(k, struct csrow_info, kobj) -#define to_csrowdev_attr(a) container_of(a, struct csrowdev_attribute, attr) +/* default attributes of the CSROW object */ +static struct attribute *csrow_attrs[] = { + &dev_attr_legacy_dev_type.attr, + &dev_attr_legacy_mem_type.attr, + &dev_attr_legacy_edac_mode.attr, + &dev_attr_legacy_size_mb.attr, + &dev_attr_legacy_ue_count.attr, + &dev_attr_legacy_ce_count.attr, + NULL, +}; -/* Set of show/store higher level functions for default csrow attributes */ -static ssize_t csrowdev_show(struct kobject *kobj, - struct attribute *attr, char *buffer) -{ - struct csrow_info *csrow = to_csrow(kobj); - struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); +static struct attribute_group csrow_attr_grp = { + .attrs = csrow_attrs, +}; - if (csrowdev_attr->show) - return csrowdev_attr->show(csrow, - buffer, csrowdev_attr->private); - return -EIO; -} +static const struct attribute_group *csrow_attr_groups[] = { + &csrow_attr_grp, + NULL +}; -static ssize_t csrowdev_store(struct kobject *kobj, struct attribute *attr, - const char *buffer, size_t count) +static void csrow_attr_release(struct device *device) { - struct csrow_info *csrow = to_csrow(kobj); - struct csrowdev_attribute *csrowdev_attr = to_csrowdev_attr(attr); - - if (csrowdev_attr->store) - return csrowdev_attr->store(csrow, - buffer, - count, csrowdev_attr->private); - return -EIO; + debugf1("Releasing csrow device %s\n", dev_name(device)); } -static const struct sysfs_ops csrowfs_ops = { - .show = csrowdev_show, - .store = csrowdev_store +static struct device_type csrow_attr_type = { + .groups = csrow_attr_groups, + .release = csrow_attr_release, }; -#define CSROWDEV_ATTR(_name,_mode,_show,_store,_private) \ -static struct csrowdev_attribute attr_##_name = { \ - .attr = {.name = __stringify(_name), .mode = _mode }, \ - .show = _show, \ - .store = _store, \ - .private = _private, \ -}; - -/* default cwrow/attribute files */ -CSROWDEV_ATTR(size_mb, S_IRUGO, csrow_size_show, NULL, 0); -CSROWDEV_ATTR(dev_type, S_IRUGO, csrow_dev_type_show, NULL, 0); -CSROWDEV_ATTR(mem_type, S_IRUGO, csrow_mem_type_show, NULL, 0); -CSROWDEV_ATTR(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL, 0); -CSROWDEV_ATTR(ue_count, S_IRUGO, csrow_ue_count_show, NULL, 0); -CSROWDEV_ATTR(ce_count, S_IRUGO, csrow_ce_count_show, NULL, 0); +/* + * possible dynamic channel DIMM Label attribute files + * + */ -/* default attributes of the CSROW object */ -static struct csrowdev_attribute *default_csrow_attr[] = { - &attr_dev_type, - &attr_mem_type, - &attr_edac_mode, - &attr_size_mb, - &attr_ue_count, - &attr_ce_count, - NULL, -}; +#define EDAC_NR_CHANNELS 6 -/* possible dynamic channel DIMM Label attribute files */ -CSROWDEV_ATTR(ch0_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch0_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 0); -CSROWDEV_ATTR(ch1_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch1_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 1); -CSROWDEV_ATTR(ch2_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch2_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 2); -CSROWDEV_ATTR(ch3_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch3_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 3); -CSROWDEV_ATTR(ch4_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch4_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 4); -CSROWDEV_ATTR(ch5_dimm_label, S_IRUGO | S_IWUSR, +DEVICE_CHANNEL(ch5_dimm_label, S_IRUGO | S_IWUSR, channel_dimm_label_show, channel_dimm_label_store, 5); /* Total possible dynamic DIMM Label attribute file table */ -static struct csrowdev_attribute *dynamic_csrow_dimm_attr[] = { - &attr_ch0_dimm_label, - &attr_ch1_dimm_label, - &attr_ch2_dimm_label, - &attr_ch3_dimm_label, - &attr_ch4_dimm_label, - &attr_ch5_dimm_label +static struct device_attribute *dynamic_csrow_dimm_attr[] = { + &dev_attr_legacy_ch0_dimm_label.attr, + &dev_attr_legacy_ch1_dimm_label.attr, + &dev_attr_legacy_ch2_dimm_label.attr, + &dev_attr_legacy_ch3_dimm_label.attr, + &dev_attr_legacy_ch4_dimm_label.attr, + &dev_attr_legacy_ch5_dimm_label.attr }; /* possible dynamic channel ce_count attribute files */ -CSROWDEV_ATTR(ch0_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 0); -CSROWDEV_ATTR(ch1_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 1); -CSROWDEV_ATTR(ch2_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 2); -CSROWDEV_ATTR(ch3_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 3); -CSROWDEV_ATTR(ch4_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 4); -CSROWDEV_ATTR(ch5_ce_count, S_IRUGO | S_IWUSR, channel_ce_count_show, NULL, 5); +DEVICE_CHANNEL(ch0_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 0); +DEVICE_CHANNEL(ch1_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 1); +DEVICE_CHANNEL(ch2_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 2); +DEVICE_CHANNEL(ch3_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 3); +DEVICE_CHANNEL(ch4_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 4); +DEVICE_CHANNEL(ch5_ce_count, S_IRUGO | S_IWUSR, + channel_ce_count_show, NULL, 5); /* Total possible dynamic ce_count attribute file table */ -static struct csrowdev_attribute *dynamic_csrow_ce_count_attr[] = { - &attr_ch0_ce_count, - &attr_ch1_ce_count, - &attr_ch2_ce_count, - &attr_ch3_ce_count, - &attr_ch4_ce_count, - &attr_ch5_ce_count +static struct device_attribute *dynamic_csrow_ce_count_attr[] = { + &dev_attr_legacy_ch0_ce_count.attr, + &dev_attr_legacy_ch1_ce_count.attr, + &dev_attr_legacy_ch2_ce_count.attr, + &dev_attr_legacy_ch3_ce_count.attr, + &dev_attr_legacy_ch4_ce_count.attr, + &dev_attr_legacy_ch5_ce_count.attr }; -#define EDAC_NR_CHANNELS 6 - -/* Create dynamic CHANNEL files, indexed by 'chan', under specifed CSROW */ -static int edac_create_channel_files(struct kobject *kobj, int chan) +/* Create a CSROW object under specifed edac_mc_device */ +static int edac_create_csrow_object(struct mem_ctl_info *mci, + struct csrow_info *csrow, int index) { - int err = -ENODEV; + int err, chan; - if (chan >= EDAC_NR_CHANNELS) - return err; + if (csrow->nr_channels >= EDAC_NR_CHANNELS) + return -ENODEV; - /* create the DIMM label attribute file */ - err = sysfs_create_file(kobj, - (struct attribute *) - dynamic_csrow_dimm_attr[chan]); - - if (!err) { - /* create the CE Count attribute file */ - err = sysfs_create_file(kobj, - (struct attribute *) - dynamic_csrow_ce_count_attr[chan]); - } else { - debugf1("%s() dimm labels and ce_count files created", - __func__); - } + csrow->dev.type = &csrow_attr_type; + csrow->dev.bus = &mci->bus; + device_initialize(&csrow->dev); + csrow->dev.parent = &mci->dev; + dev_set_name(&csrow->dev, "csrow%d", index); + dev_set_drvdata(&csrow->dev, csrow); - return err; -} + debugf0("%s(): creating (virtual) csrow node %s\n", __func__, + dev_name(&csrow->dev)); -/* No memory to release for this kobj */ -static void edac_csrow_instance_release(struct kobject *kobj) -{ - struct mem_ctl_info *mci; - struct csrow_info *cs; + err = device_add(&csrow->dev); + if (err < 0) + return err; - debugf1("%s()\n", __func__); + for (chan = 0; chan < csrow->nr_channels; chan++) { + err = device_create_file(&csrow->dev, + dynamic_csrow_dimm_attr[chan]); + if (err < 0) + goto error; + err = device_create_file(&csrow->dev, + dynamic_csrow_ce_count_attr[chan]); + if (err < 0) { + device_remove_file(&csrow->dev, + dynamic_csrow_dimm_attr[chan]); + goto error; + } + } - cs = container_of(kobj, struct csrow_info, kobj); - mci = cs->mci; + return 0; - kobject_put(&mci->edac_mci_kobj); -} +error: + for (--chan; chan >= 0; chan--) { + device_remove_file(&csrow->dev, + dynamic_csrow_dimm_attr[chan]); + device_remove_file(&csrow->dev, + dynamic_csrow_ce_count_attr[chan]); + } + put_device(&csrow->dev); -/* the kobj_type instance for a CSROW */ -static struct kobj_type ktype_csrow = { - .release = edac_csrow_instance_release, - .sysfs_ops = &csrowfs_ops, - .default_attrs = (struct attribute **)default_csrow_attr, -}; + return err; +} /* Create a CSROW object under specifed edac_mc_device */ -static int edac_create_csrow_object(struct mem_ctl_info *mci, - struct csrow_info *csrow, int index) +static int edac_create_csrow_objects(struct mem_ctl_info *mci) { - struct kobject *kobj_mci = &mci->edac_mci_kobj; - struct kobject *kobj; - int chan; - int err; + int err, i, chan; + struct csrow_info *csrow; - /* generate ..../edac/mc/mc/csrow */ - memset(&csrow->kobj, 0, sizeof(csrow->kobj)); - csrow->mci = mci; /* include container up link */ + for (i = 0; i < mci->nr_csrows; i++) { + err = edac_create_csrow_object(mci, &mci->csrows[i], i); + if (err < 0) + goto error; + } + return 0; - /* bump the mci instance's kobject's ref count */ - kobj = kobject_get(&mci->edac_mci_kobj); - if (!kobj) { - err = -ENODEV; - goto err_out; +error: + for (--i; i >= 0; i--) { + csrow = &mci->csrows[i]; + for (chan = csrow->nr_channels - 1; chan >= 0; chan--) { + device_remove_file(&csrow->dev, + dynamic_csrow_dimm_attr[chan]); + device_remove_file(&csrow->dev, + dynamic_csrow_ce_count_attr[chan]); + } + put_device(&mci->csrows[i].dev); } - /* Instanstiate the csrow object */ - err = kobject_init_and_add(&csrow->kobj, &ktype_csrow, kobj_mci, - "csrow%d", index); - if (err) - goto err_release_top_kobj; + return err; +} - /* At this point, to release a csrow kobj, one must - * call the kobject_put and allow that tear down - * to work the releasing - */ +static void edac_delete_csrow_objects(struct mem_ctl_info *mci) +{ + int i, chan; + struct csrow_info *csrow; - /* Create the dyanmic attribute files on this csrow, - * namely, the DIMM labels and the channel ce_count - */ - for (chan = 0; chan < csrow->nr_channels; chan++) { - err = edac_create_channel_files(&csrow->kobj, chan); - if (err) { - /* special case the unregister here */ - kobject_put(&csrow->kobj); - goto err_out; + for (i = mci->nr_csrows - 1; i >= 0; i--) { + csrow = &mci->csrows[i]; + for (chan = csrow->nr_channels - 1; chan >= 0; chan--) { + debugf1("Removing csrow %d channel %d sysfs nodes\n", + i, chan); + device_remove_file(&csrow->dev, + dynamic_csrow_dimm_attr[chan]); + device_remove_file(&csrow->dev, + dynamic_csrow_ce_count_attr[chan]); } + put_device(&mci->csrows[i].dev); + device_del(&mci->csrows[i].dev); } - kobject_uevent(&csrow->kobj, KOBJ_ADD); - return 0; - - /* error unwind stack */ -err_release_top_kobj: - kobject_put(&mci->edac_mci_kobj); - -err_out: - return err; } -/* default sysfs methods and data structures for the main MCI kobject */ +/* + * Memory controller device + */ + +#define to_mci(k) container_of(k, struct mem_ctl_info, dev) -static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, +static ssize_t mci_reset_counters_store(struct device *dev, + struct device_attribute *mattr, const char *data, size_t count) { - int row, chan; - - mci->ue_noinfo_count = 0; - mci->ce_noinfo_count = 0; + struct mem_ctl_info *mci = to_mci(dev); + int cnt, row, chan, i; mci->ue_mc = 0; mci->ce_mc = 0; + mci->ue_noinfo_count = 0; + mci->ce_noinfo_count = 0; for (row = 0; row < mci->nr_csrows; row++) { struct csrow_info *ri = &mci->csrows[row]; @@ -438,6 +471,13 @@ static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, ri->channels[chan].ce_count = 0; } + cnt = 1; + for (i = 0; i < mci->n_layers; i++) { + cnt *= mci->layers[i].size; + memset(mci->ce_per_layer[i], 0, cnt * sizeof(u32)); + memset(mci->ue_per_layer[i], 0, cnt * sizeof(u32)); + } + mci->start_time = jiffies; return count; } @@ -451,9 +491,11 @@ static ssize_t mci_reset_counters_store(struct mem_ctl_info *mci, * Negative value still means that an error has occurred while setting * the scrub rate. */ -static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, +static ssize_t mci_sdram_scrub_rate_store(struct device *dev, + struct device_attribute *mattr, const char *data, size_t count) { + struct mem_ctl_info *mci = to_mci(dev); unsigned long bandwidth = 0; int new_bw = 0; @@ -476,8 +518,11 @@ static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci, /* * ->get_sdram_scrub_rate() return value semantics same as above. */ -static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_sdram_scrub_rate_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); int bandwidth = 0; if (!mci->get_sdram_scrub_rate) @@ -493,38 +538,65 @@ static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data) } /* default attribute files for the MCI object */ -static ssize_t mci_ue_count_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_ue_count_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%d\n", mci->ue_mc); } -static ssize_t mci_ce_count_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_ce_count_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%d\n", mci->ce_mc); } -static ssize_t mci_ce_noinfo_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_ce_noinfo_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%d\n", mci->ce_noinfo_count); } -static ssize_t mci_ue_noinfo_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_ue_noinfo_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%d\n", mci->ue_noinfo_count); } -static ssize_t mci_seconds_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_seconds_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%ld\n", (jiffies - mci->start_time) / HZ); } -static ssize_t mci_ctl_name_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_ctl_name_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); + return sprintf(data, "%s\n", mci->ctl_name); } -static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) +static ssize_t mci_size_mb_show(struct device *dev, + struct device_attribute *mattr, + char *data) { + struct mem_ctl_info *mci = to_mci(dev); int total_pages = 0, csrow_idx, j; for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) { @@ -540,360 +612,53 @@ static ssize_t mci_size_mb_show(struct mem_ctl_info *mci, char *data) return sprintf(data, "%u\n", PAGES_TO_MiB(total_pages)); } -#define to_mci(k) container_of(k, struct mem_ctl_info, edac_mci_kobj) -#define to_mcidev_attr(a) container_of(a,struct mcidev_sysfs_attribute,attr) - -/* MCI show/store functions for top most object */ -static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr, - char *buffer) -{ - struct mem_ctl_info *mem_ctl_info = to_mci(kobj); - struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); - - debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); - - if (mcidev_attr->show) - return mcidev_attr->show(mem_ctl_info, buffer); - - return -EIO; -} - -static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr, - const char *buffer, size_t count) -{ - struct mem_ctl_info *mem_ctl_info = to_mci(kobj); - struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); - - debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); - - if (mcidev_attr->store) - return mcidev_attr->store(mem_ctl_info, buffer, count); - - return -EIO; -} - -/* Intermediate show/store table */ -static const struct sysfs_ops mci_ops = { - .show = mcidev_show, - .store = mcidev_store -}; - -#define MCIDEV_ATTR(_name,_mode,_show,_store) \ -static struct mcidev_sysfs_attribute mci_attr_##_name = { \ - .attr = {.name = __stringify(_name), .mode = _mode }, \ - .show = _show, \ - .store = _store, \ -}; - /* default Control file */ -MCIDEV_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); +DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); /* default Attribute files */ -MCIDEV_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); -MCIDEV_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); -MCIDEV_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); -MCIDEV_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); -MCIDEV_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); -MCIDEV_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); -MCIDEV_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); +DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); +DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); +DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); +DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); +DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); +DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); +DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); /* memory scrubber attribute file */ -MCIDEV_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show, +DEVICE_ATTR(sdram_scrub_rate, S_IRUGO | S_IWUSR, mci_sdram_scrub_rate_show, mci_sdram_scrub_rate_store); -static struct mcidev_sysfs_attribute *mci_attr[] = { - &mci_attr_reset_counters, - &mci_attr_mc_name, - &mci_attr_size_mb, - &mci_attr_seconds_since_reset, - &mci_attr_ue_noinfo_count, - &mci_attr_ce_noinfo_count, - &mci_attr_ue_count, - &mci_attr_ce_count, - &mci_attr_sdram_scrub_rate, +static struct attribute *mci_attrs[] = { + &dev_attr_reset_counters.attr, + &dev_attr_mc_name.attr, + &dev_attr_size_mb.attr, + &dev_attr_seconds_since_reset.attr, + &dev_attr_ue_noinfo_count.attr, + &dev_attr_ce_noinfo_count.attr, + &dev_attr_ue_count.attr, + &dev_attr_ce_count.attr, + &dev_attr_sdram_scrub_rate.attr, NULL }; - -/* - * Release of a MC controlling instance - * - * each MC control instance has the following resources upon entry: - * a) a ref count on the top memctl kobj - * b) a ref count on this module - * - * this function must decrement those ref counts and then - * issue a free on the instance's memory - */ -static void edac_mci_control_release(struct kobject *kobj) -{ - struct mem_ctl_info *mci; - - mci = to_mci(kobj); - - debugf0("%s() mci instance idx=%d releasing\n", __func__, mci->mc_idx); - - /* decrement the module ref count */ - module_put(mci->owner); -} - -static struct kobj_type ktype_mci = { - .release = edac_mci_control_release, - .sysfs_ops = &mci_ops, - .default_attrs = (struct attribute **)mci_attr, -}; - -/* EDAC memory controller sysfs kset: - * /sys/devices/system/edac/mc - */ -static struct kset *mc_kset; - -/* - * edac_mc_register_sysfs_main_kobj - * - * setups and registers the main kobject for each mci - */ -int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci) -{ - struct kobject *kobj_mci; - int err; - - debugf1("%s()\n", __func__); - - kobj_mci = &mci->edac_mci_kobj; - - /* Init the mci's kobject */ - memset(kobj_mci, 0, sizeof(*kobj_mci)); - - /* Record which module 'owns' this control structure - * and bump the ref count of the module - */ - mci->owner = THIS_MODULE; - - /* bump ref count on this module */ - if (!try_module_get(mci->owner)) { - err = -ENODEV; - goto fail_out; - } - - /* this instance become part of the mc_kset */ - kobj_mci->kset = mc_kset; - - /* register the mc kobject to the mc_kset */ - err = kobject_init_and_add(kobj_mci, &ktype_mci, NULL, - "mc%d", mci->mc_idx); - if (err) { - debugf1("%s()Failed to register '.../edac/mc%d'\n", - __func__, mci->mc_idx); - goto kobj_reg_fail; - } - kobject_uevent(kobj_mci, KOBJ_ADD); - - /* At this point, to 'free' the control struct, - * edac_mc_unregister_sysfs_main_kobj() must be used - */ - - debugf1("%s() Registered '.../edac/mc%d' kobject\n", - __func__, mci->mc_idx); - - return 0; - - /* Error exit stack */ - -kobj_reg_fail: - module_put(mci->owner); - -fail_out: - return err; -} - -/* - * edac_mc_register_sysfs_main_kobj - * - * tears down and the main mci kobject from the mc_kset - */ -void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci) -{ - debugf1("%s()\n", __func__); - - /* delete the kobj from the mc_kset */ - kobject_put(&mci->edac_mci_kobj); -} - -#define EDAC_DEVICE_SYMLINK "device" - -#define grp_to_mci(k) (container_of(k, struct mcidev_sysfs_group_kobj, kobj)->mci) - -/* MCI show/store functions for top most object */ -static ssize_t inst_grp_show(struct kobject *kobj, struct attribute *attr, - char *buffer) -{ - struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj); - struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); - - debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); - - if (mcidev_attr->show) - return mcidev_attr->show(mem_ctl_info, buffer); - - return -EIO; -} - -static ssize_t inst_grp_store(struct kobject *kobj, struct attribute *attr, - const char *buffer, size_t count) -{ - struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj); - struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); - - debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); - - if (mcidev_attr->store) - return mcidev_attr->store(mem_ctl_info, buffer, count); - - return -EIO; -} - -/* No memory to release for this kobj */ -static void edac_inst_grp_release(struct kobject *kobj) -{ - struct mcidev_sysfs_group_kobj *grp; - struct mem_ctl_info *mci; - - debugf1("%s()\n", __func__); - - grp = container_of(kobj, struct mcidev_sysfs_group_kobj, kobj); - mci = grp->mci; -} - -/* Intermediate show/store table */ -static struct sysfs_ops inst_grp_ops = { - .show = inst_grp_show, - .store = inst_grp_store +static struct attribute_group mci_attr_grp = { + .attrs = mci_attrs, }; -/* the kobj_type instance for a instance group */ -static struct kobj_type ktype_inst_grp = { - .release = edac_inst_grp_release, - .sysfs_ops = &inst_grp_ops, +static const struct attribute_group *mci_attr_groups[] = { + &mci_attr_grp, + NULL }; - -/* - * edac_create_mci_instance_attributes - * create MC driver specific attributes bellow an specified kobj - * This routine calls itself recursively, in order to create an entire - * object tree. - */ -static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, - const struct mcidev_sysfs_attribute *sysfs_attrib, - struct kobject *kobj) +static void mci_attr_release(struct device *device) { - int err; - - debugf4("%s()\n", __func__); - - while (sysfs_attrib) { - debugf4("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib); - if (sysfs_attrib->grp) { - struct mcidev_sysfs_group_kobj *grp_kobj; - - grp_kobj = kzalloc(sizeof(*grp_kobj), GFP_KERNEL); - if (!grp_kobj) - return -ENOMEM; - - grp_kobj->grp = sysfs_attrib->grp; - grp_kobj->mci = mci; - list_add_tail(&grp_kobj->list, &mci->grp_kobj_list); - - debugf0("%s() grp %s, mci %p\n", __func__, - sysfs_attrib->grp->name, mci); - - err = kobject_init_and_add(&grp_kobj->kobj, - &ktype_inst_grp, - &mci->edac_mci_kobj, - sysfs_attrib->grp->name); - if (err < 0) { - printk(KERN_ERR "kobject_init_and_add failed: %d\n", err); - return err; - } - err = edac_create_mci_instance_attributes(mci, - grp_kobj->grp->mcidev_attr, - &grp_kobj->kobj); - - if (err < 0) - return err; - } else if (sysfs_attrib->attr.name) { - debugf4("%s() file %s\n", __func__, - sysfs_attrib->attr.name); - - err = sysfs_create_file(kobj, &sysfs_attrib->attr); - if (err < 0) { - printk(KERN_ERR "sysfs_create_file failed: %d\n", err); - return err; - } - } else - break; - - sysfs_attrib++; - } - - return 0; + debugf1("Releasing mci device %s\n", dev_name(device)); } -/* - * edac_remove_mci_instance_attributes - * remove MC driver specific attributes at the topmost level - * directory of this mci instance. - */ -static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, - const struct mcidev_sysfs_attribute *sysfs_attrib, - struct kobject *kobj, int count) -{ - struct mcidev_sysfs_group_kobj *grp_kobj, *tmp; - - debugf1("%s()\n", __func__); - - /* - * loop if there are attributes and until we hit a NULL entry - * Remove first all the attributes - */ - while (sysfs_attrib) { - debugf4("%s() sysfs_attrib = %p\n",__func__, sysfs_attrib); - if (sysfs_attrib->grp) { - debugf4("%s() seeking for group %s\n", - __func__, sysfs_attrib->grp->name); - list_for_each_entry(grp_kobj, - &mci->grp_kobj_list, list) { - debugf4("%s() grp_kobj->grp = %p\n",__func__, grp_kobj->grp); - if (grp_kobj->grp == sysfs_attrib->grp) { - edac_remove_mci_instance_attributes(mci, - grp_kobj->grp->mcidev_attr, - &grp_kobj->kobj, count + 1); - debugf4("%s() group %s\n", __func__, - sysfs_attrib->grp->name); - kobject_put(&grp_kobj->kobj); - } - } - debugf4("%s() end of seeking for group %s\n", - __func__, sysfs_attrib->grp->name); - } else if (sysfs_attrib->attr.name) { - debugf4("%s() file %s\n", __func__, - sysfs_attrib->attr.name); - sysfs_remove_file(kobj, &sysfs_attrib->attr); - } else - break; - sysfs_attrib++; - } - - /* Remove the group objects */ - if (count) - return; - list_for_each_entry_safe(grp_kobj, tmp, - &mci->grp_kobj_list, list) { - list_del(&grp_kobj->list); - kfree(grp_kobj); - } -} +static struct device_type mci_attr_type = { + .groups = mci_attr_groups, + .release = mci_attr_release, +}; /* @@ -906,77 +671,80 @@ static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, */ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) { - int i, j; - int err; - struct csrow_info *csrow; - struct kobject *kobj_mci = &mci->edac_mci_kobj; + int i, err; debugf0("%s() idx=%d\n", __func__, mci->mc_idx); - INIT_LIST_HEAD(&mci->grp_kobj_list); + /* get the /sys/devices/system/edac subsys reference */ - /* create a symlink for the device */ - err = sysfs_create_link(kobj_mci, &mci->pdev->kobj, - EDAC_DEVICE_SYMLINK); - if (err) { - debugf1("%s() failure to create symlink\n", __func__); - goto fail0; - } + mci->dev.type = &mci_attr_type; + device_initialize(&mci->dev); - /* If the low level driver desires some attributes, - * then create them now for the driver. + mci->dev.parent = &mci_pdev; + mci->dev.bus = &mci->bus; + dev_set_name(&mci->dev, "mc%d", mci->mc_idx); + dev_set_drvdata(&mci->dev, mci); + pm_runtime_forbid(&mci->dev); + + /* + * The memory controller needs its own bus, in order to avoid + * namespace conflicts at /sys/bus/edac. */ - if (mci->mc_driver_sysfs_attributes) { - err = edac_create_mci_instance_attributes(mci, - mci->mc_driver_sysfs_attributes, - &mci->edac_mci_kobj); - if (err) { - debugf1("%s() failure to create mci attributes\n", - __func__); - goto fail0; - } + debugf0("creating bus %s\n",mci->bus.name); + mci->bus.name = kstrdup(dev_name(&mci->dev), GFP_KERNEL); + err = bus_register(&mci->bus); + if (err < 0) + return err; + + debugf0("%s(): creating device %s\n", __func__, + dev_name(&mci->dev)); + err = device_add(&mci->dev); + if (err < 0) { + bus_unregister(&mci->bus); + kfree(mci->bus.name); + return err; } - /* Make directories for each CSROW object under the mc kobject + /* + * Create the dimm/rank devices */ - for (i = 0; i < mci->nr_csrows; i++) { - int nr_pages = 0; - - csrow = &mci->csrows[i]; - for (j = 0; j < csrow->nr_channels; j++) - nr_pages += csrow->channels[j].dimm->nr_pages; - - if (nr_pages > 0) { - err = edac_create_csrow_object(mci, csrow, i); - if (err) { - debugf1("%s() failure: create csrow %d obj\n", - __func__, i); - goto fail1; - } + for (i = 0; i < mci->tot_dimms; i++) { + struct dimm_info *dimm = &mci->dimms[i]; + /* Only expose populated DIMMs */ + if (dimm->nr_pages == 0) + continue; +#ifdef CONFIG_EDAC_DEBUG + debugf1("%s creating dimm%d, located at ", + __func__, i); + if (edac_debug_level >= 1) { + int lay; + for (lay = 0; lay < mci->n_layers; lay++) + printk(KERN_CONT "%s %d ", + edac_layer_name[mci->layers[lay].type], + dimm->location[lay]); + printk(KERN_CONT "\n"); } +#endif } + err = edac_create_csrow_objects(mci); + if (err < 0) + goto fail; + return 0; -fail1: +fail: for (i--; i >= 0; i--) { - int nr_pages = 0; - - csrow = &mci->csrows[i]; - for (j = 0; j < csrow->nr_channels; j++) - nr_pages += csrow->channels[j].dimm->nr_pages; - if (nr_pages > 0) - kobject_put(&mci->csrows[i].kobj); + struct dimm_info *dimm = &mci->dimms[i]; + if (dimm->nr_pages == 0) + continue; + put_device(&dimm->dev); + device_del(&dimm->dev); } - - /* remove the mci instance's attributes, if any */ - edac_remove_mci_instance_attributes(mci, - mci->mc_driver_sysfs_attributes, &mci->edac_mci_kobj, 0); - - /* remove the symlink */ - sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK); - -fail0: + put_device(&mci->dev); + device_del(&mci->dev); + bus_unregister(&mci->bus); + kfree(mci->bus.name); return err; } @@ -985,98 +753,70 @@ fail0: */ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) { - struct csrow_info *csrow; - int i, j; + int i; debugf0("%s()\n", __func__); - /* remove all csrow kobjects */ - debugf4("%s() unregister this mci kobj\n", __func__); - for (i = 0; i < mci->nr_csrows; i++) { - int nr_pages = 0; - - csrow = &mci->csrows[i]; - for (j = 0; j < csrow->nr_channels; j++) - nr_pages += csrow->channels[j].dimm->nr_pages; - if (nr_pages > 0) { - debugf0("%s() unreg csrow-%d\n", __func__, i); - kobject_put(&mci->csrows[i].kobj); - } - } + edac_delete_csrow_objects(mci); - /* remove this mci instance's attribtes */ - if (mci->mc_driver_sysfs_attributes) { - debugf4("%s() unregister mci private attributes\n", __func__); - edac_remove_mci_instance_attributes(mci, - mci->mc_driver_sysfs_attributes, - &mci->edac_mci_kobj, 0); + for (i = 0; i < mci->tot_dimms; i++) { + struct dimm_info *dimm = &mci->dimms[i]; + if (dimm->nr_pages == 0) + continue; + debugf0("%s(): removing device %s\n", __func__, + dev_name(&dimm->dev)); + put_device(&dimm->dev); + device_del(&dimm->dev); } - - /* remove the symlink */ - debugf4("%s() remove_link\n", __func__); - sysfs_remove_link(&mci->edac_mci_kobj, EDAC_DEVICE_SYMLINK); - - /* unregister this instance's kobject */ - debugf4("%s() remove_mci_instance\n", __func__); - kobject_put(&mci->edac_mci_kobj); } +void edac_unregister_sysfs(struct mem_ctl_info *mci) +{ + debugf1("Unregistering device %s\n", dev_name(&mci->dev)); + put_device(&mci->dev); + device_del(&mci->dev); + bus_unregister(&mci->bus); + kfree(mci->bus.name); +} +static void mc_attr_release(struct device *device) +{ + debugf1("Releasing device %s\n", dev_name(device)); +} - +static struct device_type mc_attr_type = { + .release = mc_attr_release, +}; /* - * edac_setup_sysfs_mc_kset(void) - * - * Initialize the mc_kset for the 'mc' entry - * This requires creating the top 'mc' directory with a kset - * and its controls/attributes. - * - * To this 'mc' kset, instance 'mci' will be grouped as children. - * - * Return: 0 SUCCESS - * !0 FAILURE error code + * Init/exit code for the module. Basically, creates/removes /sys/class/rc */ -int edac_sysfs_setup_mc_kset(void) +int __init edac_mc_sysfs_init(void) { - int err = -EINVAL; struct bus_type *edac_subsys; - - debugf1("%s()\n", __func__); + int err; /* get the /sys/devices/system/edac subsys reference */ edac_subsys = edac_get_sysfs_subsys(); if (edac_subsys == NULL) { - debugf1("%s() no edac_subsys error=%d\n", __func__, err); - goto fail_out; + debugf1("%s() no edac_subsys\n", __func__); + return -EINVAL; } - /* Init the MC's kobject */ - mc_kset = kset_create_and_add("mc", NULL, &edac_subsys->dev_root->kobj); - if (!mc_kset) { - err = -ENOMEM; - debugf1("%s() Failed to register '.../edac/mc'\n", __func__); - goto fail_kset; - } + mci_pdev.bus = edac_subsys; + mci_pdev.type = &mc_attr_type; + device_initialize(&mci_pdev); + dev_set_name(&mci_pdev, "mc"); - debugf1("%s() Registered '.../edac/mc' kobject\n", __func__); + err = device_add(&mci_pdev); + if (err < 0) + return err; return 0; - -fail_kset: - edac_put_sysfs_subsys(); - -fail_out: - return err; } -/* - * edac_sysfs_teardown_mc_kset - * - * deconstruct the mc_ket for memory controllers - */ -void edac_sysfs_teardown_mc_kset(void) +void __exit edac_mc_sysfs_exit(void) { - kset_unregister(mc_kset); + put_device(&mci_pdev); + device_del(&mci_pdev); edac_put_sysfs_subsys(); } - diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c index 5ddaa86d6a6e..8735a0d3ed0c 100644 --- a/drivers/edac/edac_module.c +++ b/drivers/edac/edac_module.c @@ -90,10 +90,7 @@ static int __init edac_init(void) */ edac_pci_clear_parity_errors(); - /* - * now set up the mc_kset under the edac class object - */ - err = edac_sysfs_setup_mc_kset(); + err = edac_mc_sysfs_init(); if (err) goto error; @@ -101,15 +98,11 @@ static int __init edac_init(void) err = edac_workqueue_setup(); if (err) { edac_printk(KERN_ERR, EDAC_MC, "init WorkQueue failure\n"); - goto workq_fail; + goto error; } return 0; - /* Error teardown stack */ -workq_fail: - edac_sysfs_teardown_mc_kset(); - error: return err; } @@ -124,7 +117,7 @@ static void __exit edac_exit(void) /* tear down the various subsystems */ edac_workqueue_teardown(); - edac_sysfs_teardown_mc_kset(); + edac_mc_sysfs_exit(); } /* diff --git a/drivers/edac/edac_module.h b/drivers/edac/edac_module.h index 0ea7d14cb930..1af13676e857 100644 --- a/drivers/edac/edac_module.h +++ b/drivers/edac/edac_module.h @@ -19,12 +19,12 @@ * * edac_mc objects */ -extern int edac_sysfs_setup_mc_kset(void); -extern void edac_sysfs_teardown_mc_kset(void); -extern int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci); -extern void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci); + /* on edac_mc_sysfs.c */ +int edac_mc_sysfs_init(void); +void edac_mc_sysfs_exit(void); extern int edac_create_sysfs_mci_device(struct mem_ctl_info *mci); extern void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci); +void edac_unregister_sysfs(struct mem_ctl_info *mci); extern int edac_get_log_ue(void); extern int edac_get_log_ce(void); extern int edac_get_panic_on_ue(void); @@ -34,6 +34,7 @@ extern int edac_mc_get_panic_on_ue(void); extern int edac_get_poll_msec(void); extern int edac_mc_get_poll_msec(void); + /* on edac_device.c */ extern int edac_device_register_sysfs_main_kobj( struct edac_device_ctl_info *edac_dev); extern void edac_device_unregister_sysfs_main_kobj( diff --git a/include/linux/edac.h b/include/linux/edac.h index 4e32e8d31e0a..a2b0b6fc002c 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -13,6 +13,7 @@ #define _LINUX_EDAC_H_ #include +#include #include #include #include @@ -448,14 +449,15 @@ struct edac_mc_layer { __p; \ }) - -/* FIXME: add the proper per-location error counts */ struct dimm_info { + struct device dev; + char label[EDAC_MC_LABEL_LEN + 1]; /* DIMM label on motherboard */ /* Memory location data */ unsigned location[EDAC_MAX_LAYERS]; + struct kobject kobj; /* sysfs kobject for this csrow */ struct mem_ctl_info *mci; /* the parent */ u32 grain; /* granularity of reported error in bytes */ @@ -484,6 +486,8 @@ struct dimm_info { * patches in this series will fix this issue. */ struct rank_info { + struct device dev; + int chan_idx; struct csrow_info *csrow; struct dimm_info *dimm; @@ -492,6 +496,8 @@ struct rank_info { }; struct csrow_info { + struct device dev; + /* Used only by edac_mc_find_csrow_by_page() */ unsigned long first_page; /* first page number in csrow */ unsigned long last_page; /* last page number in csrow */ @@ -517,15 +523,6 @@ struct mcidev_sysfs_group { const struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */ }; -struct mcidev_sysfs_group_kobj { - struct list_head list; /* list for all instances within a mc */ - - struct kobject kobj; /* kobj for the group */ - - const struct mcidev_sysfs_group *grp; /* group description table */ - struct mem_ctl_info *mci; /* the parent */ -}; - /* mcidev_sysfs_attribute structure * used for driver sysfs attributes and in mem_ctl_info * sysfs top level entries @@ -536,13 +533,27 @@ struct mcidev_sysfs_attribute { const struct mcidev_sysfs_group *grp; /* Points to a group of attributes */ /* Ops for show/store values at the attribute - not used on group */ - ssize_t (*show)(struct mem_ctl_info *,char *); - ssize_t (*store)(struct mem_ctl_info *, const char *,size_t); + ssize_t (*show)(struct mem_ctl_info *, char *); + ssize_t (*store)(struct mem_ctl_info *, const char *, size_t); + + void *priv; +}; + +/* + * struct errcount_attribute - used to store the several error counts + */ +struct errcount_attribute_data { + int n_layers; + int pos[EDAC_MAX_LAYERS]; + int layer0, layer1, layer2; }; /* MEMORY controller information structure */ struct mem_ctl_info { + struct device dev; + struct bus_type bus; + struct list_head link; /* for global list of mem_ctl_info structs */ struct module *owner; /* Module owner of this control struct */ @@ -587,7 +598,15 @@ struct mem_ctl_info { struct csrow_info *csrows; unsigned nr_csrows, num_cschannel; - /* Memory Controller hierarchy */ + /* + * Memory Controller hierarchy + * + * There are basically two types of memory controller: the ones that + * sees memory sticks ("dimms"), and the ones that sees memory ranks. + * All old memory controllers enumerate memories per rank, but most + * of the recent drivers enumerate memories per DIMM, instead. + * When the memory controller is per rank, mem_is_per_rank is true. + */ unsigned n_layers; struct edac_mc_layer *layers; bool mem_is_per_rank; -- cgit v1.2.3 From d90c008963ef638cb7ab7d5eb76362b3c2d379bc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 21 Mar 2012 16:55:02 -0300 Subject: edac: Get rid of the old kobj's from the edac mc code Now that al users for the old kobj raw access are gone, we can get rid of the legacy kobj-based structures and data. Reviewed-by: Aristeu Rozanski Cc: Doug Thompson Cc: Michal Marek Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_mc.c | 1 - drivers/edac/i5000_edac.c | 3 --- drivers/edac/i82875p_edac.c | 4 ---- include/linux/edac.h | 30 ------------------------------ 4 files changed, 38 deletions(-) (limited to 'include') diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 61ae34643b49..4a6fdc03740e 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -372,7 +372,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, } mci->op_state = OP_ALLOC; - INIT_LIST_HEAD(&mci->grp_kobj_list); /* at this point, the root kobj is valid, and in order to * 'free' the object, then the function: diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index a69245ad5f32..a7da4c7ad7fa 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -1406,7 +1406,6 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx) if (mci == NULL) return -ENOMEM; - kobject_get(&mci->edac_mci_kobj); debugf0("MC: %s: %s(): mci = %p\n", __FILE__, __func__, mci); mci->pdev = &pdev->dev; /* record ptr to the generic device */ @@ -1479,7 +1478,6 @@ fail1: i5000_put_devices(mci); fail0: - kobject_put(&mci->edac_mci_kobj); edac_mc_free(mci); return -ENODEV; } @@ -1525,7 +1523,6 @@ static void __devexit i5000_remove_one(struct pci_dev *pdev) /* retrieve references to resources, and free those resources */ i5000_put_devices(mci); - kobject_put(&mci->edac_mci_kobj); edac_mc_free(mci); } diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index 1cc682d0678d..a47c6b25db31 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c @@ -426,9 +426,6 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) goto fail0; } - /* Keeps mci available after edac_mc_del_mc() till edac_mc_free() */ - kobject_get(&mci->edac_mci_kobj); - debugf3("%s(): init mci\n", __func__); mci->pdev = &pdev->dev; mci->mtype_cap = MEM_FLAG_DDR; @@ -471,7 +468,6 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) return 0; fail1: - kobject_put(&mci->edac_mci_kobj); edac_mc_free(mci); fail0: diff --git a/include/linux/edac.h b/include/linux/edac.h index a2b0b6fc002c..8a2da47daa47 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -457,7 +457,6 @@ struct dimm_info { /* Memory location data */ unsigned location[EDAC_MAX_LAYERS]; - struct kobject kobj; /* sysfs kobject for this csrow */ struct mem_ctl_info *mci; /* the parent */ u32 grain; /* granularity of reported error in bytes */ @@ -511,34 +510,11 @@ struct csrow_info { struct mem_ctl_info *mci; /* the parent */ - struct kobject kobj; /* sysfs kobject for this csrow */ - /* channel information for this csrow */ u32 nr_channels; struct rank_info *channels; }; -struct mcidev_sysfs_group { - const char *name; /* group name */ - const struct mcidev_sysfs_attribute *mcidev_attr; /* group attributes */ -}; - -/* mcidev_sysfs_attribute structure - * used for driver sysfs attributes and in mem_ctl_info - * sysfs top level entries - */ -struct mcidev_sysfs_attribute { - /* It should use either attr or grp */ - struct attribute attr; - const struct mcidev_sysfs_group *grp; /* Points to a group of attributes */ - - /* Ops for show/store values at the attribute - not used on group */ - ssize_t (*show)(struct mem_ctl_info *, char *); - ssize_t (*store)(struct mem_ctl_info *, const char *, size_t); - - void *priv; -}; - /* * struct errcount_attribute - used to store the several error counts */ @@ -641,12 +617,6 @@ struct mem_ctl_info { struct completion complete; - /* edac sysfs device control */ - struct kobject edac_mci_kobj; - - /* list for all grp instances within a mc */ - struct list_head grp_kobj_list; - /* Additional top controller level attributes, but specified * by the low level driver. * -- cgit v1.2.3 From 452a6bf955ee1842361742833e40e046287308f4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 26 Mar 2012 09:35:11 -0300 Subject: edac: Add debufs nodes to allow doing fake error inject Sometimes, it is useful to have a mechanism that generates fake errors, in order to test the EDAC core code, and the userspace tools. Provide such mechanism by adding a few debugfs nodes. Reviewed-by: Aristeu Rozanski Cc: Doug Thompson Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_mc_sysfs.c | 87 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/edac.h | 7 ++++ 2 files changed, 94 insertions(+) (limited to 'include') diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 87fb396bc550..daa418b61525 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "edac_core.h" #include "edac_module.h" @@ -783,6 +784,47 @@ static ssize_t mci_max_location_show(struct device *dev, return p - data; } +#ifdef CONFIG_EDAC_DEBUG +static ssize_t edac_fake_inject_write(struct file *file, + const char __user *data, + size_t count, loff_t *ppos) +{ + struct device *dev = file->private_data; + struct mem_ctl_info *mci = to_mci(dev); + static enum hw_event_mc_err_type type; + + type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED + : HW_EVENT_ERR_CORRECTED; + + printk(KERN_DEBUG + "Generating a %s fake error to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n", + (type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE", + mci->fake_inject_layer[0], + mci->fake_inject_layer[1], + mci->fake_inject_layer[2] + ); + edac_mc_handle_error(type, mci, 0, 0, 0, + mci->fake_inject_layer[0], + mci->fake_inject_layer[1], + mci->fake_inject_layer[2], + "FAKE ERROR", "for EDAC testing only", NULL); + + return count; +} + +static int debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations debug_fake_inject_fops = { + .open = debugfs_open, + .write = edac_fake_inject_write, + .llseek = generic_file_llseek, +}; +#endif + /* default Control file */ DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); @@ -833,6 +875,45 @@ static struct device_type mci_attr_type = { .release = mci_attr_release, }; +#ifdef CONFIG_EDAC_DEBUG +int edac_create_debug_nodes(struct mem_ctl_info *mci) +{ + struct dentry *d, *parent; + char name[80]; + int i; + + d = debugfs_create_dir(mci->dev.kobj.name, mci->debugfs); + if (!d) + return -ENOMEM; + parent = d; + + for (i = 0; i < mci->n_layers; i++) { + sprintf(name, "fake_inject_%s", + edac_layer_name[mci->layers[i].type]); + d = debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent, + &mci->fake_inject_layer[i]); + if (!d) + goto nomem; + } + + d = debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent, + &mci->fake_inject_ue); + if (!d) + goto nomem; + + d = debugfs_create_file("fake_inject", S_IWUSR, parent, + &mci->dev, + &debug_fake_inject_fops); + if (!d) + goto nomem; + + return 0; +nomem: + debugfs_remove(mci->debugfs); + return -ENOMEM; +} +#endif + /* * Create a new Memory Controller kobject instance, * mc under the 'mc' directory @@ -911,6 +992,9 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) goto fail; #endif +#ifdef CONFIG_EDAC_DEBUG + edac_create_debug_nodes(mci); +#endif return 0; fail: @@ -937,6 +1021,9 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) debugf0("%s()\n", __func__); +#ifdef CONFIG_EDAC_DEBUG + debugfs_remove(mci->debugfs); +#endif #ifdef CONFIG_EDAC_LEGACY_SYSFS edac_delete_csrow_objects(mci); #endif diff --git a/include/linux/edac.h b/include/linux/edac.h index 8a2da47daa47..64ae0c5cf62e 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -17,6 +17,7 @@ #include #include #include +#include struct device; @@ -634,6 +635,12 @@ struct mem_ctl_info { /* the internal state of this controller instance */ int op_state; + +#ifdef CONFIG_EDAC_DEBUG + struct dentry *debugfs; + u8 fake_inject_layer[EDAC_MAX_LAYERS]; + u32 fake_inject_ue; +#endif }; #endif -- cgit v1.2.3 From de3910eb79ac8c0f29a11224661c0ebaaf813039 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 24 Apr 2012 15:05:43 -0300 Subject: edac: change the mem allocation scheme to make Documentation/kobject.txt happy Kernel kobjects have rigid rules: each container object should be dynamically allocated, and can't be allocated into a single kmalloc. EDAC never obeyed this rule: it has a single malloc function that allocates all needed data into a single kzalloc. As this is not accepted anymore, change the allocation schema of the EDAC *_info structs to enforce this Kernel standard. Acked-by: Chris Metcalf Cc: Aristeu Rozanski Cc: Doug Thompson Cc: Greg K H Cc: Borislav Petkov Cc: Mark Gross Cc: Tim Small Cc: Ranganathan Desikan Cc: "Arvind R." Cc: Olof Johansson Cc: Egor Martovetsky Cc: Michal Marek Cc: Jiri Kosina Cc: Dmitry Eremin-Solenikov Cc: Benjamin Herrenschmidt Cc: Hitoshi Mitake Cc: Andrew Morton Cc: Shaohui Xie Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/amd64_edac.c | 10 ++-- drivers/edac/amd76x_edac.c | 8 +-- drivers/edac/cell_edac.c | 8 +-- drivers/edac/cpc925_edac.c | 8 +-- drivers/edac/e752x_edac.c | 4 +- drivers/edac/e7xxx_edac.c | 4 +- drivers/edac/edac_mc.c | 107 ++++++++++++++++++++++------------ drivers/edac/edac_mc_sysfs.c | 126 +++++++++++++++++++++++------------------ drivers/edac/i3000_edac.c | 6 +- drivers/edac/i3200_edac.c | 4 +- drivers/edac/i5400_edac.c | 6 +- drivers/edac/i82443bxgx_edac.c | 4 +- drivers/edac/i82860_edac.c | 6 +- drivers/edac/i82875p_edac.c | 6 +- drivers/edac/i82975x_edac.c | 10 ++-- drivers/edac/mpc85xx_edac.c | 6 +- drivers/edac/mv64x60_edac.c | 4 +- drivers/edac/pasemi_edac.c | 8 +-- drivers/edac/r82600_edac.c | 4 +- drivers/edac/tile_edac.c | 4 +- drivers/edac/x38_edac.c | 4 +- include/linux/edac.h | 59 +++++++++++++------ 22 files changed, 242 insertions(+), 164 deletions(-) (limited to 'include') diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 9905834b560f..9fbced7f65ee 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -2205,6 +2205,7 @@ static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr) static int init_csrows(struct mem_ctl_info *mci) { struct csrow_info *csrow; + struct dimm_info *dimm; struct amd64_pvt *pvt = mci->pvt_info; u64 base, mask; u32 val; @@ -2222,7 +2223,7 @@ static int init_csrows(struct mem_ctl_info *mci) !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE)); for_each_chip_select(i, 0, pvt) { - csrow = &mci->csrows[i]; + csrow = mci->csrows[i]; if (!csrow_enabled(i, 0, pvt) && !csrow_enabled(i, 1, pvt)) { debugf1("----CSROW %d EMPTY for node %d\n", i, @@ -2257,9 +2258,10 @@ static int init_csrows(struct mem_ctl_info *mci) edac_mode = EDAC_NONE; for (j = 0; j < pvt->channel_count; j++) { - csrow->channels[j].dimm->mtype = mtype; - csrow->channels[j].dimm->edac_mode = edac_mode; - csrow->channels[j].dimm->nr_pages = nr_pages; + dimm = csrow->channels[j]->dimm; + dimm->mtype = mtype; + dimm->edac_mode = edac_mode; + dimm->nr_pages = nr_pages; } } diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c index 7439786f3bef..a0c9f82875cd 100644 --- a/drivers/edac/amd76x_edac.c +++ b/drivers/edac/amd76x_edac.c @@ -146,7 +146,7 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci, if (handle_errors) { row = (info->ecc_mode_status >> 4) & 0xf; edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, - mci->csrows[row].first_page, 0, 0, + mci->csrows[row]->first_page, 0, 0, row, 0, -1, mci->ctl_name, "", NULL); } @@ -161,7 +161,7 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci, if (handle_errors) { row = info->ecc_mode_status & 0xf; edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, - mci->csrows[row].first_page, 0, 0, + mci->csrows[row]->first_page, 0, 0, row, 0, -1, mci->ctl_name, "", NULL); } @@ -194,8 +194,8 @@ static void amd76x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, int index; for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; /* find the DRAM Chip Select Base address and mask */ pci_read_config_dword(pdev, diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c index 2e5b95374dc6..478d8ee434df 100644 --- a/drivers/edac/cell_edac.c +++ b/drivers/edac/cell_edac.c @@ -33,7 +33,7 @@ struct cell_edac_priv static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar) { struct cell_edac_priv *priv = mci->pvt_info; - struct csrow_info *csrow = &mci->csrows[0]; + struct csrow_info *csrow = mci->csrows[0]; unsigned long address, pfn, offset, syndrome; dev_dbg(mci->pdev, "ECC CE err on node %d, channel %d, ar = 0x%016llx\n", @@ -56,7 +56,7 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar) static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar) { struct cell_edac_priv *priv = mci->pvt_info; - struct csrow_info *csrow = &mci->csrows[0]; + struct csrow_info *csrow = mci->csrows[0]; unsigned long address, pfn, offset; dev_dbg(mci->pdev, "ECC UE err on node %d, channel %d, ar = 0x%016llx\n", @@ -126,7 +126,7 @@ static void cell_edac_check(struct mem_ctl_info *mci) static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci) { - struct csrow_info *csrow = &mci->csrows[0]; + struct csrow_info *csrow = mci->csrows[0]; struct dimm_info *dimm; struct cell_edac_priv *priv = mci->pvt_info; struct device_node *np; @@ -150,7 +150,7 @@ static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci) csrow->last_page = csrow->first_page + nr_pages - 1; for (j = 0; j < csrow->nr_channels; j++) { - dimm = csrow->channels[j].dimm; + dimm = csrow->channels[j]->dimm; dimm->mtype = MEM_XDR; dimm->edac_mode = EDAC_SECDED; dimm->nr_pages = nr_pages / csrow->nr_channels; diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c index 3510aa446297..534491d67159 100644 --- a/drivers/edac/cpc925_edac.c +++ b/drivers/edac/cpc925_edac.c @@ -348,7 +348,7 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci) if (bba == 0) continue; /* not populated */ - csrow = &mci->csrows[index]; + csrow = mci->csrows[index]; row_size = bba * (1UL << 28); /* 256M */ csrow->first_page = last_nr_pages; @@ -380,7 +380,7 @@ static void cpc925_init_csrows(struct mem_ctl_info *mci) break; } for (j = 0; j < csrow->nr_channels; j++) { - dimm = csrow->channels[j].dimm; + dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / csrow->nr_channels; dimm->mtype = MEM_RDDR; dimm->edac_mode = EDAC_SECDED; @@ -463,7 +463,7 @@ static void cpc925_mc_get_pfn(struct mem_ctl_info *mci, u32 mear, *csrow = rank; #ifdef CONFIG_EDAC_DEBUG - if (mci->csrows[rank].first_page == 0) { + if (mci->csrows[rank]->first_page == 0) { cpc925_mc_printk(mci, KERN_ERR, "ECC occurs in a " "non-populated csrow, broken hardware?\n"); return; @@ -471,7 +471,7 @@ static void cpc925_mc_get_pfn(struct mem_ctl_info *mci, u32 mear, #endif /* Revert csrow number */ - pa = mci->csrows[rank].first_page << PAGE_SHIFT; + pa = mci->csrows[rank]->first_page << PAGE_SHIFT; /* Revert column address */ col += bcnt; diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index d1142ed8bd88..7cde7f1aafb7 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c @@ -1096,7 +1096,7 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, for (last_cumul_size = index = 0; index < mci->nr_csrows; index++) { /* mem_dev 0=x8, 1=x4 */ mem_dev = (dra >> (index * 4 + 2)) & 0x3; - csrow = &mci->csrows[remap_csrow_index(mci, index)]; + csrow = mci->csrows[remap_csrow_index(mci, index)]; mem_dev = (mem_dev == 2); pci_read_config_byte(pdev, E752X_DRB + index, &value); @@ -1127,7 +1127,7 @@ static void e752x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, } else edac_mode = EDAC_NONE; for (i = 0; i < csrow->nr_channels; i++) { - struct dimm_info *dimm = csrow->channels[i].dimm; + struct dimm_info *dimm = csrow->channels[i]->dimm; debugf3("Initializing rank at (%i,%i)\n", index, i); dimm->nr_pages = nr_pages / csrow->nr_channels; diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c index bab31aab983d..c6c0ebaca371 100644 --- a/drivers/edac/e7xxx_edac.c +++ b/drivers/edac/e7xxx_edac.c @@ -378,7 +378,7 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, for (index = 0; index < mci->nr_csrows; index++) { /* mem_dev 0=x8, 1=x4 */ mem_dev = (dra >> (index * 4 + 3)) & 0x1; - csrow = &mci->csrows[index]; + csrow = mci->csrows[index]; pci_read_config_byte(pdev, E7XXX_DRB + index, &value); /* convert a 64 or 32 MiB DRB to a page size. */ @@ -409,7 +409,7 @@ static void e7xxx_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, edac_mode = EDAC_NONE; for (j = 0; j < drc_chan + 1; j++) { - dimm = csrow->channels[j].dimm; + dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / (drc_chan + 1); dimm->grain = 1 << 12; /* 4KiB - resolution of CELOG */ diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 4a6fdc03740e..db2ba31ba2b1 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -210,15 +210,15 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, { struct mem_ctl_info *mci; struct edac_mc_layer *layer; - struct csrow_info *csi, *csr; - struct rank_info *chi, *chp, *chan; + struct csrow_info *csr; + struct rank_info *chan; struct dimm_info *dimm; u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS]; unsigned pos[EDAC_MAX_LAYERS]; unsigned size, tot_dimms = 1, count = 1; unsigned tot_csrows = 1, tot_channels = 1, tot_errcount = 0; void *pvt, *p, *ptr = NULL; - int i, j, row, chn, n, len; + int i, j, row, chn, n, len, off; bool per_rank = false; BUG_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0); @@ -244,9 +244,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, */ mci = edac_align_ptr(&ptr, sizeof(*mci), 1); layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers); - csi = edac_align_ptr(&ptr, sizeof(*csi), tot_csrows); - chi = edac_align_ptr(&ptr, sizeof(*chi), tot_csrows * tot_channels); - dimm = edac_align_ptr(&ptr, sizeof(*dimm), tot_dimms); for (i = 0; i < n_layers; i++) { count *= layers[i].size; debugf4("%s: errcount layer %d size %d\n", __func__, i, count); @@ -264,6 +261,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, tot_dimms, per_rank ? "ranks" : "dimms", tot_csrows * tot_channels); + mci = kzalloc(size, GFP_KERNEL); if (mci == NULL) return NULL; @@ -272,9 +270,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, * rather than an imaginary chunk of memory located at address 0. */ layer = (struct edac_mc_layer *)(((char *)mci) + ((unsigned long)layer)); - csi = (struct csrow_info *)(((char *)mci) + ((unsigned long)csi)); - chi = (struct rank_info *)(((char *)mci) + ((unsigned long)chi)); - dimm = (struct dimm_info *)(((char *)mci) + ((unsigned long)dimm)); for (i = 0; i < n_layers; i++) { mci->ce_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ce_per_layer[i])); mci->ue_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ue_per_layer[i])); @@ -283,8 +278,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, /* setup index and various internal pointers */ mci->mc_idx = mc_num; - mci->csrows = csi; - mci->dimms = dimm; mci->tot_dimms = tot_dimms; mci->pvt_info = pvt; mci->n_layers = n_layers; @@ -295,39 +288,60 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, mci->mem_is_per_rank = per_rank; /* - * Fill the csrow struct + * Alocate and fill the csrow/channels structs */ + mci->csrows = kcalloc(sizeof(*mci->csrows), tot_csrows, GFP_KERNEL); + if (!mci->csrows) + goto error; for (row = 0; row < tot_csrows; row++) { - csr = &csi[row]; + csr = kzalloc(sizeof(**mci->csrows), GFP_KERNEL); + if (!csr) + goto error; + mci->csrows[row] = csr; csr->csrow_idx = row; csr->mci = mci; csr->nr_channels = tot_channels; - chp = &chi[row * tot_channels]; - csr->channels = chp; + csr->channels = kcalloc(sizeof(*csr->channels), tot_channels, + GFP_KERNEL); + if (!csr->channels) + goto error; for (chn = 0; chn < tot_channels; chn++) { - chan = &chp[chn]; + chan = kzalloc(sizeof(**csr->channels), GFP_KERNEL); + if (!chan) + goto error; + csr->channels[chn] = chan; chan->chan_idx = chn; chan->csrow = csr; } } /* - * Fill the dimm struct + * Allocate and fill the dimm structs */ + mci->dimms = kcalloc(sizeof(*mci->dimms), tot_dimms, GFP_KERNEL); + if (!mci->dimms) + goto error; + memset(&pos, 0, sizeof(pos)); row = 0; chn = 0; debugf4("%s: initializing %d %s\n", __func__, tot_dimms, per_rank ? "ranks" : "dimms"); for (i = 0; i < tot_dimms; i++) { - chan = &csi[row].channels[chn]; - dimm = EDAC_DIMM_PTR(layer, mci->dimms, n_layers, - pos[0], pos[1], pos[2]); + chan = mci->csrows[row]->channels[chn]; + off = EDAC_DIMM_OFF(layer, n_layers, pos[0], pos[1], pos[2]); + if (off < 0 || off >= tot_dimms) { + edac_mc_printk(mci, KERN_ERR, "EDAC core bug: EDAC_DIMM_OFF is trying to do an illegal data access\n"); + goto error; + } + + dimm = kzalloc(sizeof(**mci->dimms), GFP_KERNEL); + mci->dimms[off] = dimm; dimm->mci = mci; - debugf2("%s: %d: %s%zd (%d:%d:%d): row %d, chan %d\n", __func__, - i, per_rank ? "rank" : "dimm", (dimm - mci->dimms), + debugf2("%s: %d: %s%i (%d:%d:%d): row %d, chan %d\n", __func__, + i, per_rank ? "rank" : "dimm", off, pos[0], pos[1], pos[2], row, chn); /* @@ -381,6 +395,28 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, */ return mci; + +error: + if (mci->dimms) { + for (i = 0; i < tot_dimms; i++) + kfree(mci->dimms[i]); + kfree(mci->dimms); + } + if (mci->csrows) { + for (chn = 0; chn < tot_channels; chn++) { + csr = mci->csrows[chn]; + if (csr) { + for (chn = 0; chn < tot_channels; chn++) + kfree(csr->channels[chn]); + kfree(csr); + } + kfree(mci->csrows[i]); + } + kfree(mci->csrows); + } + kfree(mci); + + return NULL; } EXPORT_SYMBOL_GPL(edac_mc_alloc); @@ -393,10 +429,8 @@ void edac_mc_free(struct mem_ctl_info *mci) { debugf1("%s()\n", __func__); + /* the mci instance is freed here, when the sysfs object is dropped */ edac_unregister_sysfs(mci); - - /* free the mci instance memory here */ - kfree(mci); } EXPORT_SYMBOL_GPL(edac_mc_free); @@ -668,13 +702,12 @@ int edac_mc_add_mc(struct mem_ctl_info *mci) for (i = 0; i < mci->nr_csrows; i++) { int j; - edac_mc_dump_csrow(&mci->csrows[i]); - for (j = 0; j < mci->csrows[i].nr_channels; j++) - edac_mc_dump_channel(&mci->csrows[i]. - channels[j]); + edac_mc_dump_csrow(mci->csrows[i]); + for (j = 0; j < mci->csrows[i]->nr_channels; j++) + edac_mc_dump_channel(mci->csrows[i]->channels[j]); } for (i = 0; i < mci->tot_dimms; i++) - edac_mc_dump_dimm(&mci->dimms[i]); + edac_mc_dump_dimm(mci->dimms[i]); } #endif mutex_lock(&mem_ctls_mutex); @@ -793,17 +826,17 @@ static void edac_mc_scrub_block(unsigned long page, unsigned long offset, /* FIXME - should return -1 */ int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, unsigned long page) { - struct csrow_info *csrows = mci->csrows; + struct csrow_info **csrows = mci->csrows; int row, i, j, n; debugf1("MC%d: %s(): 0x%lx\n", mci->mc_idx, __func__, page); row = -1; for (i = 0; i < mci->nr_csrows; i++) { - struct csrow_info *csrow = &csrows[i]; + struct csrow_info *csrow = csrows[i]; n = 0; for (j = 0; j < csrow->nr_channels; j++) { - struct dimm_info *dimm = csrow->channels[j].dimm; + struct dimm_info *dimm = csrow->channels[j]->dimm; n += dimm->nr_pages; } if (n == 0) @@ -1062,7 +1095,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, p = label; *p = '\0'; for (i = 0; i < mci->tot_dimms; i++) { - struct dimm_info *dimm = &mci->dimms[i]; + struct dimm_info *dimm = mci->dimms[i]; if (top_layer >= 0 && top_layer != dimm->location[0]) continue; @@ -1120,13 +1153,13 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, strcpy(label, "unknown memory"); if (type == HW_EVENT_ERR_CORRECTED) { if (row >= 0) { - mci->csrows[row].ce_count++; + mci->csrows[row]->ce_count++; if (chan >= 0) - mci->csrows[row].channels[chan].ce_count++; + mci->csrows[row]->channels[chan]->ce_count++; } } else if (row >= 0) - mci->csrows[row].ue_count++; + mci->csrows[row]->ue_count++; } /* Fill the RAM location data */ diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 0f671907c90b..87b8d7d6385f 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -82,7 +82,7 @@ module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int, &edac_mc_poll_msec, 0644); MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds"); -static struct device mci_pdev; +static struct device *mci_pdev; /* * various constants for Memory Controllers @@ -181,7 +181,7 @@ static ssize_t csrow_size_show(struct device *dev, u32 nr_pages = 0; for (i = 0; i < csrow->nr_channels; i++) - nr_pages += csrow->channels[i].dimm->nr_pages; + nr_pages += csrow->channels[i]->dimm->nr_pages; return sprintf(data, "%u\n", PAGES_TO_MiB(nr_pages)); } @@ -190,7 +190,7 @@ static ssize_t csrow_mem_type_show(struct device *dev, { struct csrow_info *csrow = to_csrow(dev); - return sprintf(data, "%s\n", mem_types[csrow->channels[0].dimm->mtype]); + return sprintf(data, "%s\n", mem_types[csrow->channels[0]->dimm->mtype]); } static ssize_t csrow_dev_type_show(struct device *dev, @@ -198,7 +198,7 @@ static ssize_t csrow_dev_type_show(struct device *dev, { struct csrow_info *csrow = to_csrow(dev); - return sprintf(data, "%s\n", dev_types[csrow->channels[0].dimm->dtype]); + return sprintf(data, "%s\n", dev_types[csrow->channels[0]->dimm->dtype]); } static ssize_t csrow_edac_mode_show(struct device *dev, @@ -207,7 +207,7 @@ static ssize_t csrow_edac_mode_show(struct device *dev, { struct csrow_info *csrow = to_csrow(dev); - return sprintf(data, "%s\n", edac_caps[csrow->channels[0].dimm->edac_mode]); + return sprintf(data, "%s\n", edac_caps[csrow->channels[0]->dimm->edac_mode]); } /* show/store functions for DIMM Label attributes */ @@ -217,7 +217,7 @@ static ssize_t channel_dimm_label_show(struct device *dev, { struct csrow_info *csrow = to_csrow(dev); unsigned chan = to_channel(mattr); - struct rank_info *rank = &csrow->channels[chan]; + struct rank_info *rank = csrow->channels[chan]; /* if field has not been initialized, there is nothing to send */ if (!rank->dimm->label[0]) @@ -233,7 +233,7 @@ static ssize_t channel_dimm_label_store(struct device *dev, { struct csrow_info *csrow = to_csrow(dev); unsigned chan = to_channel(mattr); - struct rank_info *rank = &csrow->channels[chan]; + struct rank_info *rank = csrow->channels[chan]; ssize_t max_size = 0; @@ -250,7 +250,7 @@ static ssize_t channel_ce_count_show(struct device *dev, { struct csrow_info *csrow = to_csrow(dev); unsigned chan = to_channel(mattr); - struct rank_info *rank = &csrow->channels[chan]; + struct rank_info *rank = csrow->channels[chan]; return sprintf(data, "%u\n", rank->ce_count); } @@ -283,9 +283,12 @@ static const struct attribute_group *csrow_attr_groups[] = { NULL }; -static void csrow_attr_release(struct device *device) +static void csrow_attr_release(struct device *dev) { - debugf1("Releasing csrow device %s\n", dev_name(device)); + struct csrow_info *csrow = container_of(dev, struct csrow_info, dev); + + debugf1("Releasing csrow device %s\n", dev_name(dev)); + kfree(csrow); } static struct device_type csrow_attr_type = { @@ -352,7 +355,7 @@ static inline int nr_pages_per_csrow(struct csrow_info *csrow) int chan, nr_pages = 0; for (chan = 0; chan < csrow->nr_channels; chan++) - nr_pages += csrow->channels[chan].dimm->nr_pages; + nr_pages += csrow->channels[chan]->dimm->nr_pages; return nr_pages; } @@ -382,7 +385,7 @@ static int edac_create_csrow_object(struct mem_ctl_info *mci, for (chan = 0; chan < csrow->nr_channels; chan++) { /* Only expose populated DIMMs */ - if (!csrow->channels[chan].dimm->nr_pages) + if (!csrow->channels[chan]->dimm->nr_pages) continue; err = device_create_file(&csrow->dev, dynamic_csrow_dimm_attr[chan]); @@ -418,10 +421,10 @@ static int edac_create_csrow_objects(struct mem_ctl_info *mci) struct csrow_info *csrow; for (i = 0; i < mci->nr_csrows; i++) { - csrow = &mci->csrows[i]; + csrow = mci->csrows[i]; if (!nr_pages_per_csrow(csrow)) continue; - err = edac_create_csrow_object(mci, &mci->csrows[i], i); + err = edac_create_csrow_object(mci, mci->csrows[i], i); if (err < 0) goto error; } @@ -429,18 +432,18 @@ static int edac_create_csrow_objects(struct mem_ctl_info *mci) error: for (--i; i >= 0; i--) { - csrow = &mci->csrows[i]; + csrow = mci->csrows[i]; if (!nr_pages_per_csrow(csrow)) continue; for (chan = csrow->nr_channels - 1; chan >= 0; chan--) { - if (!csrow->channels[chan].dimm->nr_pages) + if (!csrow->channels[chan]->dimm->nr_pages) continue; device_remove_file(&csrow->dev, dynamic_csrow_dimm_attr[chan]); device_remove_file(&csrow->dev, dynamic_csrow_ce_count_attr[chan]); } - put_device(&mci->csrows[i].dev); + put_device(&mci->csrows[i]->dev); } return err; @@ -452,11 +455,11 @@ static void edac_delete_csrow_objects(struct mem_ctl_info *mci) struct csrow_info *csrow; for (i = mci->nr_csrows - 1; i >= 0; i--) { - csrow = &mci->csrows[i]; + csrow = mci->csrows[i]; if (!nr_pages_per_csrow(csrow)) continue; for (chan = csrow->nr_channels - 1; chan >= 0; chan--) { - if (!csrow->channels[chan].dimm->nr_pages) + if (!csrow->channels[chan]->dimm->nr_pages) continue; debugf1("Removing csrow %d channel %d sysfs nodes\n", i, chan); @@ -465,8 +468,8 @@ static void edac_delete_csrow_objects(struct mem_ctl_info *mci) device_remove_file(&csrow->dev, dynamic_csrow_ce_count_attr[chan]); } - put_device(&mci->csrows[i].dev); - device_del(&mci->csrows[i].dev); + put_device(&mci->csrows[i]->dev); + device_del(&mci->csrows[i]->dev); } } #endif @@ -585,9 +588,12 @@ static const struct attribute_group *dimm_attr_groups[] = { NULL }; -static void dimm_attr_release(struct device *device) +static void dimm_attr_release(struct device *dev) { - debugf1("Releasing dimm device %s\n", dev_name(device)); + struct dimm_info *dimm = container_of(dev, struct dimm_info, dev); + + debugf1("Releasing dimm device %s\n", dev_name(dev)); + kfree(dimm); } static struct device_type dimm_attr_type = { @@ -641,13 +647,13 @@ static ssize_t mci_reset_counters_store(struct device *dev, mci->ce_noinfo_count = 0; for (row = 0; row < mci->nr_csrows; row++) { - struct csrow_info *ri = &mci->csrows[row]; + struct csrow_info *ri = mci->csrows[row]; ri->ue_count = 0; ri->ce_count = 0; for (chan = 0; chan < ri->nr_channels; chan++) - ri->channels[chan].ce_count = 0; + ri->channels[chan]->ce_count = 0; } cnt = 1; @@ -779,10 +785,10 @@ static ssize_t mci_size_mb_show(struct device *dev, int total_pages = 0, csrow_idx, j; for (csrow_idx = 0; csrow_idx < mci->nr_csrows; csrow_idx++) { - struct csrow_info *csrow = &mci->csrows[csrow_idx]; + struct csrow_info *csrow = mci->csrows[csrow_idx]; for (j = 0; j < csrow->nr_channels; j++) { - struct dimm_info *dimm = csrow->channels[j].dimm; + struct dimm_info *dimm = csrow->channels[j]->dimm; total_pages += dimm->nr_pages; } @@ -889,9 +895,12 @@ static const struct attribute_group *mci_attr_groups[] = { NULL }; -static void mci_attr_release(struct device *device) +static void mci_attr_release(struct device *dev) { - debugf1("Releasing mci device %s\n", dev_name(device)); + struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); + + debugf1("Releasing csrow device %s\n", dev_name(dev)); + kfree(mci); } static struct device_type mci_attr_type = { @@ -950,29 +959,28 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) { int i, err; - debugf0("%s() idx=%d\n", __func__, mci->mc_idx); + /* + * The memory controller needs its own bus, in order to avoid + * namespace conflicts at /sys/bus/edac. + */ + mci->bus.name = kasprintf(GFP_KERNEL, "mc%d", mci->mc_idx); + if (!mci->bus.name) + return -ENOMEM; + debugf0("creating bus %s\n",mci->bus.name); + err = bus_register(&mci->bus); + if (err < 0) + return err; /* get the /sys/devices/system/edac subsys reference */ - mci->dev.type = &mci_attr_type; device_initialize(&mci->dev); - mci->dev.parent = &mci_pdev; + mci->dev.parent = mci_pdev; mci->dev.bus = &mci->bus; dev_set_name(&mci->dev, "mc%d", mci->mc_idx); dev_set_drvdata(&mci->dev, mci); pm_runtime_forbid(&mci->dev); - /* - * The memory controller needs its own bus, in order to avoid - * namespace conflicts at /sys/bus/edac. - */ - debugf0("creating bus %s\n",mci->bus.name); - mci->bus.name = kstrdup(dev_name(&mci->dev), GFP_KERNEL); - err = bus_register(&mci->bus); - if (err < 0) - return err; - debugf0("%s(): creating device %s\n", __func__, dev_name(&mci->dev)); err = device_add(&mci->dev); @@ -986,7 +994,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) * Create the dimm/rank devices */ for (i = 0; i < mci->tot_dimms; i++) { - struct dimm_info *dimm = &mci->dimms[i]; + struct dimm_info *dimm = mci->dimms[i]; /* Only expose populated DIMMs */ if (dimm->nr_pages == 0) continue; @@ -1023,7 +1031,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) fail: for (i--; i >= 0; i--) { - struct dimm_info *dimm = &mci->dimms[i]; + struct dimm_info *dimm = mci->dimms[i]; if (dimm->nr_pages == 0) continue; put_device(&dimm->dev); @@ -1053,7 +1061,7 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) #endif for (i = 0; i < mci->tot_dimms; i++) { - struct dimm_info *dimm = &mci->dimms[i]; + struct dimm_info *dimm = mci->dimms[i]; if (dimm->nr_pages == 0) continue; debugf0("%s(): removing device %s\n", __func__, @@ -1072,9 +1080,15 @@ void edac_unregister_sysfs(struct mem_ctl_info *mci) kfree(mci->bus.name); } -static void mc_attr_release(struct device *device) +static void mc_attr_release(struct device *dev) { - debugf1("Releasing device %s\n", dev_name(device)); + /* + * There's no container structure here, as this is just the mci + * parent device, used to create the /sys/devices/mc sysfs node. + * So, there are no attributes on it. + */ + debugf1("Releasing device %s\n", dev_name(dev)); + kfree(dev); } static struct device_type mc_attr_type = { @@ -1095,21 +1109,25 @@ int __init edac_mc_sysfs_init(void) return -EINVAL; } - mci_pdev.bus = edac_subsys; - mci_pdev.type = &mc_attr_type; - device_initialize(&mci_pdev); - dev_set_name(&mci_pdev, "mc"); + mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL); + + mci_pdev->bus = edac_subsys; + mci_pdev->type = &mc_attr_type; + device_initialize(mci_pdev); + dev_set_name(mci_pdev, "mc"); - err = device_add(&mci_pdev); + err = device_add(mci_pdev); if (err < 0) return err; + debugf0("device %s created\n", dev_name(mci_pdev)); + return 0; } void __exit edac_mc_sysfs_exit(void) { - put_device(&mci_pdev); - device_del(&mci_pdev); + put_device(mci_pdev); + device_del(mci_pdev); edac_put_sysfs_subsys(); } diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c index d1ebd9b9ad6f..812213da7f91 100644 --- a/drivers/edac/i3000_edac.c +++ b/drivers/edac/i3000_edac.c @@ -236,7 +236,7 @@ static int i3000_process_error_info(struct mem_ctl_info *mci, int row, multi_chan, channel; unsigned long pfn, offset; - multi_chan = mci->csrows[0].nr_channels - 1; + multi_chan = mci->csrows[0]->nr_channels - 1; if (!(info->errsts & I3000_ERRSTS_BITS)) return 0; @@ -393,7 +393,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) for (last_cumul_size = i = 0; i < mci->nr_csrows; i++) { u8 value; u32 cumul_size; - struct csrow_info *csrow = &mci->csrows[i]; + struct csrow_info *csrow = mci->csrows[i]; value = drb[i]; cumul_size = value << (I3000_DRB_SHIFT - PAGE_SHIFT); @@ -410,7 +410,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) last_cumul_size = cumul_size; for (j = 0; j < nr_channels; j++) { - struct dimm_info *dimm = csrow->channels[j].dimm; + struct dimm_info *dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / nr_channels; dimm->grain = I3000_DEAP_GRAIN; diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c index 600a05df3759..c5f0fb31d5e0 100644 --- a/drivers/edac/i3200_edac.c +++ b/drivers/edac/i3200_edac.c @@ -379,7 +379,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx) */ for (i = 0; i < mci->nr_csrows; i++) { unsigned long nr_pages; - struct csrow_info *csrow = &mci->csrows[i]; + struct csrow_info *csrow = mci->csrows[i]; nr_pages = drb_to_nr_pages(drbs, stacked, i / I3200_RANKS_PER_CHANNEL, @@ -389,7 +389,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx) continue; for (j = 0; j < nr_channels; j++) { - struct dimm_info *dimm = csrow->channels[j].dimm; + struct dimm_info *dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / nr_channels; dimm->grain = nr_pages << PAGE_SHIFT; diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c index ba60694437bd..0570cf3d2563 100644 --- a/drivers/edac/i5400_edac.c +++ b/drivers/edac/i5400_edac.c @@ -1203,8 +1203,8 @@ static int i5400_init_dimms(struct mem_ctl_info *mci) size_mb = pvt->dimm_info[slot][channel].megabytes; - debugf2("%s: dimm%zd (branch %d channel %d slot %d): %d.%03d GB\n", - __func__, dimm - mci->dimms, + debugf2("%s: dimm (branch %d channel %d slot %d): %d.%03d GB\n", + __func__, channel / 2, channel % 2, slot, size_mb / 1000, size_mb % 1000); @@ -1227,7 +1227,7 @@ static int i5400_init_dimms(struct mem_ctl_info *mci) * With such single-DIMM mode, the SDCC algorithm degrades to SECDEC+. */ if (ndimms == 1) - mci->dimms[0].edac_mode = EDAC_SECDED; + mci->dimms[0]->edac_mode = EDAC_SECDED; return (ndimms == 0); } diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c index 65fd2e1eceb8..0f2751bf3ffe 100644 --- a/drivers/edac/i82443bxgx_edac.c +++ b/drivers/edac/i82443bxgx_edac.c @@ -197,8 +197,8 @@ static void i82443bxgx_init_csrows(struct mem_ctl_info *mci, pci_read_config_byte(pdev, I82443BXGX_DRAMC, &dramc); row_high_limit_last = 0; for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; pci_read_config_byte(pdev, I82443BXGX_DRB + index, &drbar); debugf1("MC%d: %s: %s() Row=%d DRB = %#0x\n", diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c index 8f3350000942..06a3c8d26d19 100644 --- a/drivers/edac/i82860_edac.c +++ b/drivers/edac/i82860_edac.c @@ -116,7 +116,7 @@ static int i82860_process_error_info(struct mem_ctl_info *mci, info->eap >>= PAGE_SHIFT; row = edac_mc_find_csrow_by_page(mci, info->eap); - dimm = mci->csrows[row].channels[0].dimm; + dimm = mci->csrows[row]->channels[0]->dimm; if (info->errsts & 0x0002) edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, @@ -161,8 +161,8 @@ static void i82860_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev) * in all eight rows. */ for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; pci_read_config_word(pdev, I82860_GBA + index * 2, &value); cumul_size = (value & I82860_GBA_MASK) << diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index a47c6b25db31..97fd6b769c84 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c @@ -227,7 +227,7 @@ static int i82875p_process_error_info(struct mem_ctl_info *mci, { int row, multi_chan; - multi_chan = mci->csrows[0].nr_channels - 1; + multi_chan = mci->csrows[0]->nr_channels - 1; if (!(info->errsts & 0x0081)) return 0; @@ -367,7 +367,7 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci, */ for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; + csrow = mci->csrows[index]; value = readb(ovrfl_window + I82875P_DRB + index); cumul_size = value << (I82875P_DRB_SHIFT - PAGE_SHIFT); @@ -382,7 +382,7 @@ static void i82875p_init_csrows(struct mem_ctl_info *mci, last_cumul_size = cumul_size; for (j = 0; j < nr_chans; j++) { - dimm = csrow->channels[j].dimm; + dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / nr_chans; dimm->grain = 1 << 12; /* I82875P_EAP has 4KiB reolution */ diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c index 8b26401efa19..4d239ab31e34 100644 --- a/drivers/edac/i82975x_edac.c +++ b/drivers/edac/i82975x_edac.c @@ -308,10 +308,10 @@ static int i82975x_process_error_info(struct mem_ctl_info *mci, (info->xeap & 1) ? 1 : 0, info->eap, (unsigned int) page); return 0; } - chan = (mci->csrows[row].nr_channels == 1) ? 0 : info->eap & 1; + chan = (mci->csrows[row]->nr_channels == 1) ? 0 : info->eap & 1; offst = info->eap & ((1 << PAGE_SHIFT) - - (1 << mci->csrows[row].channels[chan].dimm->grain)); + (1 << mci->csrows[row]->channels[chan]->dimm->grain)); if (info->errsts & 0x0002) edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, @@ -394,7 +394,7 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci, */ for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; + csrow = mci->csrows[index]; value = readb(mch_window + I82975X_DRB + index + ((index >= 4) ? 0x80 : 0)); @@ -421,10 +421,10 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci, */ dtype = i82975x_dram_type(mch_window, index); for (chan = 0; chan < csrow->nr_channels; chan++) { - dimm = mci->csrows[index].channels[chan].dimm; + dimm = mci->csrows[index]->channels[chan]->dimm; dimm->nr_pages = nr_pages / csrow->nr_channels; - strncpy(csrow->channels[chan].dimm->label, + strncpy(csrow->channels[chan]->dimm->label, labels[(index >> 1) + (chan * 2)], EDAC_MC_LABEL_LEN); dimm->grain = 1 << 7; /* 128Byte cache-line resolution */ diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index d132dbbd9bd7..0db6f1e84656 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c @@ -825,7 +825,7 @@ static void mpc85xx_mc_check(struct mem_ctl_info *mci) pfn = err_addr >> PAGE_SHIFT; for (row_index = 0; row_index < mci->nr_csrows; row_index++) { - csrow = &mci->csrows[row_index]; + csrow = mci->csrows[row_index]; if ((pfn >= csrow->first_page) && (pfn <= csrow->last_page)) break; } @@ -945,8 +945,8 @@ static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci) u32 start; u32 end; - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; cs_bnds = in_be32(pdata->mc_vbase + MPC85XX_MC_CS_BNDS_0 + (index * MPC85XX_MC_CS_BNDS_OFS)); diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c index ff6b8e248e89..3a58ba9158db 100644 --- a/drivers/edac/mv64x60_edac.c +++ b/drivers/edac/mv64x60_edac.c @@ -670,8 +670,8 @@ static void mv64x60_init_csrows(struct mem_ctl_info *mci, ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG); - csrow = &mci->csrows[0]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[0]; + dimm = csrow->channels[0]->dimm; dimm->nr_pages = pdata->total_mem >> PAGE_SHIFT; dimm->grain = 8; diff --git a/drivers/edac/pasemi_edac.c b/drivers/edac/pasemi_edac.c index 92becaa8722a..44f73b00df01 100644 --- a/drivers/edac/pasemi_edac.c +++ b/drivers/edac/pasemi_edac.c @@ -111,14 +111,14 @@ static void pasemi_edac_process_error_info(struct mem_ctl_info *mci, u32 errsta) if (errsta & (MCDEBUG_ERRSTA_MBE_STATUS | MCDEBUG_ERRSTA_RFL_STATUS)) { edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, - mci->csrows[cs].first_page, 0, 0, + mci->csrows[cs]->first_page, 0, 0, cs, 0, -1, mci->ctl_name, "", NULL); } /* correctable/single-bit errors */ if (errsta & MCDEBUG_ERRSTA_SBE_STATUS) edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, - mci->csrows[cs].first_page, 0, 0, + mci->csrows[cs]->first_page, 0, 0, cs, 0, -1, mci->ctl_name, "", NULL); } @@ -141,8 +141,8 @@ static int pasemi_edac_init_csrows(struct mem_ctl_info *mci, int index; for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; pci_read_config_dword(pdev, MCDRAM_RANKCFG + (index * 12), diff --git a/drivers/edac/r82600_edac.c b/drivers/edac/r82600_edac.c index cf4ccbdba85d..445c9ff27b88 100644 --- a/drivers/edac/r82600_edac.c +++ b/drivers/edac/r82600_edac.c @@ -230,8 +230,8 @@ static void r82600_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, row_high_limit_last = 0; for (index = 0; index < mci->nr_csrows; index++) { - csrow = &mci->csrows[index]; - dimm = csrow->channels[0].dimm; + csrow = mci->csrows[index]; + dimm = csrow->channels[0]->dimm; /* find the DRAM Chip Select Base address and mask */ pci_read_config_byte(pdev, R82600_DRBA + index, &drbar); diff --git a/drivers/edac/tile_edac.c b/drivers/edac/tile_edac.c index 604bc4df653a..fc77f77fa065 100644 --- a/drivers/edac/tile_edac.c +++ b/drivers/edac/tile_edac.c @@ -84,10 +84,10 @@ static void tile_edac_check(struct mem_ctl_info *mci) */ static int __devinit tile_edac_init_csrows(struct mem_ctl_info *mci) { - struct csrow_info *csrow = &mci->csrows[0]; + struct csrow_info *csrow = mci->csrows[0]; struct tile_edac_priv *priv = mci->pvt_info; struct mshim_mem_info mem_info; - struct dimm_info *dimm = csrow->channels[0].dimm; + struct dimm_info *dimm = csrow->channels[0]->dimm; if (hv_dev_pread(priv->hv_devhdl, 0, (HV_VirtAddr)&mem_info, sizeof(struct mshim_mem_info), MSHIM_MEM_INFO_OFF) != diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c index f9506f26e2bf..ae699be78b24 100644 --- a/drivers/edac/x38_edac.c +++ b/drivers/edac/x38_edac.c @@ -378,7 +378,7 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx) */ for (i = 0; i < mci->nr_csrows; i++) { unsigned long nr_pages; - struct csrow_info *csrow = &mci->csrows[i]; + struct csrow_info *csrow = mci->csrows[i]; nr_pages = drb_to_nr_pages(drbs, stacked, i / X38_RANKS_PER_CHANNEL, @@ -388,7 +388,7 @@ static int x38_probe1(struct pci_dev *pdev, int dev_idx) continue; for (j = 0; j < x38_channel_num; j++) { - struct dimm_info *dimm = csrow->channels[j].dimm; + struct dimm_info *dimm = csrow->channels[j]->dimm; dimm->nr_pages = nr_pages / x38_channel_num; dimm->grain = nr_pages << PAGE_SHIFT; diff --git a/include/linux/edac.h b/include/linux/edac.h index 64ae0c5cf62e..6677af853e30 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -412,23 +412,21 @@ struct edac_mc_layer { #define EDAC_MAX_LAYERS 3 /** - * EDAC_DIMM_PTR - Macro responsible to find a pointer inside a pointer array + * EDAC_DIMM_OFF - Macro responsible to get a pointer offset inside a pointer array * for the element given by [layer0,layer1,layer2] position * * @layers: a struct edac_mc_layer array, describing how many elements * were allocated for each layer - * @var: name of the var where we want to get the pointer - * (like mci->dimms) * @n_layers: Number of layers at the @layers array * @layer0: layer0 position * @layer1: layer1 position. Unused if n_layers < 2 * @layer2: layer2 position. Unused if n_layers < 3 * - * For 1 layer, this macro returns &var[layer0] + * For 1 layer, this macro returns &var[layer0] - &var * For 2 layers, this macro is similar to allocate a bi-dimensional array - * and to return "&var[layer0][layer1]" + * and to return "&var[layer0][layer1] - &var" * For 3 layers, this macro is similar to allocate a tri-dimensional array - * and to return "&var[layer0][layer1][layer2]" + * and to return "&var[layer0][layer1][layer2] - &var" * * A loop could be used here to make it more generic, but, as we only have * 3 layers, this is a little faster. @@ -436,17 +434,46 @@ struct edac_mc_layer { * a NULL is returned, causing an OOPS during the memory allocation routine, * with would point to the developer that he's doing something wrong. */ -#define EDAC_DIMM_PTR(layers, var, nlayers, layer0, layer1, layer2) ({ \ - typeof(var) __p; \ +#define EDAC_DIMM_OFF(layers, nlayers, layer0, layer1, layer2) ({ \ + int __i; \ if ((nlayers) == 1) \ - __p = &var[layer0]; \ + __i = layer0; \ else if ((nlayers) == 2) \ - __p = &var[(layer1) + ((layers[1]).size * (layer0))]; \ + __i = (layer1) + ((layers[1]).size * (layer0)); \ else if ((nlayers) == 3) \ - __p = &var[(layer2) + ((layers[2]).size * ((layer1) + \ - ((layers[1]).size * (layer0))))]; \ + __i = (layer2) + ((layers[2]).size * ((layer1) + \ + ((layers[1]).size * (layer0)))); \ else \ + __i = -EINVAL; \ + __i; \ +}) + +/** + * EDAC_DIMM_PTR - Macro responsible to get a pointer inside a pointer array + * for the element given by [layer0,layer1,layer2] position + * + * @layers: a struct edac_mc_layer array, describing how many elements + * were allocated for each layer + * @var: name of the var where we want to get the pointer + * (like mci->dimms) + * @n_layers: Number of layers at the @layers array + * @layer0: layer0 position + * @layer1: layer1 position. Unused if n_layers < 2 + * @layer2: layer2 position. Unused if n_layers < 3 + * + * For 1 layer, this macro returns &var[layer0] + * For 2 layers, this macro is similar to allocate a bi-dimensional array + * and to return "&var[layer0][layer1]" + * For 3 layers, this macro is similar to allocate a tri-dimensional array + * and to return "&var[layer0][layer1][layer2]" + */ +#define EDAC_DIMM_PTR(layers, var, nlayers, layer0, layer1, layer2) ({ \ + typeof(*var) __p; \ + int ___i = EDAC_DIMM_OFF(layers, nlayers, layer0, layer1, layer2); \ + if (___i < 0) \ __p = NULL; \ + else \ + __p = (var)[___i]; \ __p; \ }) @@ -486,8 +513,6 @@ struct dimm_info { * patches in this series will fix this issue. */ struct rank_info { - struct device dev; - int chan_idx; struct csrow_info *csrow; struct dimm_info *dimm; @@ -513,7 +538,7 @@ struct csrow_info { /* channel information for this csrow */ u32 nr_channels; - struct rank_info *channels; + struct rank_info **channels; }; /* @@ -572,7 +597,7 @@ struct mem_ctl_info { unsigned long (*ctl_page_to_phys) (struct mem_ctl_info * mci, unsigned long page); int mc_idx; - struct csrow_info *csrows; + struct csrow_info **csrows; unsigned nr_csrows, num_cschannel; /* @@ -592,7 +617,7 @@ struct mem_ctl_info { * DIMM info. Will eventually remove the entire csrows_info some day */ unsigned tot_dimms; - struct dimm_info *dimms; + struct dimm_info **dimms; /* * FIXME - what about controllers on other busses? - IDs must be -- cgit v1.2.3 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') 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 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') 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 From 55afabaa0df0dd139c8796a71beb43d1216fbe43 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 11 Jun 2012 15:52:29 -0700 Subject: inet: Fix BUG triggered by __rt{,6}_get_peer(). If no peer actually gets attached (either because create is zero or the peer allocation fails) we'll trigger a BUG because we unconditionally do an rt{,6}_peer_ptr() afterwards. Fix this by guarding it with the proper check. Signed-off-by: David S. Miller --- include/net/ip6_route.h | 2 +- include/net/route.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index f88a85cf31c3..a2cda240ca95 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -61,7 +61,7 @@ static inline struct inet_peer *__rt6_get_peer(struct rt6_info *rt, int create) return rt6_peer_ptr(rt); rt6_bind_peer(rt, create); - return rt6_peer_ptr(rt); + return (rt6_has_peer(rt) ? rt6_peer_ptr(rt) : NULL); } static inline struct inet_peer *rt6_get_peer(struct rt6_info *rt) diff --git a/include/net/route.h b/include/net/route.h index cc693a5bb20d..2bfbc9329ea9 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -334,7 +334,7 @@ static inline struct inet_peer *__rt_get_peer(struct rtable *rt, __be32 daddr, i return rt_peer_ptr(rt); rt_bind_peer(rt, daddr, create); - return rt_peer_ptr(rt); + return (rt_has_peer(rt) ? rt_peer_ptr(rt) : NULL); } static inline struct inet_peer *rt_get_peer(struct rtable *rt, __be32 daddr) -- cgit v1.2.3 From a6e51c1e3425fe61040e9e770f514cfdf576a1f0 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Fri, 8 Jun 2012 12:22:46 +0200 Subject: staging: Delete if_strip.h Commit f80a3f62383bf673c310926d55142d51f118926d ("Staging: strip: delete the driver") left if_strip.h unused: nothing in the tree includes it anymore. It is still exported, but since nothing in the kernel uses struct MetricomAddress, that seems pointless. Delete this header too. Signed-off-by: Paul Bolle Signed-off-by: Greg Kroah-Hartman --- include/linux/Kbuild | 1 - include/linux/if_strip.h | 27 --------------------------- 2 files changed, 28 deletions(-) delete mode 100644 include/linux/if_strip.h (limited to 'include') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 8760be30b375..9366142ec7e5 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -183,7 +183,6 @@ header-y += if_ppp.h header-y += if_pppol2tp.h header-y += if_pppox.h header-y += if_slip.h -header-y += if_strip.h header-y += if_team.h header-y += if_tun.h header-y += if_tunnel.h diff --git a/include/linux/if_strip.h b/include/linux/if_strip.h deleted file mode 100644 index 6526a6235832..000000000000 --- a/include/linux/if_strip.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * if_strip.h -- - * - * Definitions for the STRIP interface - * - * Copyright 1996 The Board of Trustees of The Leland Stanford - * Junior University. All Rights Reserved. - * - * Permission to use, copy, modify, and distribute this - * software and its documentation for any purpose and without - * fee is hereby granted, provided that the above copyright - * notice appear in all copies. Stanford University - * makes no representations about the suitability of this - * software for any purpose. It is provided "as is" without - * express or implied warranty. - */ - -#ifndef __LINUX_STRIP_H -#define __LINUX_STRIP_H - -#include - -typedef struct { - __u8 c[6]; -} MetricomAddress; - -#endif -- cgit v1.2.3 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') 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 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') 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 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') 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 From 5f246e890502fed387e0f959e2224ea680c03423 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 11 Jun 2012 11:13:07 +0300 Subject: Bluetooth: Update HCI timeouts constants to use msecs_to_jiffies The HCI constants are always used in form of jiffies. So just include the conversion from msecs in the define itself. This has the advantage of making the code where the timeout is used more readable and avoiding unnecessary conversions. The patch is similar to commit ba13ccd9 doing the same job for L2CAP Reported-by: Marcel Holtmann Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 10 +++++----- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 25 +++++++++++-------------- 3 files changed, 17 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 2a6b0b8b7120..7dcd3495edde 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -139,11 +139,11 @@ enum { #define HCIINQUIRY _IOR('H', 240, int) /* HCI timeouts */ -#define HCI_DISCONN_TIMEOUT (2000) /* 2 seconds */ -#define HCI_PAIRING_TIMEOUT (60000) /* 60 seconds */ -#define HCI_INIT_TIMEOUT (10000) /* 10 seconds */ -#define HCI_CMD_TIMEOUT (1000) /* 1 seconds */ -#define HCI_ACL_TX_TIMEOUT (45000) /* 45 seconds */ +#define HCI_DISCONN_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ +#define HCI_PAIRING_TIMEOUT msecs_to_jiffies(60000) /* 60 seconds */ +#define HCI_INIT_TIMEOUT msecs_to_jiffies(10000) /* 10 seconds */ +#define HCI_CMD_TIMEOUT msecs_to_jiffies(1000) /* 1 seconds */ +#define HCI_ACL_TX_TIMEOUT msecs_to_jiffies(45000) /* 45 seconds */ /* HCI data types */ #define HCI_COMMAND_PKT 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 20fd57367ddc..75766b7f0dc7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -598,7 +598,7 @@ static inline void hci_conn_put(struct hci_conn *conn) if (conn->type == ACL_LINK || conn->type == LE_LINK) { del_timer(&conn->idle_timer); if (conn->state == BT_CONNECTED) { - timeo = msecs_to_jiffies(conn->disc_timeout); + timeo = conn->disc_timeout; if (!conn->out) timeo *= 2; } else { diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 471e4fb1b6e5..e91bf7e15666 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -690,12 +690,11 @@ int hci_dev_open(__u16 dev) set_bit(HCI_INIT, &hdev->flags); hdev->init_last_cmd = 0; - ret = __hci_request(hdev, hci_init_req, 0, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); + ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT); if (lmp_host_le_capable(hdev)) ret = __hci_request(hdev, hci_le_init_req, 0, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); + HCI_INIT_TIMEOUT); clear_bit(HCI_INIT, &hdev->flags); } @@ -782,8 +781,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) if (!test_bit(HCI_RAW, &hdev->flags) && test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) { set_bit(HCI_INIT, &hdev->flags); - __hci_request(hdev, hci_reset_req, 0, - msecs_to_jiffies(HCI_CMD_TIMEOUT)); + __hci_request(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT); clear_bit(HCI_INIT, &hdev->flags); } @@ -872,8 +870,7 @@ int hci_dev_reset(__u16 dev) hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0; if (!test_bit(HCI_RAW, &hdev->flags)) - ret = __hci_request(hdev, hci_reset_req, 0, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); + ret = __hci_request(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT); done: hci_req_unlock(hdev); @@ -913,7 +910,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) switch (cmd) { case HCISETAUTH: err = hci_request(hdev, hci_auth_req, dr.dev_opt, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); + HCI_INIT_TIMEOUT); break; case HCISETENCRYPT: @@ -925,23 +922,23 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) if (!test_bit(HCI_AUTH, &hdev->flags)) { /* Auth must be enabled first */ err = hci_request(hdev, hci_auth_req, dr.dev_opt, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); + HCI_INIT_TIMEOUT); if (err) break; } err = hci_request(hdev, hci_encrypt_req, dr.dev_opt, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); + HCI_INIT_TIMEOUT); break; case HCISETSCAN: err = hci_request(hdev, hci_scan_req, dr.dev_opt, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); + HCI_INIT_TIMEOUT); break; case HCISETLINKPOL: err = hci_request(hdev, hci_linkpol_req, dr.dev_opt, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); + HCI_INIT_TIMEOUT); break; case HCISETLINKMODE: @@ -2455,7 +2452,7 @@ static void __check_timeout(struct hci_dev *hdev, unsigned int cnt) /* ACL tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ if (!cnt && time_after(jiffies, hdev->acl_last_tx + - msecs_to_jiffies(HCI_ACL_TX_TIMEOUT))) + HCI_ACL_TX_TIMEOUT)) hci_link_tx_to(hdev, ACL_LINK); } } @@ -2839,7 +2836,7 @@ static void hci_cmd_work(struct work_struct *work) del_timer(&hdev->cmd_timer); else mod_timer(&hdev->cmd_timer, - jiffies + msecs_to_jiffies(HCI_CMD_TIMEOUT)); + jiffies + HCI_CMD_TIMEOUT); } else { skb_queue_head(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 From dd2757f8b557ab2030154896eac9b2285557dda6 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 7 Jun 2012 15:55:57 +0200 Subject: drm/i915: stop using dev->agp->base For that to work we need to export the base address of the gtt mmio window from intel-gtt. Also replace all other uses of dev->agp by values we already have at hand. Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 5 ++--- drivers/gpu/drm/i915/i915_dma.c | 21 +++++++++++++-------- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_gem.c | 2 +- drivers/gpu/drm/i915/i915_gem_debug.c | 3 ++- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/i915/intel_fb.c | 4 +++- drivers/gpu/drm/i915/intel_ringbuffer.c | 6 ++++-- include/drm/intel-gtt.h | 2 ++ 9 files changed, 29 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 53c4c7fca10b..2aab0a03ee42 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -66,7 +66,6 @@ static struct _intel_private { struct pci_dev *bridge_dev; u8 __iomem *registers; phys_addr_t gtt_bus_addr; - phys_addr_t gma_bus_addr; u32 PGETBL_save; u32 __iomem *gtt; /* I915G */ bool clear_fake_agp; /* on first access via agp, fill with scratch */ @@ -779,7 +778,7 @@ static bool intel_enable_gtt(void) pci_read_config_dword(intel_private.pcidev, I915_GMADDR, &gma_addr); - intel_private.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK); + intel_private.base.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK); if (INTEL_GTT_GEN >= 6) return true; @@ -860,7 +859,7 @@ static int intel_fake_agp_configure(void) return -EIO; intel_private.clear_fake_agp = true; - agp_bridge->gart_bus_addr = intel_private.gma_bus_addr; + agp_bridge->gart_bus_addr = intel_private.base.gma_bus_addr; return 0; } diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 97a5a5857f5b..c639d431ad66 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1085,8 +1085,8 @@ static int i915_set_status_page(struct drm_device *dev, void *data, ring->status_page.gfx_addr = hws->addr & (0x1ffff<<12); - dev_priv->dri1.gfx_hws_cpu_addr = ioremap_wc(dev->agp->base + hws->addr, - 4096); + dev_priv->dri1.gfx_hws_cpu_addr = + ioremap_wc(dev_priv->mm.gtt_base_addr + hws->addr, 4096); if (dev_priv->dri1.gfx_hws_cpu_addr == NULL) { i915_dma_cleanup(dev); ring->status_page.gfx_addr = 0; @@ -1482,15 +1482,18 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) } aperture_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + dev_priv->mm.gtt_base_addr = dev_priv->mm.gtt->gma_bus_addr; dev_priv->mm.gtt_mapping = - io_mapping_create_wc(dev->agp->base, aperture_size); + io_mapping_create_wc(dev_priv->mm.gtt_base_addr, + aperture_size); if (dev_priv->mm.gtt_mapping == NULL) { ret = -EIO; goto out_rmmap; } - i915_mtrr_setup(dev_priv, dev->agp->base, aperture_size); + i915_mtrr_setup(dev_priv, dev_priv->mm.gtt_base_addr, + aperture_size); /* The i915 workqueue is primarily used for batched retirement of * requests (and thus managing bo) once the task has been completed @@ -1602,8 +1605,9 @@ out_gem_unload: destroy_workqueue(dev_priv->wq); out_mtrrfree: if (dev_priv->mm.gtt_mtrr >= 0) { - mtrr_del(dev_priv->mm.gtt_mtrr, dev->agp->base, - dev->agp->agp_info.aper_size * 1024 * 1024); + mtrr_del(dev_priv->mm.gtt_mtrr, + dev_priv->mm.gtt_base_addr, + aperture_size); dev_priv->mm.gtt_mtrr = -1; } io_mapping_free(dev_priv->mm.gtt_mapping); @@ -1640,8 +1644,9 @@ int i915_driver_unload(struct drm_device *dev) io_mapping_free(dev_priv->mm.gtt_mapping); if (dev_priv->mm.gtt_mtrr >= 0) { - mtrr_del(dev_priv->mm.gtt_mtrr, dev->agp->base, - dev->agp->agp_info.aper_size * 1024 * 1024); + mtrr_del(dev_priv->mm.gtt_mtrr, + dev_priv->mm.gtt_base_addr, + dev_priv->mm.gtt->gtt_mappable_entries * PAGE_SIZE); dev_priv->mm.gtt_mtrr = -1; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ccabadd2b6c3..ae4129b3cb3e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -651,6 +651,7 @@ typedef struct drm_i915_private { unsigned long gtt_end; struct io_mapping *gtt_mapping; + phys_addr_t gtt_base_addr; int gtt_mtrr; /** PPGTT used for aliasing the PPGTT with the GTT */ diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index deaa0d4bb456..108e4c2b5ffa 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1122,7 +1122,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) obj->fault_mappable = true; - pfn = ((dev->agp->base + obj->gtt_offset) >> PAGE_SHIFT) + + pfn = ((dev_priv->mm.gtt_base_addr + obj->gtt_offset) >> PAGE_SHIFT) + page_offset; /* Finally, remap it using the new GTT offset */ diff --git a/drivers/gpu/drm/i915/i915_gem_debug.c b/drivers/gpu/drm/i915/i915_gem_debug.c index a4f6aaabca99..bddf7bed183f 100644 --- a/drivers/gpu/drm/i915/i915_gem_debug.c +++ b/drivers/gpu/drm/i915/i915_gem_debug.c @@ -132,7 +132,8 @@ i915_gem_object_check_coherency(struct drm_i915_gem_object *obj, int handle) __func__, obj, obj->gtt_offset, handle, obj->size / 1024); - gtt_mapping = ioremap(dev->agp->base + obj->gtt_offset, obj->base.size); + gtt_mapping = ioremap(dev_priv->mm.gtt_base_addr + obj->gtt_offset, + obj->base.size); if (gtt_mapping == NULL) { DRM_ERROR("failed to map GTT space\n"); return; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index bad36e0b8045..174549df5929 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6954,7 +6954,7 @@ void intel_modeset_init(struct drm_device *dev) dev->mode_config.max_width = 8192; dev->mode_config.max_height = 8192; } - dev->mode_config.fb_base = dev->agp->base; + dev->mode_config.fb_base = dev_priv->mm.gtt_base_addr; DRM_DEBUG_KMS("%d display pipe%s available.\n", dev_priv->num_pipe, dev_priv->num_pipe > 1 ? "s" : ""); diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index bf8690720a0c..e9f8338bd802 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -140,7 +140,9 @@ static int intelfb_create(struct intel_fbdev *ifbdev, info->fix.smem_start = dev->mode_config.fb_base + obj->gtt_offset; info->fix.smem_len = size; - info->screen_base = ioremap_wc(dev->agp->base + obj->gtt_offset, size); + info->screen_base = + ioremap_wc(dev_priv->mm.gtt_base_addr + obj->gtt_offset, + size); if (!info->screen_base) { ret = -ENOSPC; goto out_unpin; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 89a5e7f89d7a..14025ab9d4ca 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -968,6 +968,7 @@ static int intel_init_ring_buffer(struct drm_device *dev, struct intel_ring_buffer *ring) { struct drm_i915_gem_object *obj; + struct drm_i915_private *dev_priv = dev->dev_private; int ret; ring->dev = dev; @@ -997,8 +998,9 @@ static int intel_init_ring_buffer(struct drm_device *dev, if (ret) goto err_unref; - ring->virtual_start = ioremap_wc(dev->agp->base + obj->gtt_offset, - ring->size); + ring->virtual_start = + ioremap_wc(dev_priv->mm.gtt->gma_bus_addr + obj->gtt_offset, + ring->size); if (ring->virtual_start == NULL) { DRM_ERROR("Failed to map ringbuffer.\n"); ret = -EINVAL; diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 923afb5dcf0c..8048c005c6f6 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -19,6 +19,8 @@ const struct intel_gtt { dma_addr_t scratch_page_dma; /* for ppgtt PDE access */ u32 __iomem *gtt; + /* needed for ioremap in drm/i915 */ + phys_addr_t gma_bus_addr; } *intel_gtt_get(void); void intel_gtt_chipset_flush(void); -- cgit v1.2.3 From 14be93ddff61eb196382aeaa3ac86f4db844aeb0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 8 Jun 2012 15:55:40 +0200 Subject: drm/i915 + agp/intel-gtt: prep work for direct setup To be able to directly set up the intel-gtt code from drm/i915 and avoid setting up the fake-agp driver we need to prepare a few things: - pass both the bridge and gpu pci_dev to the probe function and add code to handle the gpu pdev both being present (for drm/i915) and not present (fake agp). - add refcounting to the remove function so that unloading drm/i915 doesn't kill the fake agp driver v2: Fix up the cleanup and refcount, noticed by Jani Nikula. Reviewed-by: Jani Nikula Signed-Off-by: Daniel Vetter --- drivers/char/agp/intel-agp.c | 5 +++-- drivers/char/agp/intel-agp.h | 3 --- drivers/char/agp/intel-gtt.c | 46 +++++++++++++++++++++++++++++++++-------- drivers/gpu/drm/i915/i915_dma.c | 13 ++++++++++-- include/drm/intel-gtt.h | 4 ++++ 5 files changed, 55 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 764f70c5e690..c98c5689bb0b 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -12,6 +12,7 @@ #include #include "agp.h" #include "intel-agp.h" +#include int intel_agp_enabled; EXPORT_SYMBOL(intel_agp_enabled); @@ -747,7 +748,7 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, bridge->capndx = cap_ptr; - if (intel_gmch_probe(pdev, bridge)) + if (intel_gmch_probe(pdev, NULL, bridge)) goto found_gmch; for (i = 0; intel_agp_chipsets[i].name != NULL; i++) { @@ -824,7 +825,7 @@ static void __devexit agp_intel_remove(struct pci_dev *pdev) agp_remove_bridge(bridge); - intel_gmch_remove(pdev); + intel_gmch_remove(); agp_put_bridge(bridge); } diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h index c0091753a0d1..cf2e764b1760 100644 --- a/drivers/char/agp/intel-agp.h +++ b/drivers/char/agp/intel-agp.h @@ -250,7 +250,4 @@ #define PCI_DEVICE_ID_INTEL_HASWELL_SDV 0x0c16 /* SDV */ #define PCI_DEVICE_ID_INTEL_HASWELL_E_HB 0x0c04 -int intel_gmch_probe(struct pci_dev *pdev, - struct agp_bridge_data *bridge); -void intel_gmch_remove(struct pci_dev *pdev); #endif diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 5e6c89e1d5eb..cea9f9905c7d 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -75,6 +75,7 @@ static struct _intel_private { struct resource ifp_resource; int resource_valid; struct page *scratch_page; + int refcount; } intel_private; #define INTEL_GTT_GEN intel_private.driver->gen @@ -1522,14 +1523,32 @@ static int find_gmch(u16 device) return 1; } -int intel_gmch_probe(struct pci_dev *pdev, - struct agp_bridge_data *bridge) +int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, + struct agp_bridge_data *bridge) { int i, mask; - intel_private.driver = NULL; + + /* + * Can be called from the fake agp driver but also directly from + * drm/i915.ko. Hence we need to check whether everything is set up + * already. + */ + if (intel_private.driver) { + intel_private.refcount++; + return 1; + } for (i = 0; intel_gtt_chipsets[i].name != NULL; i++) { - if (find_gmch(intel_gtt_chipsets[i].gmch_chip_id)) { + if (gpu_pdev) { + if (gpu_pdev->device == + intel_gtt_chipsets[i].gmch_chip_id) { + intel_private.pcidev = pci_dev_get(gpu_pdev); + intel_private.driver = + intel_gtt_chipsets[i].gtt_driver; + + break; + } + } else if (find_gmch(intel_gtt_chipsets[i].gmch_chip_id)) { intel_private.driver = intel_gtt_chipsets[i].gtt_driver; break; @@ -1539,15 +1558,17 @@ int intel_gmch_probe(struct pci_dev *pdev, if (!intel_private.driver) return 0; + intel_private.refcount++; + if (bridge) { bridge->driver = &intel_fake_agp_driver; bridge->dev_private_data = &intel_private; - bridge->dev = pdev; + bridge->dev = bridge_pdev; } - intel_private.bridge_dev = pci_dev_get(pdev); + intel_private.bridge_dev = pci_dev_get(bridge_pdev); - dev_info(&pdev->dev, "Intel %s Chipset\n", intel_gtt_chipsets[i].name); + dev_info(&bridge_pdev->dev, "Intel %s Chipset\n", intel_gtt_chipsets[i].name); mask = intel_private.driver->dma_mask_size; if (pci_set_dma_mask(intel_private.pcidev, DMA_BIT_MASK(mask))) @@ -1557,8 +1578,11 @@ int intel_gmch_probe(struct pci_dev *pdev, pci_set_consistent_dma_mask(intel_private.pcidev, DMA_BIT_MASK(mask)); - if (intel_gtt_init() != 0) + if (intel_gtt_init() != 0) { + intel_gmch_remove(); + return 0; + } return 1; } @@ -1577,12 +1601,16 @@ void intel_gtt_chipset_flush(void) } EXPORT_SYMBOL(intel_gtt_chipset_flush); -void intel_gmch_remove(struct pci_dev *pdev) +void intel_gmch_remove(void) { + if (--intel_private.refcount) + return; + if (intel_private.pcidev) pci_dev_put(intel_private.pcidev); if (intel_private.bridge_dev) pci_dev_put(intel_private.bridge_dev); + intel_private.driver = NULL; } EXPORT_SYMBOL(intel_gmch_remove); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index c639d431ad66..cf512e7178b4 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1474,11 +1474,18 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto put_bridge; } + ret = intel_gmch_probe(dev_priv->bridge_dev, dev->pdev, NULL); + if (!ret) { + DRM_ERROR("failed to set up gmch\n"); + ret = -EIO; + goto out_rmmap; + } + dev_priv->mm.gtt = intel_gtt_get(); if (!dev_priv->mm.gtt) { DRM_ERROR("Failed to initialize GTT\n"); ret = -ENODEV; - goto out_rmmap; + goto put_gmch; } aperture_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; @@ -1489,7 +1496,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) aperture_size); if (dev_priv->mm.gtt_mapping == NULL) { ret = -EIO; - goto out_rmmap; + goto put_gmch; } i915_mtrr_setup(dev_priv, dev_priv->mm.gtt_base_addr, @@ -1611,6 +1618,8 @@ out_mtrrfree: dev_priv->mm.gtt_mtrr = -1; } io_mapping_free(dev_priv->mm.gtt_mapping); +put_gmch: + intel_gmch_remove(); out_rmmap: pci_iounmap(dev->pdev, dev_priv->regs); put_bridge: diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 8048c005c6f6..84ebd7188fc6 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -23,6 +23,10 @@ const struct intel_gtt { phys_addr_t gma_bus_addr; } *intel_gtt_get(void); +int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, + struct agp_bridge_data *bridge); +void intel_gmch_remove(void); + void intel_gtt_chipset_flush(void); void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg); void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries); -- cgit v1.2.3 From 8ecd1a6615f0d9de6759aafe229bc1cc4ee99c7b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 7 Jun 2012 15:56:03 +0200 Subject: drm/i915: call intel_enable_gtt When drm/i915 is in control of the gtt, we need to call the enable function at all the relevant places ourselves. Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 3 ++- drivers/gpu/drm/i915/i915_gem.c | 3 +++ include/drm/intel-gtt.h | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 4387e69f8b11..419a25eeefd8 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -777,7 +777,7 @@ static void i830_write_entry(dma_addr_t addr, unsigned int entry, writel(addr | pte_flags, intel_private.gtt + entry); } -static bool intel_enable_gtt(void) +bool intel_enable_gtt(void) { u8 __iomem *reg; @@ -823,6 +823,7 @@ static bool intel_enable_gtt(void) return true; } +EXPORT_SYMBOL(intel_enable_gtt); static int i830_setup(void) { diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 108e4c2b5ffa..2884b0865473 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3689,6 +3689,9 @@ i915_gem_init_hw(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; int ret; + if (!intel_enable_gtt()) + return -EIO; + i915_gem_l3_remap(dev); i915_gem_init_swizzling(dev); diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 84ebd7188fc6..8e29d551bb3c 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -27,6 +27,8 @@ int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, struct agp_bridge_data *bridge); void intel_gmch_remove(void); +bool intel_enable_gtt(void); + void intel_gtt_chipset_flush(void); void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg); void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries); -- cgit v1.2.3 From cf35ad61aca2c0c8983fa1e140c901f6588aba7e Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Fri, 8 Jun 2012 18:06:45 +0200 Subject: iio: add mcp4725 I2C DAC driver v5: * fix warnings (Jonathan Cameron) v4: * remove unused indio_dev pointer in mcp4725_data (Jonathan Cameron) * use u16 instead of unsigned short in mcp4725_data (Jonathan Cameron) * #include mcp4725.h from linux/iio/dac/ v3: * move from staging to drivers/iio * switch to chan_spec * dev_get_drvdata() -> dev_to_iio_dev() * annotate probe() and remove() with __devinit and __devexit v2 (based on comments from Jonathan Cameron and Lars-Peter Clausen): * did NOT switch to chan_spec yet * rebase to staging-next tree, update iio header locations * dropped dac.h #include, not needed * strict_strtol() -> kstrtol() * call iio_device_unregister() in remove() * everything in one patch Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/dac/Kconfig | 11 ++ drivers/iio/dac/Makefile | 1 + drivers/iio/dac/mcp4725.c | 227 ++++++++++++++++++++++++++++++++++++++++ include/linux/iio/dac/mcp4725.h | 16 +++ 4 files changed, 255 insertions(+) create mode 100644 drivers/iio/dac/mcp4725.c create mode 100644 include/linux/iio/dac/mcp4725.h (limited to 'include') diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index a626f03871ec..92fb3a003510 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -118,4 +118,15 @@ config MAX517 This driver can also be built as a module. If so, the module will be called max517. +config MCP4725 + tristate "MCP4725 DAC driver" + depends on I2C + ---help--- + Say Y here if you want to build a driver for the Microchip + MCP 4725 12-bit digital-to-analog converter (DAC) with I2C + interface. + + To compile this driver as a module, choose M here: the module + will be called mcp4725. + endmenu diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 8ab1d264aab7..9ea3ceeefc07 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o obj-$(CONFIG_MAX517) += max517.o +obj-$(CONFIG_MCP4725) += mcp4725.o diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c new file mode 100644 index 000000000000..e0e168bd5b45 --- /dev/null +++ b/drivers/iio/dac/mcp4725.c @@ -0,0 +1,227 @@ +/* + * mcp4725.c - Support for Microchip MCP4725 + * + * Copyright (C) 2012 Peter Meerwald + * + * Based on max517 by Roland Stigge + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * driver for the Microchip I2C 12-bit digital-to-analog converter (DAC) + * (7-bit I2C slave address 0x60, the three LSBs can be configured in + * hardware) + * + * writing the DAC value to EEPROM is not supported + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#define MCP4725_DRV_NAME "mcp4725" + +struct mcp4725_data { + struct i2c_client *client; + u16 vref_mv; + u16 dac_value; +}; + +#ifdef CONFIG_PM_SLEEP +static int mcp4725_suspend(struct device *dev) +{ + u8 outbuf[2]; + + outbuf[0] = 0x3 << 4; /* power-down bits, 500 kOhm resistor */ + outbuf[1] = 0; + + return i2c_master_send(to_i2c_client(dev), outbuf, 2); +} + +static int mcp4725_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mcp4725_data *data = iio_priv(indio_dev); + u8 outbuf[2]; + + /* restore previous DAC value */ + outbuf[0] = (data->dac_value >> 8) & 0xf; + outbuf[1] = data->dac_value & 0xff; + + return i2c_master_send(to_i2c_client(dev), outbuf, 2); +} + +static SIMPLE_DEV_PM_OPS(mcp4725_pm_ops, mcp4725_suspend, mcp4725_resume); +#define MCP4725_PM_OPS (&mcp4725_pm_ops) +#else +#define MCP4725_PM_OPS NULL +#endif + +static const struct iio_chan_spec mcp4725_channel = { + .type = IIO_VOLTAGE, + .indexed = 1, + .output = 1, + .channel = 0, + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_type = IIO_ST('u', 12, 16, 0), +}; + +static int mcp4725_set_value(struct iio_dev *indio_dev, int val) +{ + struct mcp4725_data *data = iio_priv(indio_dev); + u8 outbuf[2]; + int ret; + + if (val >= (1 << 12) || val < 0) + return -EINVAL; + + outbuf[0] = (val >> 8) & 0xf; + outbuf[1] = val & 0xff; + + ret = i2c_master_send(data->client, outbuf, 2); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + else + return 0; +} + +static int mcp4725_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mcp4725_data *data = iio_priv(indio_dev); + unsigned long scale_uv; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = data->dac_value; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = (data->vref_mv * 1000) >> 12; + *val = scale_uv / 1000000; + *val2 = scale_uv % 1000000; + return IIO_VAL_INT_PLUS_MICRO; + } + return -EINVAL; +} + +static int mcp4725_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct mcp4725_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = mcp4725_set_value(indio_dev, val); + data->dac_value = val; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct iio_info mcp4725_info = { + .read_raw = mcp4725_read_raw, + .write_raw = mcp4725_write_raw, + .driver_module = THIS_MODULE, +}; + +static int __devinit mcp4725_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mcp4725_data *data; + struct iio_dev *indio_dev; + struct mcp4725_platform_data *platform_data = client->dev.platform_data; + u8 inbuf[3]; + int err; + + if (!platform_data || !platform_data->vref_mv) { + dev_err(&client->dev, "invalid platform data"); + err = -EINVAL; + goto exit; + } + + indio_dev = iio_device_alloc(sizeof(*data)); + if (indio_dev == NULL) { + err = -ENOMEM; + goto exit; + } + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &mcp4725_info; + indio_dev->channels = &mcp4725_channel; + indio_dev->num_channels = 1; + indio_dev->modes = INDIO_DIRECT_MODE; + + data->vref_mv = platform_data->vref_mv; + + /* read current DAC value */ + err = i2c_master_recv(client, inbuf, 3); + if (err < 0) { + dev_err(&client->dev, "failed to read DAC value"); + goto exit_free_device; + } + data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4); + + err = iio_device_register(indio_dev); + if (err) + goto exit_free_device; + + dev_info(&client->dev, "MCP4725 DAC registered\n"); + + return 0; + +exit_free_device: + iio_device_free(indio_dev); +exit: + return err; +} + +static int __devexit mcp4725_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + iio_device_free(indio_dev); + + return 0; +} + +static const struct i2c_device_id mcp4725_id[] = { + { "mcp4725", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcp4725_id); + +static struct i2c_driver mcp4725_driver = { + .driver = { + .name = MCP4725_DRV_NAME, + .pm = MCP4725_PM_OPS, + }, + .probe = mcp4725_probe, + .remove = __devexit_p(mcp4725_remove), + .id_table = mcp4725_id, +}; +module_i2c_driver(mcp4725_driver); + +MODULE_AUTHOR("Peter Meerwald "); +MODULE_DESCRIPTION("MCP4725 12-bit DAC"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/iio/dac/mcp4725.h b/include/linux/iio/dac/mcp4725.h new file mode 100644 index 000000000000..91530e6611e9 --- /dev/null +++ b/include/linux/iio/dac/mcp4725.h @@ -0,0 +1,16 @@ +/* + * MCP4725 DAC driver + * + * Copyright (C) 2012 Peter Meerwald + * + * Licensed under the GPL-2 or later. + */ + +#ifndef IIO_DAC_MCP4725_H_ +#define IIO_DAC_MCP4725_H_ + +struct mcp4725_platform_data { + u16 vref_mv; +}; + +#endif /* IIO_DAC_MCP4725_H_ */ -- cgit v1.2.3 From 988bb033d703c8b2e0e71c63f3f55616a0220ced Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Tue, 12 Jun 2012 14:39:37 +0200 Subject: iio: drop comment about 'real' channels Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/types.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 1b073b1cc7c2..d086736a9033 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -11,7 +11,6 @@ #define _IIO_TYPES_H_ enum iio_chan_type { - /* real channel types */ IIO_VOLTAGE, IIO_CURRENT, IIO_POWER, -- cgit v1.2.3 From 1787948873fd2ca730fb5891b6a2ade510367ace Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Tue, 12 Jun 2012 15:38:45 +0200 Subject: iio: fix typos in iio.h v2: * "used in in-kernel" (Jonathan Cameron) Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/iio.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 944c63511731..01afaa08ff21 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -131,7 +131,7 @@ struct iio_chan_spec_ext_info { /** * struct iio_enum - Enum channel info attribute - * items: A array of strings. + * items: An array of strings. * num_items: Length of the item array. * set: Set callback function, may be NULL. * get: Get callback function, may be NULL. @@ -218,7 +218,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, * @extend_name: Allows labeling of channel attributes with an * informative name. Note this has no effect codes etc, * unlike modifiers. - * @datasheet_name: A name used in in kernel mapping of channels. It should + * @datasheet_name: A name used in in-kernel mapping of channels. It should * correspond to the first name that the channel is referred * to by in the datasheet (e.g. IND), or the nearest * possible compound name (e.g. IND-INC). @@ -393,7 +393,7 @@ struct iio_buffer_setup_ops { * @trig: [INTERN] current device trigger (buffer modes) * @pollfunc: [DRIVER] function run on trigger being received * @channels: [DRIVER] channel specification structure table - * @num_channels: [DRIVER] number of chanels specified in @channels. + * @num_channels: [DRIVER] number of channels specified in @channels. * @channel_attr_list: [INTERN] keep track of automatically created channel * attributes * @chan_attr_group: [INTERN] group for all attrs in base directory -- cgit v1.2.3 From a16561c6f713630270b905297f3eedf02db7fa98 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Tue, 12 Jun 2012 15:38:46 +0200 Subject: iio: clarify channel and indexed in struct iio_chan_spec Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/iio.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 01afaa08ff21..9a9e6d52a6de 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -196,7 +196,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, /** * struct iio_chan_spec - specification of a single channel * @type: What type of measurement is the channel making. - * @channel: What number or name do we wish to assign the channel. + * @channel: What number do we wish to assign the channel. * @channel2: If there is a second number for a differential * channel then this is it. If modified is set then the * value here specifies the modifier. @@ -227,9 +227,8 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, * channel2. Examples are IIO_MOD_X for axial sensors about * the 'x' axis. * @indexed: Specify the channel has a numerical index. If not, - * the value in channel will be suppressed for attribute - * but not for event codes. Typically set it to 0 when - * the index is false. + * the channel index number will be suppressed for sysfs + * attributes but not for event codes. * @differential: Channel is differential. */ struct iio_chan_spec { -- cgit v1.2.3 From f4c349395e8ad4fe07f1222502568f0d9d5d1dfc Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Tue, 12 Jun 2012 15:38:47 +0200 Subject: iio: mark struct iio_enum elements with @ in comment Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/iio.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 9a9e6d52a6de..bd3e7217ceee 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -131,10 +131,10 @@ struct iio_chan_spec_ext_info { /** * struct iio_enum - Enum channel info attribute - * items: An array of strings. - * num_items: Length of the item array. - * set: Set callback function, may be NULL. - * get: Get callback function, may be NULL. + * @items: An array of strings. + * @num_items: Length of the item array. + * @set: Set callback function, may be NULL. + * @get: Get callback function, may be NULL. * * The iio_enum struct can be used to implement enum style channel attributes. * Enum style attributes are those which have a set of strings which map to -- cgit v1.2.3 From 7dcd7b60724cab551deba54f31752fa8959efab4 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Tue, 12 Jun 2012 15:38:48 +0200 Subject: iio: cleanup iio/iio.h indentation of parameter description, fix parameter name (@dev -> @indio_dev) in comments, IIO device info structure -> IIO device structure Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/iio.h | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index bd3e7217ceee..ae0cad908182 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -131,10 +131,10 @@ struct iio_chan_spec_ext_info { /** * struct iio_enum - Enum channel info attribute - * @items: An array of strings. - * @num_items: Length of the item array. - * @set: Set callback function, may be NULL. - * @get: Get callback function, may be NULL. + * @items: An array of strings. + * @num_items: Length of the item array. + * @set: Set callback function, may be NULL. + * @get: Get callback function, may be NULL. * * The iio_enum struct can be used to implement enum style channel attributes. * Enum style attributes are those which have a set of strings which map to @@ -162,9 +162,9 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, /** * IIO_ENUM() - Initialize enum extended channel attribute - * @_name: Attribute name - * @_shared: Whether the attribute is shared between all channels - * @_e: Pointer to a iio_enum struct + * @_name: Attribute name + * @_shared: Whether the attribute is shared between all channels + * @_e: Pointer to a iio_enum struct * * This should usually be used together with IIO_ENUM_AVAILABLE() */ @@ -179,8 +179,8 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, /** * IIO_ENUM_AVAILABLE() - Initialize enum available extended channel attribute - * @_name: Attribute name ("_available" will be appended to the name) - * @_e: Pointer to a iio_enum struct + * @_name: Attribute name ("_available" will be appended to the name) + * @_e: Pointer to a iio_enum struct * * Creates a read only attribute which list all the available enum items in a * space separated list. This should usually be used together with IIO_ENUM() @@ -201,7 +201,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, * channel then this is it. If modified is set then the * value here specifies the modifier. * @address: Driver specific identifier. - * @scan_index: Monotonic index to give ordering in scans when read + * @scan_index: Monotonic index to give ordering in scans when read * from a buffer. * @scan_type: Sign: 's' or 'u' to specify signed or unsigned * realbits: Number of valid bits of data @@ -211,7 +211,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, * endianness: little or big endian * @info_mask: What information is to be exported about this channel. * This includes calibbias, scale etc. - * @event_mask: What events can this channel produce. + * @event_mask: What events can this channel produce. * @ext_info: Array of extended info attributes for this channel. * The array is NULL terminated, the last element should * have it's name field set to NULL. @@ -482,7 +482,7 @@ extern struct bus_type iio_bus_type; /** * iio_device_put() - reference counted deallocation of struct device - * @dev: the iio_device containing the device + * @indio_dev: IIO device structure containing the device **/ static inline void iio_device_put(struct iio_dev *indio_dev) { @@ -492,7 +492,7 @@ static inline void iio_device_put(struct iio_dev *indio_dev) /** * dev_to_iio_dev() - Get IIO device struct from a device struct - * @dev: The device embedded in the IIO device + * @dev: The device embedded in the IIO device * * Note: The device must be a IIO device, otherwise the result is undefined. */ @@ -503,7 +503,7 @@ static inline struct iio_dev *dev_to_iio_dev(struct device *dev) /** * iio_device_get() - increment reference count for the device - * @indio_dev: IIO device structure + * @indio_dev: IIO device structure * * Returns: The passed IIO device **/ @@ -516,7 +516,7 @@ static inline struct iio_dev *iio_device_get(struct iio_dev *indio_dev) #define IIO_ALIGN L1_CACHE_BYTES /** * iio_device_alloc() - allocate an iio_dev from a driver - * @sizeof_priv: Space to allocate for private structure. + * @sizeof_priv: Space to allocate for private structure. **/ struct iio_dev *iio_device_alloc(int sizeof_priv); @@ -533,13 +533,13 @@ static inline struct iio_dev *iio_priv_to_dev(void *priv) /** * iio_device_free() - free an iio_dev from a driver - * @dev: the iio_dev associated with the device + * @indio_dev: the iio_dev associated with the device **/ void iio_device_free(struct iio_dev *indio_dev); /** * iio_buffer_enabled() - helper function to test if the buffer is enabled - * @indio_dev: IIO device info structure for device + * @indio_dev: IIO device structure for device **/ static inline bool iio_buffer_enabled(struct iio_dev *indio_dev) { @@ -549,7 +549,7 @@ static inline bool iio_buffer_enabled(struct iio_dev *indio_dev) /** * iio_get_debugfs_dentry() - helper function to get the debugfs_dentry - * @indio_dev: IIO device info structure for device + * @indio_dev: IIO device structure for device **/ #if defined(CONFIG_DEBUG_FS) static inline struct dentry *iio_get_debugfs_dentry(struct iio_dev *indio_dev) -- cgit v1.2.3 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') 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 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') 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 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') 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 From af7985bf85840e3dc90ba108a679db044f91f00e Mon Sep 17 00:00:00 2001 From: Jefferson Delfes Date: Mon, 11 Jun 2012 09:18:51 -0400 Subject: Bluetooth: Fix flags of mgmt_device_found event Change flags field to matches userspace structure. This field needs to be converted to little endian before forward it. Signed-off-by: Jefferson Delfes Signed-off-by: Gustavo Padovan --- include/net/bluetooth/mgmt.h | 2 +- net/bluetooth/mgmt.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 23fd0546fccb..4348ee8bda69 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -444,7 +444,7 @@ struct mgmt_ev_auth_failed { struct mgmt_ev_device_found { struct mgmt_addr_info addr; __s8 rssi; - __u8 flags[4]; + __le32 flags; __le16 eir_len; __u8 eir[0]; } __packed; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c72307cc25fc..b4816632d724 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3546,9 +3546,9 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ev->addr.type = link_to_bdaddr(link_type, addr_type); ev->rssi = rssi; if (cfm_name) - ev->flags[0] |= MGMT_DEV_FOUND_CONFIRM_NAME; + ev->flags |= MGMT_DEV_FOUND_CONFIRM_NAME; if (!ssp) - ev->flags[0] |= MGMT_DEV_FOUND_LEGACY_PAIRING; + ev->flags |= MGMT_DEV_FOUND_LEGACY_PAIRING; if (eir_len > 0) memcpy(ev->eir, eir, eir_len); @@ -3558,6 +3558,7 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, dev_class, 3); ev->eir_len = cpu_to_le16(eir_len); + ev->flags = cpu_to_le32(ev->flags); ev_size = sizeof(*ev) + eir_len; -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 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') 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 From 4527715979a34601b783f5f12774586c679c2a89 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 28 May 2012 23:31:39 -0700 Subject: usb: renesas_usbhs: add DMAEngine ID specification note renesas_usbhs DMAEngine uses D0FIFO/D1FIFO, but the data transfer direction should be fixed for keeping consistency. This patch explain about it on renesas_usbhs.h Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/renesas_usbhs.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 547e59cc00ea..c5d36c65c33b 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -132,6 +132,14 @@ struct renesas_usbhs_driver_param { * option: * * dma id for dmaengine + * The data transfer direction on D0FIFO/D1FIFO should be + * fixed for keeping consistency. + * So, the platform id settings will be.. + * .d0_tx_id = xx_TX, + * .d1_rx_id = xx_RX, + * or + * .d1_tx_id = xx_TX, + * .d0_rx_id = xx_RX, */ int d0_tx_id; int d0_rx_id; -- cgit v1.2.3 From c2e935a7db6e7354e9dd138b7f6f4c53affc09d9 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Wed, 13 Jun 2012 20:34:12 +0800 Subject: USB: move transceiver from ehci_hcd and ohci_hcd to hcd and rename it as phy - to decrease redundant since both ehci_hcd and ohci_hcd have the same variable - it helps access phy in usb core code - phy is more meaningful than transceiver Signed-off-by: Richard Zhao Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-fsl.c | 21 ++++++++++----------- drivers/usb/host/ehci-hub.c | 2 +- drivers/usb/host/ehci.h | 4 ---- drivers/usb/host/ohci-omap.c | 27 ++++++++++++++------------- drivers/usb/host/ohci.h | 5 ----- include/linux/usb/hcd.h | 6 ++++++ 6 files changed, 31 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 43362577b54a..3379945b095e 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -142,19 +142,19 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, if (pdata->operating_mode == FSL_USB2_DR_OTG) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - ehci->transceiver = usb_get_transceiver(); - dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", - hcd, ehci, ehci->transceiver); + hcd->phy = usb_get_transceiver(); + dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, phy=0x%p\n", + hcd, ehci, hcd->phy); - if (ehci->transceiver) { - retval = otg_set_host(ehci->transceiver->otg, + if (hcd->phy) { + retval = otg_set_host(hcd->phy->otg, &ehci_to_hcd(ehci)->self); if (retval) { - usb_put_transceiver(ehci->transceiver); + usb_put_transceiver(hcd->phy); goto err4; } } else { - dev_err(&pdev->dev, "can't find transceiver\n"); + dev_err(&pdev->dev, "can't find phy\n"); retval = -ENODEV; goto err4; } @@ -190,11 +190,10 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - if (ehci->transceiver) { - otg_set_host(ehci->transceiver->otg, NULL); - usb_put_transceiver(ehci->transceiver); + if (hcd->phy) { + otg_set_host(hcd->phy->otg, NULL); + usb_put_transceiver(hcd->phy); } usb_remove_hcd(hcd); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index fc9e7cc6ac9b..dd5eef6af6df 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -724,7 +724,7 @@ static int ehci_hub_control ( #ifdef CONFIG_USB_OTG if ((hcd->self.otg_port == (wIndex + 1)) && hcd->self.b_hnp_enable) { - otg_start_hnp(ehci->transceiver->otg); + otg_start_hnp(hcd->phy->otg); break; } #endif diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 2694ed6558d2..85c3572155d1 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -175,10 +175,6 @@ struct ehci_hcd { /* one per controller */ #ifdef DEBUG struct dentry *debug_dir; #endif - /* - * OTG controllers and transceivers need software interaction - */ - struct usb_phy *transceiver; }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 9ce35d0d9d5d..eccddb461396 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -167,14 +167,15 @@ static int omap_1510_local_bus_init(void) static void start_hnp(struct ohci_hcd *ohci) { - const unsigned port = ohci_to_hcd(ohci)->self.otg_port - 1; + struct usb_hcd *hcd = ohci_to_hcd(ohci); + const unsigned port = hcd->self.otg_port - 1; unsigned long flags; u32 l; - otg_start_hnp(ohci->transceiver->otg); + otg_start_hnp(hcd->phy->otg); local_irq_save(flags); - ohci->transceiver->state = OTG_STATE_A_SUSPEND; + hcd->phy->state = OTG_STATE_A_SUSPEND; writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]); l = omap_readl(OTG_CTRL); l &= ~OTG_A_BUSREQ; @@ -211,18 +212,18 @@ static int ohci_omap_init(struct usb_hcd *hcd) #ifdef CONFIG_USB_OTG if (need_transceiver) { - ohci->transceiver = usb_get_transceiver(); - if (ohci->transceiver) { - int status = otg_set_host(ohci->transceiver->otg, + hcd->phy = usb_get_transceiver(); + if (hcd->phy) { + int status = otg_set_host(hcd->phy->otg, &ohci_to_hcd(ohci)->self); - dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n", - ohci->transceiver->label, status); + dev_dbg(hcd->self.controller, "init %s phy, status %d\n", + hcd->phy->label, status); if (status) { - usb_put_transceiver(ohci->transceiver); + usb_put_transceiver(hcd->phy); return status; } } else { - dev_err(hcd->self.controller, "can't find transceiver\n"); + dev_err(hcd->self.controller, "can't find phy\n"); return -ENODEV; } ohci->start_hnp = start_hnp; @@ -403,9 +404,9 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) struct ohci_hcd *ohci = hcd_to_ohci (hcd); usb_remove_hcd(hcd); - if (ohci->transceiver) { - (void) otg_set_host(ohci->transceiver->otg, 0); - usb_put_transceiver(ohci->transceiver); + if (hcd->phy) { + (void) otg_set_host(hcd->phy->otg, 0); + usb_put_transceiver(hcd->phy); } if (machine_is_omap_osk()) gpio_free(9); diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 1b19aea25a2b..d3299143d9e2 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -372,11 +372,6 @@ struct ohci_hcd { struct ed *ed_controltail; /* last in ctrl list */ struct ed *periodic [NUM_INTS]; /* shadow int_table */ - /* - * OTG controllers and transceivers need software interaction; - * other external transceivers should be software-transparent - */ - struct usb_phy *transceiver; void (*start_hnp)(struct ohci_hcd *ohci); /* diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 7f855d50cdf5..c532cbeabfbc 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -93,6 +93,12 @@ struct usb_hcd { */ const struct hc_driver *driver; /* hw-specific hooks */ + /* + * OTG and some Host controllers need software interaction with phys; + * other external phys should be software-transparent + */ + struct usb_phy *phy; + /* Flags that need to be manipulated atomically because they can * change while the host controller is running. Always use * set_bit() or clear_bit() to change their values. -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 From 81df2d594340dcb6d1a02191976be88a1ca8120c Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 18 May 2012 21:27:43 +0200 Subject: USB: allow match on bInterfaceNumber MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some composite USB devices provide multiple interfaces with different functions, all using "vendor-specific" for class/subclass/protocol. Another OS use interface numbers to match the driver and interface. It seems these devices are designed with that in mind - using static interface numbers for the different functions. This adds support for matching against the bInterfaceNumber, allowing such devices to be supported without having to resort to testing against interface number whitelists and/or blacklists in the probe. Signed-off-by: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 9 +++++++-- drivers/usb/core/message.c | 5 +++-- drivers/usb/core/sysfs.c | 5 +++-- include/linux/mod_devicetable.h | 7 +++++++ include/linux/usb.h | 16 ++++++++++++++++ scripts/mod/file2alias.c | 5 ++++- 6 files changed, 40 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index f536aebc958e..23d7bbd199a5 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -622,14 +622,15 @@ int usb_match_one_id(struct usb_interface *interface, if (!usb_match_device(dev, id)) return 0; - /* The interface class, subclass, and protocol should never be + /* The interface class, subclass, protocol and number should never be * checked for a match if the device class is Vendor Specific, * unless the match record specifies the Vendor ID. */ if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC && !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS | - USB_DEVICE_ID_MATCH_INT_PROTOCOL))) + USB_DEVICE_ID_MATCH_INT_PROTOCOL | + USB_DEVICE_ID_MATCH_INT_NUMBER))) return 0; if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && @@ -644,6 +645,10 @@ int usb_match_one_id(struct usb_interface *interface, (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol)) return 0; + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) && + (id->bInterfaceNumber != intf->desc.bInterfaceNumber)) + return 0; + return 1; } EXPORT_SYMBOL_GPL(usb_match_one_id); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index b548cf1dbc62..ca7fc392fd9e 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1559,7 +1559,7 @@ static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "MODALIAS=usb:" - "v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X", + "v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02Xin%02X", le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct), le16_to_cpu(usb_dev->descriptor.bcdDevice), @@ -1568,7 +1568,8 @@ static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env) usb_dev->descriptor.bDeviceProtocol, alt->desc.bInterfaceClass, alt->desc.bInterfaceSubClass, - alt->desc.bInterfaceProtocol)) + alt->desc.bInterfaceProtocol, + alt->desc.bInterfaceNumber)) return -ENOMEM; return 0; diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 9a56e3adf476..777f03c37725 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -840,7 +840,7 @@ static ssize_t show_modalias(struct device *dev, alt = intf->cur_altsetting; return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X" - "ic%02Xisc%02Xip%02X\n", + "ic%02Xisc%02Xip%02Xin%02X\n", le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct), le16_to_cpu(udev->descriptor.bcdDevice), @@ -849,7 +849,8 @@ static ssize_t show_modalias(struct device *dev, udev->descriptor.bDeviceProtocol, alt->desc.bInterfaceClass, alt->desc.bInterfaceSubClass, - alt->desc.bInterfaceProtocol); + alt->desc.bInterfaceProtocol, + alt->desc.bInterfaceNumber); } static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 5db93821f9c7..7771d453e5f3 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -78,6 +78,9 @@ struct ieee1394_device_id { * of a given interface; other interfaces may support other classes. * @bInterfaceSubClass: Subclass of interface; associated with bInterfaceClass. * @bInterfaceProtocol: Protocol of interface; associated with bInterfaceClass. + * @bInterfaceNumber: Number of interface; composite devices may use + * fixed interface numbers to differentiate between vendor-specific + * interfaces. * @driver_info: Holds information used by the driver. Usually it holds * a pointer to a descriptor understood by the driver, or perhaps * device flags. @@ -115,6 +118,9 @@ struct usb_device_id { __u8 bInterfaceSubClass; __u8 bInterfaceProtocol; + /* Used for vendor-specific interface matches */ + __u8 bInterfaceNumber; + /* not matched against */ kernel_ulong_t driver_info; }; @@ -130,6 +136,7 @@ struct usb_device_id { #define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080 #define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 +#define USB_DEVICE_ID_MATCH_INT_NUMBER 0x0400 #define HID_ANY_ID (~0) #define HID_BUS_ANY 0xffff diff --git a/include/linux/usb.h b/include/linux/usb.h index dea39dc551d4..f717fbdaee8e 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -776,6 +776,22 @@ static inline int usb_make_path(struct usb_device *dev, char *buf, size_t size) .idProduct = (prod), \ .bInterfaceProtocol = (pr) +/** + * USB_DEVICE_INTERFACE_NUMBER - describe a usb device with a specific interface number + * @vend: the 16 bit USB Vendor ID + * @prod: the 16 bit USB Product ID + * @num: bInterfaceNumber value + * + * This macro is used to create a struct usb_device_id that matches a + * specific interface number of devices. + */ +#define USB_DEVICE_INTERFACE_NUMBER(vend, prod, num) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_NUMBER, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bInterfaceNumber = (num) + /** * USB_DEVICE_INFO - macro used to describe a class of usb devices * @cl: bDeviceClass value diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 5759751a1f61..7ed6864ef65b 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -156,7 +156,7 @@ static void device_id_check(const char *modname, const char *device_id, } /* USB is special because the bcdDevice can be matched against a numeric range */ -/* Looks like "usb:vNpNdNdcNdscNdpNicNiscNipN" */ +/* Looks like "usb:vNpNdNdcNdscNdpNicNiscNipNinN" */ static void do_usb_entry(struct usb_device_id *id, unsigned int bcdDevice_initial, int bcdDevice_initial_digits, unsigned char range_lo, unsigned char range_hi, @@ -210,6 +210,9 @@ static void do_usb_entry(struct usb_device_id *id, ADD(alias, "ip", id->match_flags&USB_DEVICE_ID_MATCH_INT_PROTOCOL, id->bInterfaceProtocol); + ADD(alias, "in", + id->match_flags&USB_DEVICE_ID_MATCH_INT_NUMBER, + id->bInterfaceNumber); add_wildcard(alias); buf_printf(&mod->dev_table_buf, -- cgit v1.2.3 From dcce0489477f07ac1331aee71f18d6274e19a9c1 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 11 Jun 2012 18:39:50 +0200 Subject: KVM: trace events: update list of exit reasons The list of exit reasons for the kvm_userspace_exit event was missing recent additions; bring it into sync again. Reviewed-by: Christian Borntraeger Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- include/trace/events/kvm.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h index 46e3cd8e197a..3df5925fe641 100644 --- a/include/trace/events/kvm.h +++ b/include/trace/events/kvm.h @@ -13,7 +13,8 @@ ERSN(DEBUG), ERSN(HLT), ERSN(MMIO), ERSN(IRQ_WINDOW_OPEN), \ ERSN(SHUTDOWN), ERSN(FAIL_ENTRY), ERSN(INTR), ERSN(SET_TPR), \ ERSN(TPR_ACCESS), ERSN(S390_SIEIC), ERSN(S390_RESET), ERSN(DCR),\ - ERSN(NMI), ERSN(INTERNAL_ERROR), ERSN(OSI) + ERSN(NMI), ERSN(INTERNAL_ERROR), ERSN(OSI), ERSN(PAPR_HCALL), \ + ERSN(S390_UCONTROL) TRACE_EVENT(kvm_userspace_exit, TP_PROTO(__u32 reason, int errno), -- cgit v1.2.3 From f29e5956aebafe63f81e80f972c44c4a666e5c7f Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Sat, 26 May 2012 06:20:19 -0700 Subject: pstore: Add console log messages support Pstore doesn't support logging kernel messages in run-time, it only dumps dmesg when kernel oopses/panics. This makes pstore useless for debugging hangs caused by HW issues or improper use of HW (e.g. weird device inserted -> driver tried to write a reserved bits -> SoC hanged. In that case we don't get any messages in the pstore. Therefore, let's add a runtime logging support: PSTORE_TYPE_CONSOLE. Signed-off-by: Anton Vorontsov Acked-by: Kees Cook Acked-by: Colin Cross Signed-off-by: Greg Kroah-Hartman --- fs/pstore/Kconfig | 7 +++++++ fs/pstore/inode.c | 3 +++ fs/pstore/platform.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/pstore.h | 1 + 4 files changed, 48 insertions(+) (limited to 'include') diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig index 23ade2680a4a..d044de6ee308 100644 --- a/fs/pstore/Kconfig +++ b/fs/pstore/Kconfig @@ -12,6 +12,13 @@ config PSTORE If you don't have a platform persistent store driver, say N. +config PSTORE_CONSOLE + bool "Log kernel console messages" + depends on PSTORE + help + When the option is enabled, pstore will log all kernel + messages, even if no oops or panic happened. + config PSTORE_RAM tristate "Log panic/oops to a RAM buffer" depends on PSTORE diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index 11a2aa2a56c4..45bff5441b04 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -212,6 +212,9 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, case PSTORE_TYPE_DMESG: sprintf(name, "dmesg-%s-%lld", psname, id); break; + case PSTORE_TYPE_CONSOLE: + sprintf(name, "console-%s", psname); + break; case PSTORE_TYPE_MCE: sprintf(name, "mce-%s-%lld", psname, id); break; diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 82c585f715e3..61461ed9b6c8 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -1,6 +1,7 @@ /* * Persistent Storage - platform driver interface parts. * + * Copyright (C) 2007-2008 Google, Inc. * Copyright (C) 2010 Intel Corporation * * This program is free software; you can redistribute it and/or modify @@ -22,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -156,6 +158,40 @@ static struct kmsg_dumper pstore_dumper = { .dump = pstore_dump, }; +#ifdef CONFIG_PSTORE_CONSOLE +static void pstore_console_write(struct console *con, const char *s, unsigned c) +{ + const char *e = s + c; + + while (s < e) { + unsigned long flags; + + if (c > psinfo->bufsize) + c = psinfo->bufsize; + spin_lock_irqsave(&psinfo->buf_lock, flags); + memcpy(psinfo->buf, s, c); + psinfo->write(PSTORE_TYPE_CONSOLE, 0, NULL, 0, c, psinfo); + spin_unlock_irqrestore(&psinfo->buf_lock, flags); + s += c; + c = e - s; + } +} + +static struct console pstore_console = { + .name = "pstore", + .write = pstore_console_write, + .flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME, + .index = -1, +}; + +static void pstore_register_console(void) +{ + register_console(&pstore_console); +} +#else +static void pstore_register_console(void) {} +#endif + /* * platform specific persistent storage driver registers with * us here. If pstore is already mounted, call the platform @@ -193,6 +229,7 @@ int pstore_register(struct pstore_info *psi) pstore_get_records(0); kmsg_dump_register(&pstore_dumper); + pstore_register_console(); pstore_timer.expires = jiffies + PSTORE_INTERVAL; add_timer(&pstore_timer); diff --git a/include/linux/pstore.h b/include/linux/pstore.h index e1461e143be2..1bd014b8e432 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -29,6 +29,7 @@ enum pstore_type_id { PSTORE_TYPE_DMESG = 0, PSTORE_TYPE_MCE = 1, + PSTORE_TYPE_CONSOLE = 2, PSTORE_TYPE_UNKNOWN = 255 }; -- cgit v1.2.3 From b5d38e9bf1b0c4db19e336b59b38dfb5d28bf1bf Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Sat, 26 May 2012 06:20:23 -0700 Subject: pstore/ram: Add console messages handling The console log size is configurable via ramoops.console_size module option, and the log itself is available via /console-ramoops file. Signed-off-by: Anton Vorontsov Acked-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- fs/pstore/ram.c | 100 ++++++++++++++++++++++++++++++++++++++------- include/linux/pstore_ram.h | 1 + 2 files changed, 87 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index d770d7266e96..c7acf94ff475 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -41,6 +41,10 @@ module_param(record_size, ulong, 0400); MODULE_PARM_DESC(record_size, "size of each dump done on oops/panic"); +static ulong ramoops_console_size = MIN_MEM_SIZE; +module_param_named(console_size, ramoops_console_size, ulong, 0400); +MODULE_PARM_DESC(console_size, "size of kernel console log"); + static ulong mem_address; module_param(mem_address, ulong, 0400); MODULE_PARM_DESC(mem_address, @@ -63,14 +67,17 @@ MODULE_PARM_DESC(ramoops_ecc, struct ramoops_context { struct persistent_ram_zone **przs; + struct persistent_ram_zone *cprz; phys_addr_t phys_addr; unsigned long size; size_t record_size; + size_t console_size; int dump_oops; bool ecc; unsigned int max_dump_cnt; unsigned int dump_write_cnt; unsigned int dump_read_cnt; + unsigned int console_read_cnt; struct pstore_info pstore; }; @@ -82,6 +89,7 @@ static int ramoops_pstore_open(struct pstore_info *psi) struct ramoops_context *cxt = psi->data; cxt->dump_read_cnt = 0; + cxt->console_read_cnt = 0; return 0; } @@ -124,6 +132,9 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, prz = ramoops_get_next_prz(cxt->przs, &cxt->dump_read_cnt, cxt->max_dump_cnt, id, type, PSTORE_TYPE_DMESG, 1); + if (!prz) + prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt, + 1, id, type, PSTORE_TYPE_CONSOLE, 0); if (!prz) return 0; @@ -167,7 +178,13 @@ static int ramoops_pstore_write(enum pstore_type_id type, struct persistent_ram_zone *prz = cxt->przs[cxt->dump_write_cnt]; size_t hlen; - /* Currently ramoops is designed to only store dmesg dumps. */ + if (type == PSTORE_TYPE_CONSOLE) { + if (!cxt->cprz) + return -ENOMEM; + persistent_ram_write(cxt->cprz, cxt->pstore.buf, size); + return 0; + } + if (type != PSTORE_TYPE_DMESG) return -EINVAL; @@ -204,12 +221,23 @@ static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, struct pstore_info *psi) { struct ramoops_context *cxt = psi->data; + struct persistent_ram_zone *prz; - if (id >= cxt->max_dump_cnt) + switch (type) { + case PSTORE_TYPE_DMESG: + if (id >= cxt->max_dump_cnt) + return -EINVAL; + prz = cxt->przs[id]; + break; + case PSTORE_TYPE_CONSOLE: + prz = cxt->cprz; + break; + default: return -EINVAL; + } - persistent_ram_free_old(cxt->przs[id]); - persistent_ram_zap(cxt->przs[id]); + persistent_ram_free_old(prz); + persistent_ram_zap(prz); return 0; } @@ -276,6 +304,32 @@ fail_prz: return err; } +static int ramoops_init_prz(struct device *dev, struct ramoops_context *cxt, + struct persistent_ram_zone **prz, + phys_addr_t *paddr, size_t sz) +{ + if (!sz) + return 0; + + if (*paddr + sz > *paddr + cxt->size) + return -ENOMEM; + + *prz = persistent_ram_new(*paddr, sz, cxt->ecc); + if (IS_ERR(*prz)) { + int err = PTR_ERR(*prz); + + dev_err(dev, "failed to request mem region (0x%zx@0x%llx): %d\n", + sz, (unsigned long long)*paddr, err); + return err; + } + + persistent_ram_zap(*prz); + + *paddr += sz; + + return 0; +} + static int __init ramoops_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -291,34 +345,50 @@ static int __init ramoops_probe(struct platform_device *pdev) if (cxt->max_dump_cnt) goto fail_out; - if (!pdata->mem_size || !pdata->record_size) { - pr_err("The memory size and the record size must be " + if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size)) { + pr_err("The memory size and the record/console size must be " "non-zero\n"); goto fail_out; } pdata->mem_size = rounddown_pow_of_two(pdata->mem_size); pdata->record_size = rounddown_pow_of_two(pdata->record_size); + pdata->console_size = rounddown_pow_of_two(pdata->console_size); cxt->dump_read_cnt = 0; cxt->size = pdata->mem_size; cxt->phys_addr = pdata->mem_address; cxt->record_size = pdata->record_size; + cxt->console_size = pdata->console_size; cxt->dump_oops = pdata->dump_oops; cxt->ecc = pdata->ecc; paddr = cxt->phys_addr; - dump_mem_sz = cxt->size; + dump_mem_sz = cxt->size - cxt->console_size; err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz); - if (err) { + if (err) + goto fail_out; + + err = ramoops_init_prz(dev, cxt, &cxt->cprz, &paddr, cxt->console_size); + if (err) + goto fail_init_cprz; + + if (!cxt->przs && !cxt->cprz) { pr_err("memory size too small, minimum is %lu\n", - cxt->record_size); - goto fail_count; + cxt->console_size + cxt->record_size); + goto fail_cnt; } cxt->pstore.data = cxt; - cxt->pstore.bufsize = cxt->przs[0]->buffer_size; + /* + * Console can handle any buffer size, so prefer dumps buffer + * size since usually it is smaller. + */ + if (cxt->przs) + cxt->pstore.bufsize = cxt->przs[0]->buffer_size; + else + cxt->pstore.bufsize = cxt->cprz->buffer_size; cxt->pstore.buf = kmalloc(cxt->pstore.bufsize, GFP_KERNEL); spin_lock_init(&cxt->pstore.buf_lock); if (!cxt->pstore.buf) { @@ -341,9 +411,8 @@ static int __init ramoops_probe(struct platform_device *pdev) record_size = pdata->record_size; dump_oops = pdata->dump_oops; - pr_info("attached 0x%lx@0x%llx (%ux0x%zx), ecc: %s\n", + pr_info("attached 0x%lx@0x%llx, ecc: %s\n", cxt->size, (unsigned long long)cxt->phys_addr, - cxt->max_dump_cnt, cxt->record_size, ramoops_ecc ? "on" : "off"); return 0; @@ -353,7 +422,9 @@ fail_buf: fail_clear: cxt->pstore.bufsize = 0; cxt->max_dump_cnt = 0; -fail_count: +fail_cnt: + kfree(cxt->cprz); +fail_init_cprz: ramoops_free_przs(cxt); fail_out: return err; @@ -405,6 +476,7 @@ static int __init ramoops_init(void) dummy_data->mem_size = mem_size; dummy_data->mem_address = mem_address; dummy_data->record_size = record_size; + dummy_data->console_size = ramoops_console_size; dummy_data->dump_oops = dump_oops; dummy_data->ecc = ramoops_ecc; dummy = platform_create_bundle(&ramoops_driver, ramoops_probe, diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h index 3b823d49a85a..9385d41cb1c3 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h @@ -93,6 +93,7 @@ struct ramoops_platform_data { unsigned long mem_size; unsigned long mem_address; unsigned long record_size; + unsigned long console_size; int dump_oops; bool ecc; }; -- cgit v1.2.3 From b8587daa756141da776e3d4c3a5a315f5af78708 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Sat, 26 May 2012 06:20:27 -0700 Subject: pstore/ram_core: Remove now unused code The code tried to maintain the global list of persistent ram zones, which isn't a great idea overall, plus since Android's ram_console is no longer there, we can remove some unused functions. Signed-off-by: Anton Vorontsov Acked-by: Kees Cook Acked-by: Colin Cross Signed-off-by: Greg Kroah-Hartman --- fs/pstore/ram_core.c | 77 ---------------------------------------------- include/linux/pstore_ram.h | 19 ------------ 2 files changed, 96 deletions(-) (limited to 'include') diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c index 78f6d4b2addb..0fd81611525c 100644 --- a/fs/pstore/ram_core.c +++ b/fs/pstore/ram_core.c @@ -35,8 +35,6 @@ struct persistent_ram_buffer { #define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */ -static __initdata LIST_HEAD(persistent_ram_list); - static inline size_t buffer_size(struct persistent_ram_zone *prz) { return atomic_read(&prz->buffer->size); @@ -462,78 +460,3 @@ err: kfree(prz); return ERR_PTR(ret); } - -#ifndef MODULE -static int __init persistent_ram_buffer_init(const char *name, - struct persistent_ram_zone *prz) -{ - int i; - struct persistent_ram *ram; - struct persistent_ram_descriptor *desc; - phys_addr_t start; - - list_for_each_entry(ram, &persistent_ram_list, node) { - start = ram->start; - for (i = 0; i < ram->num_descs; i++) { - desc = &ram->descs[i]; - if (!strcmp(desc->name, name)) - return persistent_ram_buffer_map(start, - desc->size, prz); - start += desc->size; - } - } - - return -EINVAL; -} - -static __init -struct persistent_ram_zone *__persistent_ram_init(struct device *dev, bool ecc) -{ - struct persistent_ram_zone *prz; - int ret = -ENOMEM; - - prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL); - if (!prz) { - pr_err("persistent_ram: failed to allocate persistent ram zone\n"); - goto err; - } - - ret = persistent_ram_buffer_init(dev_name(dev), prz); - if (ret) { - pr_err("persistent_ram: failed to initialize buffer\n"); - goto err; - } - - persistent_ram_post_init(prz, ecc); - - return prz; -err: - kfree(prz); - return ERR_PTR(ret); -} - -struct persistent_ram_zone * __init -persistent_ram_init_ringbuffer(struct device *dev, bool ecc) -{ - return __persistent_ram_init(dev, ecc); -} - -int __init persistent_ram_early_init(struct persistent_ram *ram) -{ - int ret; - - ret = memblock_reserve(ram->start, ram->size); - if (ret) { - pr_err("Failed to reserve persistent memory from %08lx-%08lx\n", - (long)ram->start, (long)(ram->start + ram->size - 1)); - return ret; - } - - list_add_tail(&ram->node, &persistent_ram_list); - - pr_info("Initialized persistent memory from %08lx-%08lx\n", - (long)ram->start, (long)(ram->start + ram->size - 1)); - - return 0; -} -#endif diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h index 9385d41cb1c3..2470bb591434 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h @@ -25,21 +25,6 @@ struct persistent_ram_buffer; -struct persistent_ram_descriptor { - const char *name; - phys_addr_t size; -}; - -struct persistent_ram { - phys_addr_t start; - phys_addr_t size; - - int num_descs; - struct persistent_ram_descriptor *descs; - - struct list_head node; -}; - struct persistent_ram_zone { phys_addr_t paddr; size_t size; @@ -63,15 +48,11 @@ struct persistent_ram_zone { size_t old_log_size; }; -int persistent_ram_early_init(struct persistent_ram *ram); - struct persistent_ram_zone * __init persistent_ram_new(phys_addr_t start, size_t size, bool ecc); void persistent_ram_free(struct persistent_ram_zone *prz); void persistent_ram_zap(struct persistent_ram_zone *prz); -struct persistent_ram_zone *persistent_ram_init_ringbuffer(struct device *dev, - bool ecc); int persistent_ram_write(struct persistent_ram_zone *prz, const void *s, unsigned int count); -- cgit v1.2.3 From b8c24c4aef94b1f0daafb450363fef13a1163780 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 13 Jun 2012 10:24:52 -0500 Subject: slob: Define page struct fields used in mm_types.h Define the fields used by slob in mm_types.h and use struct page instead of struct slob_page in slob. This cleans up numerous of typecasts in slob.c and makes readers aware of slob's use of page struct fields. [Also cleans up some bitrot in slob.c. The page struct field layout in slob.c is an old layout and does not match the one in mm_types.h] Reviewed-by: Glauber Costa Acked-by: David Rientjes Reviewed-by: Joonsoo Kim Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/mm_types.h | 7 +++- mm/slob.c | 95 +++++++++++++++++++----------------------------- 2 files changed, 42 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index dad95bdd06d7..5922c3452592 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -53,7 +53,7 @@ struct page { struct { union { pgoff_t index; /* Our offset within mapping. */ - void *freelist; /* slub first free object */ + void *freelist; /* slub/slob first free object */ }; union { @@ -81,11 +81,12 @@ struct page { */ atomic_t _mapcount; - struct { + struct { /* SLUB */ unsigned inuse:16; unsigned objects:15; unsigned frozen:1; }; + int units; /* SLOB */ }; atomic_t _count; /* Usage count, see below. */ }; @@ -107,6 +108,8 @@ struct page { short int pobjects; #endif }; + + struct list_head list; /* slobs list of pages */ }; /* Remainder is not double word aligned */ diff --git a/mm/slob.c b/mm/slob.c index 8105be42cad1..30862a2d56a9 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -91,34 +91,13 @@ struct slob_block { }; typedef struct slob_block slob_t; -/* - * We use struct page fields to manage some slob allocation aspects, - * however to avoid the horrible mess in include/linux/mm_types.h, we'll - * just define our own struct page type variant here. - */ -struct slob_page { - union { - struct { - unsigned long flags; /* mandatory */ - atomic_t _count; /* mandatory */ - slobidx_t units; /* free units left in page */ - unsigned long pad[2]; - slob_t *free; /* first free slob_t in page */ - struct list_head list; /* linked list of free pages */ - }; - struct page page; - }; -}; -static inline void struct_slob_page_wrong_size(void) -{ BUILD_BUG_ON(sizeof(struct slob_page) != sizeof(struct page)); } - /* * free_slob_page: call before a slob_page is returned to the page allocator. */ -static inline void free_slob_page(struct slob_page *sp) +static inline void free_slob_page(struct page *sp) { - reset_page_mapcount(&sp->page); - sp->page.mapping = NULL; + reset_page_mapcount(sp); + sp->mapping = NULL; } /* @@ -133,44 +112,44 @@ static LIST_HEAD(free_slob_large); /* * is_slob_page: True for all slob pages (false for bigblock pages) */ -static inline int is_slob_page(struct slob_page *sp) +static inline int is_slob_page(struct page *sp) { - return PageSlab((struct page *)sp); + return PageSlab(sp); } -static inline void set_slob_page(struct slob_page *sp) +static inline void set_slob_page(struct page *sp) { - __SetPageSlab((struct page *)sp); + __SetPageSlab(sp); } -static inline void clear_slob_page(struct slob_page *sp) +static inline void clear_slob_page(struct page *sp) { - __ClearPageSlab((struct page *)sp); + __ClearPageSlab(sp); } -static inline struct slob_page *slob_page(const void *addr) +static inline struct page *slob_page(const void *addr) { - return (struct slob_page *)virt_to_page(addr); + return virt_to_page(addr); } /* * slob_page_free: true for pages on free_slob_pages list. */ -static inline int slob_page_free(struct slob_page *sp) +static inline int slob_page_free(struct page *sp) { - return PageSlobFree((struct page *)sp); + return PageSlobFree(sp); } -static void set_slob_page_free(struct slob_page *sp, struct list_head *list) +static void set_slob_page_free(struct page *sp, struct list_head *list) { list_add(&sp->list, list); - __SetPageSlobFree((struct page *)sp); + __SetPageSlobFree(sp); } -static inline void clear_slob_page_free(struct slob_page *sp) +static inline void clear_slob_page_free(struct page *sp) { list_del(&sp->list); - __ClearPageSlobFree((struct page *)sp); + __ClearPageSlobFree(sp); } #define SLOB_UNIT sizeof(slob_t) @@ -267,12 +246,12 @@ static void slob_free_pages(void *b, int order) /* * Allocate a slob block within a given slob_page sp. */ -static void *slob_page_alloc(struct slob_page *sp, size_t size, int align) +static void *slob_page_alloc(struct page *sp, size_t size, int align) { slob_t *prev, *cur, *aligned = NULL; int delta = 0, units = SLOB_UNITS(size); - for (prev = NULL, cur = sp->free; ; prev = cur, cur = slob_next(cur)) { + for (prev = NULL, cur = sp->freelist; ; prev = cur, cur = slob_next(cur)) { slobidx_t avail = slob_units(cur); if (align) { @@ -296,12 +275,12 @@ static void *slob_page_alloc(struct slob_page *sp, size_t size, int align) if (prev) set_slob(prev, slob_units(prev), next); else - sp->free = next; + sp->freelist = next; } else { /* fragment */ if (prev) set_slob(prev, slob_units(prev), cur + units); else - sp->free = cur + units; + sp->freelist = cur + units; set_slob(cur + units, avail - units, next); } @@ -320,7 +299,7 @@ static void *slob_page_alloc(struct slob_page *sp, size_t size, int align) */ static void *slob_alloc(size_t size, gfp_t gfp, int align, int node) { - struct slob_page *sp; + struct page *sp; struct list_head *prev; struct list_head *slob_list; slob_t *b = NULL; @@ -341,7 +320,7 @@ static void *slob_alloc(size_t size, gfp_t gfp, int align, int node) * If there's a node specification, search for a partial * page with a matching node id in the freelist. */ - if (node != -1 && page_to_nid(&sp->page) != node) + if (node != -1 && page_to_nid(sp) != node) continue; #endif /* Enough room on this page? */ @@ -374,7 +353,7 @@ static void *slob_alloc(size_t size, gfp_t gfp, int align, int node) spin_lock_irqsave(&slob_lock, flags); sp->units = SLOB_UNITS(PAGE_SIZE); - sp->free = b; + sp->freelist = b; INIT_LIST_HEAD(&sp->list); set_slob(b, SLOB_UNITS(PAGE_SIZE), b + SLOB_UNITS(PAGE_SIZE)); set_slob_page_free(sp, slob_list); @@ -392,7 +371,7 @@ static void *slob_alloc(size_t size, gfp_t gfp, int align, int node) */ static void slob_free(void *block, int size) { - struct slob_page *sp; + struct page *sp; slob_t *prev, *next, *b = (slob_t *)block; slobidx_t units; unsigned long flags; @@ -421,7 +400,7 @@ static void slob_free(void *block, int size) if (!slob_page_free(sp)) { /* This slob page is about to become partially free. Easy! */ sp->units = units; - sp->free = b; + sp->freelist = b; set_slob(b, units, (void *)((unsigned long)(b + SLOB_UNITS(PAGE_SIZE)) & PAGE_MASK)); @@ -441,15 +420,15 @@ static void slob_free(void *block, int size) */ sp->units += units; - if (b < sp->free) { - if (b + units == sp->free) { - units += slob_units(sp->free); - sp->free = slob_next(sp->free); + if (b < (slob_t *)sp->freelist) { + if (b + units == sp->freelist) { + units += slob_units(sp->freelist); + sp->freelist = slob_next(sp->freelist); } - set_slob(b, units, sp->free); - sp->free = b; + set_slob(b, units, sp->freelist); + sp->freelist = b; } else { - prev = sp->free; + prev = sp->freelist; next = slob_next(prev); while (b > next) { prev = next; @@ -522,7 +501,7 @@ EXPORT_SYMBOL(__kmalloc_node); void kfree(const void *block) { - struct slob_page *sp; + struct page *sp; trace_kfree(_RET_IP_, block); @@ -536,14 +515,14 @@ void kfree(const void *block) unsigned int *m = (unsigned int *)(block - align); slob_free(m, *m + align); } else - put_page(&sp->page); + put_page(sp); } EXPORT_SYMBOL(kfree); /* can't use ksize for kmem_cache_alloc memory, only kmalloc */ size_t ksize(const void *block) { - struct slob_page *sp; + struct page *sp; BUG_ON(!block); if (unlikely(block == ZERO_SIZE_PTR)) @@ -555,7 +534,7 @@ size_t ksize(const void *block) unsigned int *m = (unsigned int *)(block - align); return SLOB_UNITS(*m) * SLOB_UNIT; } else - return sp->page.private; + return sp->private; } EXPORT_SYMBOL(ksize); -- cgit v1.2.3 From e571b0ad3495be5793e54e21cd244c4545c49d88 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 13 Jun 2012 10:24:55 -0500 Subject: slab: Use page struct fields instead of casting Add fields to the page struct so that it is properly documented that slab overlays the lru fields. This cleans up some casts in slab. Reviewed-by: Glauber Costa Reviewed-by: Joonsoo Kim Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/mm_types.h | 4 ++++ mm/slab.c | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 5922c3452592..680a5e4e8cd5 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -110,6 +110,10 @@ struct page { }; struct list_head list; /* slobs list of pages */ + struct { /* slab fields */ + struct kmem_cache *slab_cache; + struct slab *slab_page; + }; }; /* Remainder is not double word aligned */ diff --git a/mm/slab.c b/mm/slab.c index e901a36e2520..af05147d7abd 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -496,25 +496,25 @@ static bool slab_max_order_set __initdata; */ static inline void page_set_cache(struct page *page, struct kmem_cache *cache) { - page->lru.next = (struct list_head *)cache; + page->slab_cache = cache; } static inline struct kmem_cache *page_get_cache(struct page *page) { page = compound_head(page); BUG_ON(!PageSlab(page)); - return (struct kmem_cache *)page->lru.next; + return page->slab_cache; } static inline void page_set_slab(struct page *page, struct slab *slab) { - page->lru.prev = (struct list_head *)slab; + page->slab_page = slab; } static inline struct slab *page_get_slab(struct page *page) { BUG_ON(!PageSlab(page)); - return (struct slab *)page->lru.prev; + return page->slab_page; } static inline struct kmem_cache *virt_to_cache(const void *obj) -- cgit v1.2.3 From 3b0efdfa1e719303536c04d9abca43abeb40f80a Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Wed, 13 Jun 2012 10:24:57 -0500 Subject: mm, sl[aou]b: Extract common fields from struct kmem_cache Define a struct that describes common fields used in all slab allocators. A slab allocator either uses the common definition (like SLOB) or is required to provide members of kmem_cache with the definition given. After that it will be possible to share code that only operates on those fields of kmem_cache. The patch basically takes the slob definition of kmem cache and uses the field namees for the other allocators. It also standardizes the names used for basic object lengths in allocators: object_size Struct size specified at kmem_cache_create. Basically the payload expected to be used by the subsystem. size The size of memory allocator for each object. This size is larger than object_size and includes padding, alignment and extra metadata for each object (f.e. for debugging and rcu). Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab.h | 24 ++++++++++ include/linux/slab_def.h | 10 ++-- include/linux/slub_def.h | 2 +- mm/slab.c | 117 +++++++++++++++++++++++------------------------ mm/slob.c | 9 +--- mm/slub.c | 80 ++++++++++++++++---------------- 6 files changed, 130 insertions(+), 112 deletions(-) (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index 67d5d94b783a..0dd2dfa7beca 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -92,6 +92,30 @@ #define ZERO_OR_NULL_PTR(x) ((unsigned long)(x) <= \ (unsigned long)ZERO_SIZE_PTR) +/* + * Common fields provided in kmem_cache by all slab allocators + * This struct is either used directly by the allocator (SLOB) + * or the allocator must include definitions for all fields + * provided in kmem_cache_common in their definition of kmem_cache. + * + * Once we can do anonymous structs (C11 standard) we could put a + * anonymous struct definition in these allocators so that the + * separate allocations in the kmem_cache structure of SLAB and + * SLUB is no longer needed. + */ +#ifdef CONFIG_SLOB +struct kmem_cache { + unsigned int object_size;/* The original size of the object */ + unsigned int size; /* The aligned/padded/added on size */ + unsigned int align; /* Alignment as calculated */ + unsigned long flags; /* Active flags on the slab */ + const char *name; /* Slab name for sysfs */ + int refcount; /* Use counter */ + void (*ctor)(void *); /* Called on object slot creation */ + struct list_head list; /* List of all slab caches on the system */ +}; +#endif + /* * struct kmem_cache related prototypes */ diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h index fbd1117fdfde..1d93f27d81de 100644 --- a/include/linux/slab_def.h +++ b/include/linux/slab_def.h @@ -27,7 +27,7 @@ struct kmem_cache { unsigned int limit; unsigned int shared; - unsigned int buffer_size; + unsigned int size; u32 reciprocal_buffer_size; /* 2) touched by every alloc & free from the backend */ @@ -52,7 +52,10 @@ struct kmem_cache { /* 4) cache creation/removal */ const char *name; - struct list_head next; + struct list_head list; + int refcount; + int object_size; + int align; /* 5) statistics */ #ifdef CONFIG_DEBUG_SLAB @@ -73,12 +76,11 @@ struct kmem_cache { /* * If debugging is enabled, then the allocator can add additional - * fields and/or padding to every object. buffer_size contains the total + * fields and/or padding to every object. size contains the total * object size including these internal fields, the following two * variables contain the offset to the user object and its size. */ int obj_offset; - int obj_size; #endif /* CONFIG_DEBUG_SLAB */ /* 6) per-cpu/per-node data, touched during every alloc/free */ diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index ebdcf4ba42ee..df448adb7283 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -82,7 +82,7 @@ struct kmem_cache { unsigned long flags; unsigned long min_partial; int size; /* The size of an object including meta data */ - int objsize; /* The size of an object without meta data */ + int object_size; /* The size of an object without meta data */ int offset; /* Free pointer offset. */ int cpu_partial; /* Number of per cpu partial objects to keep around */ struct kmem_cache_order_objects oo; diff --git a/mm/slab.c b/mm/slab.c index 28a8f7d29d4a..e2b3907b7b0c 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -424,8 +424,8 @@ static void kmem_list3_init(struct kmem_list3 *parent) * cachep->obj_offset - BYTES_PER_WORD .. cachep->obj_offset - 1: * redzone word. * cachep->obj_offset: The real object. - * cachep->buffer_size - 2* BYTES_PER_WORD: redzone word [BYTES_PER_WORD long] - * cachep->buffer_size - 1* BYTES_PER_WORD: last caller address + * cachep->size - 2* BYTES_PER_WORD: redzone word [BYTES_PER_WORD long] + * cachep->size - 1* BYTES_PER_WORD: last caller address * [BYTES_PER_WORD long] */ static int obj_offset(struct kmem_cache *cachep) @@ -435,7 +435,7 @@ static int obj_offset(struct kmem_cache *cachep) static int obj_size(struct kmem_cache *cachep) { - return cachep->obj_size; + return cachep->object_size; } static unsigned long long *dbg_redzone1(struct kmem_cache *cachep, void *objp) @@ -449,23 +449,23 @@ static unsigned long long *dbg_redzone2(struct kmem_cache *cachep, void *objp) { BUG_ON(!(cachep->flags & SLAB_RED_ZONE)); if (cachep->flags & SLAB_STORE_USER) - return (unsigned long long *)(objp + cachep->buffer_size - + return (unsigned long long *)(objp + cachep->size - sizeof(unsigned long long) - REDZONE_ALIGN); - return (unsigned long long *) (objp + cachep->buffer_size - + return (unsigned long long *) (objp + cachep->size - sizeof(unsigned long long)); } static void **dbg_userword(struct kmem_cache *cachep, void *objp) { BUG_ON(!(cachep->flags & SLAB_STORE_USER)); - return (void **)(objp + cachep->buffer_size - BYTES_PER_WORD); + return (void **)(objp + cachep->size - BYTES_PER_WORD); } #else #define obj_offset(x) 0 -#define obj_size(cachep) (cachep->buffer_size) +#define obj_size(cachep) (cachep->size) #define dbg_redzone1(cachep, objp) ({BUG(); (unsigned long long *)NULL;}) #define dbg_redzone2(cachep, objp) ({BUG(); (unsigned long long *)NULL;}) #define dbg_userword(cachep, objp) ({BUG(); (void **)NULL;}) @@ -475,7 +475,7 @@ static void **dbg_userword(struct kmem_cache *cachep, void *objp) #ifdef CONFIG_TRACING size_t slab_buffer_size(struct kmem_cache *cachep) { - return cachep->buffer_size; + return cachep->size; } EXPORT_SYMBOL(slab_buffer_size); #endif @@ -513,13 +513,13 @@ static inline struct slab *virt_to_slab(const void *obj) static inline void *index_to_obj(struct kmem_cache *cache, struct slab *slab, unsigned int idx) { - return slab->s_mem + cache->buffer_size * idx; + return slab->s_mem + cache->size * idx; } /* - * We want to avoid an expensive divide : (offset / cache->buffer_size) - * Using the fact that buffer_size is a constant for a particular cache, - * we can replace (offset / cache->buffer_size) by + * We want to avoid an expensive divide : (offset / cache->size) + * Using the fact that size is a constant for a particular cache, + * we can replace (offset / cache->size) by * reciprocal_divide(offset, cache->reciprocal_buffer_size) */ static inline unsigned int obj_to_index(const struct kmem_cache *cache, @@ -565,7 +565,7 @@ static struct kmem_cache cache_cache = { .batchcount = 1, .limit = BOOT_CPUCACHE_ENTRIES, .shared = 1, - .buffer_size = sizeof(struct kmem_cache), + .size = sizeof(struct kmem_cache), .name = "kmem_cache", }; @@ -1134,7 +1134,7 @@ static int init_cache_nodelists_node(int node) struct kmem_list3 *l3; const int memsize = sizeof(struct kmem_list3); - list_for_each_entry(cachep, &cache_chain, next) { + list_for_each_entry(cachep, &cache_chain, list) { /* * Set up the size64 kmemlist for cpu before we can * begin anything. Make sure some other cpu on this @@ -1172,7 +1172,7 @@ static void __cpuinit cpuup_canceled(long cpu) int node = cpu_to_mem(cpu); const struct cpumask *mask = cpumask_of_node(node); - list_for_each_entry(cachep, &cache_chain, next) { + list_for_each_entry(cachep, &cache_chain, list) { struct array_cache *nc; struct array_cache *shared; struct array_cache **alien; @@ -1222,7 +1222,7 @@ free_array_cache: * the respective cache's slabs, now we can go ahead and * shrink each nodelist to its limit. */ - list_for_each_entry(cachep, &cache_chain, next) { + list_for_each_entry(cachep, &cache_chain, list) { l3 = cachep->nodelists[node]; if (!l3) continue; @@ -1251,7 +1251,7 @@ static int __cpuinit cpuup_prepare(long cpu) * Now we can go ahead with allocating the shared arrays and * array caches */ - list_for_each_entry(cachep, &cache_chain, next) { + list_for_each_entry(cachep, &cache_chain, list) { struct array_cache *nc; struct array_cache *shared = NULL; struct array_cache **alien = NULL; @@ -1383,7 +1383,7 @@ static int __meminit drain_cache_nodelists_node(int node) struct kmem_cache *cachep; int ret = 0; - list_for_each_entry(cachep, &cache_chain, next) { + list_for_each_entry(cachep, &cache_chain, list) { struct kmem_list3 *l3; l3 = cachep->nodelists[node]; @@ -1526,7 +1526,7 @@ void __init kmem_cache_init(void) /* 1) create the cache_cache */ INIT_LIST_HEAD(&cache_chain); - list_add(&cache_cache.next, &cache_chain); + list_add(&cache_cache.list, &cache_chain); cache_cache.colour_off = cache_line_size(); cache_cache.array[smp_processor_id()] = &initarray_cache.cache; cache_cache.nodelists[node] = &initkmem_list3[CACHE_CACHE + node]; @@ -1534,18 +1534,16 @@ void __init kmem_cache_init(void) /* * struct kmem_cache size depends on nr_node_ids & nr_cpu_ids */ - cache_cache.buffer_size = offsetof(struct kmem_cache, array[nr_cpu_ids]) + + cache_cache.size = offsetof(struct kmem_cache, array[nr_cpu_ids]) + nr_node_ids * sizeof(struct kmem_list3 *); -#if DEBUG - cache_cache.obj_size = cache_cache.buffer_size; -#endif - cache_cache.buffer_size = ALIGN(cache_cache.buffer_size, + cache_cache.object_size = cache_cache.size; + cache_cache.size = ALIGN(cache_cache.size, cache_line_size()); cache_cache.reciprocal_buffer_size = - reciprocal_value(cache_cache.buffer_size); + reciprocal_value(cache_cache.size); for (order = 0; order < MAX_ORDER; order++) { - cache_estimate(order, cache_cache.buffer_size, + cache_estimate(order, cache_cache.size, cache_line_size(), 0, &left_over, &cache_cache.num); if (cache_cache.num) break; @@ -1671,7 +1669,7 @@ void __init kmem_cache_init_late(void) /* 6) resize the head arrays to their final sizes */ mutex_lock(&cache_chain_mutex); - list_for_each_entry(cachep, &cache_chain, next) + list_for_each_entry(cachep, &cache_chain, list) if (enable_cpucache(cachep, GFP_NOWAIT)) BUG(); mutex_unlock(&cache_chain_mutex); @@ -1724,7 +1722,7 @@ slab_out_of_memory(struct kmem_cache *cachep, gfp_t gfpflags, int nodeid) "SLAB: Unable to allocate memory on node %d (gfp=0x%x)\n", nodeid, gfpflags); printk(KERN_WARNING " cache: %s, object size: %d, order: %d\n", - cachep->name, cachep->buffer_size, cachep->gfporder); + cachep->name, cachep->size, cachep->gfporder); for_each_online_node(node) { unsigned long active_objs = 0, num_objs = 0, free_objects = 0; @@ -2028,10 +2026,10 @@ static void slab_destroy_debugcheck(struct kmem_cache *cachep, struct slab *slab if (cachep->flags & SLAB_POISON) { #ifdef CONFIG_DEBUG_PAGEALLOC - if (cachep->buffer_size % PAGE_SIZE == 0 && + if (cachep->size % PAGE_SIZE == 0 && OFF_SLAB(cachep)) kernel_map_pages(virt_to_page(objp), - cachep->buffer_size / PAGE_SIZE, 1); + cachep->size / PAGE_SIZE, 1); else check_poison_obj(cachep, objp); #else @@ -2281,7 +2279,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, mutex_lock(&cache_chain_mutex); } - list_for_each_entry(pc, &cache_chain, next) { + list_for_each_entry(pc, &cache_chain, list) { char tmp; int res; @@ -2294,7 +2292,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, if (res) { printk(KERN_ERR "SLAB: cache with size %d has lost its name\n", - pc->buffer_size); + pc->size); continue; } @@ -2399,8 +2397,9 @@ kmem_cache_create (const char *name, size_t size, size_t align, goto oops; cachep->nodelists = (struct kmem_list3 **)&cachep->array[nr_cpu_ids]; + cachep->object_size = size; + cachep->align = align; #if DEBUG - cachep->obj_size = size; /* * Both debugging options require word-alignment which is calculated @@ -2423,7 +2422,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, } #if FORCED_DEBUG && defined(CONFIG_DEBUG_PAGEALLOC) if (size >= malloc_sizes[INDEX_L3 + 1].cs_size - && cachep->obj_size > cache_line_size() && ALIGN(size, align) < PAGE_SIZE) { + && cachep->object_size > cache_line_size() && ALIGN(size, align) < PAGE_SIZE) { cachep->obj_offset += PAGE_SIZE - ALIGN(size, align); size = PAGE_SIZE; } @@ -2492,7 +2491,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, cachep->gfpflags = 0; if (CONFIG_ZONE_DMA_FLAG && (flags & SLAB_CACHE_DMA)) cachep->gfpflags |= GFP_DMA; - cachep->buffer_size = size; + cachep->size = size; cachep->reciprocal_buffer_size = reciprocal_value(size); if (flags & CFLGS_OFF_SLAB) { @@ -2526,7 +2525,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, } /* cache setup completed, link it into the list */ - list_add(&cachep->next, &cache_chain); + list_add(&cachep->list, &cache_chain); oops: if (!cachep && (flags & SLAB_PANIC)) panic("kmem_cache_create(): failed to create slab `%s'\n", @@ -2721,10 +2720,10 @@ void kmem_cache_destroy(struct kmem_cache *cachep) /* * the chain is never empty, cache_cache is never destroyed */ - list_del(&cachep->next); + list_del(&cachep->list); if (__cache_shrink(cachep)) { slab_error(cachep, "Can't free all objects"); - list_add(&cachep->next, &cache_chain); + list_add(&cachep->list, &cache_chain); mutex_unlock(&cache_chain_mutex); put_online_cpus(); return; @@ -2821,10 +2820,10 @@ static void cache_init_objs(struct kmem_cache *cachep, slab_error(cachep, "constructor overwrote the" " start of an object"); } - if ((cachep->buffer_size % PAGE_SIZE) == 0 && + if ((cachep->size % PAGE_SIZE) == 0 && OFF_SLAB(cachep) && cachep->flags & SLAB_POISON) kernel_map_pages(virt_to_page(objp), - cachep->buffer_size / PAGE_SIZE, 0); + cachep->size / PAGE_SIZE, 0); #else if (cachep->ctor) cachep->ctor(objp); @@ -3058,10 +3057,10 @@ static void *cache_free_debugcheck(struct kmem_cache *cachep, void *objp, #endif if (cachep->flags & SLAB_POISON) { #ifdef CONFIG_DEBUG_PAGEALLOC - if ((cachep->buffer_size % PAGE_SIZE)==0 && OFF_SLAB(cachep)) { + if ((cachep->size % PAGE_SIZE)==0 && OFF_SLAB(cachep)) { store_stackinfo(cachep, objp, (unsigned long)caller); kernel_map_pages(virt_to_page(objp), - cachep->buffer_size / PAGE_SIZE, 0); + cachep->size / PAGE_SIZE, 0); } else { poison_obj(cachep, objp, POISON_FREE); } @@ -3211,9 +3210,9 @@ static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep, return objp; if (cachep->flags & SLAB_POISON) { #ifdef CONFIG_DEBUG_PAGEALLOC - if ((cachep->buffer_size % PAGE_SIZE) == 0 && OFF_SLAB(cachep)) + if ((cachep->size % PAGE_SIZE) == 0 && OFF_SLAB(cachep)) kernel_map_pages(virt_to_page(objp), - cachep->buffer_size / PAGE_SIZE, 1); + cachep->size / PAGE_SIZE, 1); else check_poison_obj(cachep, objp); #else @@ -3243,7 +3242,7 @@ static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep, unsigned objnr; slabp = virt_to_head_page(objp)->slab_page; - objnr = (unsigned)(objp - slabp->s_mem) / cachep->buffer_size; + objnr = (unsigned)(objp - slabp->s_mem) / cachep->size; slab_bufctl(slabp)[objnr] = BUFCTL_ACTIVE; } #endif @@ -3747,7 +3746,7 @@ void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags) void *ret = __cache_alloc(cachep, flags, __builtin_return_address(0)); trace_kmem_cache_alloc(_RET_IP_, ret, - obj_size(cachep), cachep->buffer_size, flags); + obj_size(cachep), cachep->size, flags); return ret; } @@ -3775,7 +3774,7 @@ void *kmem_cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid) __builtin_return_address(0)); trace_kmem_cache_alloc_node(_RET_IP_, ret, - obj_size(cachep), cachep->buffer_size, + obj_size(cachep), cachep->size, flags, nodeid); return ret; @@ -3857,7 +3856,7 @@ static __always_inline void *__do_kmalloc(size_t size, gfp_t flags, ret = __cache_alloc(cachep, flags, caller); trace_kmalloc((unsigned long) caller, ret, - size, cachep->buffer_size, flags); + size, cachep->size, flags); return ret; } @@ -4011,7 +4010,7 @@ static int alloc_kmemlist(struct kmem_cache *cachep, gfp_t gfp) return 0; fail: - if (!cachep->next.next) { + if (!cachep->list.next) { /* Cache is not active yet. Roll back what we did */ node--; while (node >= 0) { @@ -4105,13 +4104,13 @@ static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp) * The numbers are guessed, we should auto-tune as described by * Bonwick. */ - if (cachep->buffer_size > 131072) + if (cachep->size > 131072) limit = 1; - else if (cachep->buffer_size > PAGE_SIZE) + else if (cachep->size > PAGE_SIZE) limit = 8; - else if (cachep->buffer_size > 1024) + else if (cachep->size > 1024) limit = 24; - else if (cachep->buffer_size > 256) + else if (cachep->size > 256) limit = 54; else limit = 120; @@ -4126,7 +4125,7 @@ static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp) * to a larger limit. Thus disabled by default. */ shared = 0; - if (cachep->buffer_size <= PAGE_SIZE && num_possible_cpus() > 1) + if (cachep->size <= PAGE_SIZE && num_possible_cpus() > 1) shared = 8; #if DEBUG @@ -4196,7 +4195,7 @@ static void cache_reap(struct work_struct *w) /* Give up. Setup the next iteration. */ goto out; - list_for_each_entry(searchp, &cache_chain, next) { + list_for_each_entry(searchp, &cache_chain, list) { check_irq_on(); /* @@ -4289,7 +4288,7 @@ static void s_stop(struct seq_file *m, void *p) static int s_show(struct seq_file *m, void *p) { - struct kmem_cache *cachep = list_entry(p, struct kmem_cache, next); + struct kmem_cache *cachep = list_entry(p, struct kmem_cache, list); struct slab *slabp; unsigned long active_objs; unsigned long num_objs; @@ -4345,7 +4344,7 @@ static int s_show(struct seq_file *m, void *p) printk(KERN_ERR "slab: cache %s error: %s\n", name, error); seq_printf(m, "%-17s %6lu %6lu %6u %4u %4d", - name, active_objs, num_objs, cachep->buffer_size, + name, active_objs, num_objs, cachep->size, cachep->num, (1 << cachep->gfporder)); seq_printf(m, " : tunables %4u %4u %4u", cachep->limit, cachep->batchcount, cachep->shared); @@ -4437,7 +4436,7 @@ static ssize_t slabinfo_write(struct file *file, const char __user *buffer, /* Find the cache in the chain of caches. */ mutex_lock(&cache_chain_mutex); res = -EINVAL; - list_for_each_entry(cachep, &cache_chain, next) { + list_for_each_entry(cachep, &cache_chain, list) { if (!strcmp(cachep->name, kbuf)) { if (limit < 1 || batchcount < 1 || batchcount > limit || shared < 0) { @@ -4513,7 +4512,7 @@ static void handle_slab(unsigned long *n, struct kmem_cache *c, struct slab *s) int i; if (n[0] == n[1]) return; - for (i = 0, p = s->s_mem; i < c->num; i++, p += c->buffer_size) { + for (i = 0, p = s->s_mem; i < c->num; i++, p += c->size) { if (slab_bufctl(s)[i] != BUFCTL_ACTIVE) continue; if (!add_caller(n, (unsigned long)*dbg_userword(c, p))) diff --git a/mm/slob.c b/mm/slob.c index c85265d22e08..95d1c7dd88e0 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -506,13 +506,6 @@ size_t ksize(const void *block) } EXPORT_SYMBOL(ksize); -struct kmem_cache { - unsigned int size, align; - unsigned long flags; - const char *name; - void (*ctor)(void *); -}; - struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *)) { @@ -523,7 +516,7 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t size, if (c) { c->name = name; - c->size = size; + c->size = c->object_size; if (flags & SLAB_DESTROY_BY_RCU) { /* leave room for rcu footer at the end of object */ c->size += sizeof(struct slob_rcu); diff --git a/mm/slub.c b/mm/slub.c index 2de3c996f327..797271f5afb8 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -311,7 +311,7 @@ static inline size_t slab_ksize(const struct kmem_cache *s) * and whatever may come after it. */ if (s->flags & (SLAB_RED_ZONE | SLAB_POISON)) - return s->objsize; + return s->object_size; #endif /* @@ -609,11 +609,11 @@ static void print_trailer(struct kmem_cache *s, struct page *page, u8 *p) if (p > addr + 16) print_section("Bytes b4 ", p - 16, 16); - print_section("Object ", p, min_t(unsigned long, s->objsize, + print_section("Object ", p, min_t(unsigned long, s->object_size, PAGE_SIZE)); if (s->flags & SLAB_RED_ZONE) - print_section("Redzone ", p + s->objsize, - s->inuse - s->objsize); + print_section("Redzone ", p + s->object_size, + s->inuse - s->object_size); if (s->offset) off = s->offset + sizeof(void *); @@ -655,12 +655,12 @@ static void init_object(struct kmem_cache *s, void *object, u8 val) u8 *p = object; if (s->flags & __OBJECT_POISON) { - memset(p, POISON_FREE, s->objsize - 1); - p[s->objsize - 1] = POISON_END; + memset(p, POISON_FREE, s->object_size - 1); + p[s->object_size - 1] = POISON_END; } if (s->flags & SLAB_RED_ZONE) - memset(p + s->objsize, val, s->inuse - s->objsize); + memset(p + s->object_size, val, s->inuse - s->object_size); } static void restore_bytes(struct kmem_cache *s, char *message, u8 data, @@ -705,10 +705,10 @@ static int check_bytes_and_report(struct kmem_cache *s, struct page *page, * Poisoning uses 0x6b (POISON_FREE) and the last byte is * 0xa5 (POISON_END) * - * object + s->objsize + * object + s->object_size * Padding to reach word boundary. This is also used for Redzoning. * Padding is extended by another word if Redzoning is enabled and - * objsize == inuse. + * object_size == inuse. * * We fill with 0xbb (RED_INACTIVE) for inactive objects and with * 0xcc (RED_ACTIVE) for objects in use. @@ -727,7 +727,7 @@ static int check_bytes_and_report(struct kmem_cache *s, struct page *page, * object + s->size * Nothing is used beyond s->size. * - * If slabcaches are merged then the objsize and inuse boundaries are mostly + * If slabcaches are merged then the object_size and inuse boundaries are mostly * ignored. And therefore no slab options that rely on these boundaries * may be used with merged slabcaches. */ @@ -787,25 +787,25 @@ static int check_object(struct kmem_cache *s, struct page *page, void *object, u8 val) { u8 *p = object; - u8 *endobject = object + s->objsize; + u8 *endobject = object + s->object_size; if (s->flags & SLAB_RED_ZONE) { if (!check_bytes_and_report(s, page, object, "Redzone", - endobject, val, s->inuse - s->objsize)) + endobject, val, s->inuse - s->object_size)) return 0; } else { - if ((s->flags & SLAB_POISON) && s->objsize < s->inuse) { + if ((s->flags & SLAB_POISON) && s->object_size < s->inuse) { check_bytes_and_report(s, page, p, "Alignment padding", - endobject, POISON_INUSE, s->inuse - s->objsize); + endobject, POISON_INUSE, s->inuse - s->object_size); } } if (s->flags & SLAB_POISON) { if (val != SLUB_RED_ACTIVE && (s->flags & __OBJECT_POISON) && (!check_bytes_and_report(s, page, p, "Poison", p, - POISON_FREE, s->objsize - 1) || + POISON_FREE, s->object_size - 1) || !check_bytes_and_report(s, page, p, "Poison", - p + s->objsize - 1, POISON_END, 1))) + p + s->object_size - 1, POISON_END, 1))) return 0; /* * check_pad_bytes cleans up on its own. @@ -926,7 +926,7 @@ static void trace(struct kmem_cache *s, struct page *page, void *object, page->freelist); if (!alloc) - print_section("Object ", (void *)object, s->objsize); + print_section("Object ", (void *)object, s->object_size); dump_stack(); } @@ -942,14 +942,14 @@ static inline int slab_pre_alloc_hook(struct kmem_cache *s, gfp_t flags) lockdep_trace_alloc(flags); might_sleep_if(flags & __GFP_WAIT); - return should_failslab(s->objsize, flags, s->flags); + return should_failslab(s->object_size, flags, s->flags); } static inline void slab_post_alloc_hook(struct kmem_cache *s, gfp_t flags, void *object) { flags &= gfp_allowed_mask; kmemcheck_slab_alloc(s, flags, object, slab_ksize(s)); - kmemleak_alloc_recursive(object, s->objsize, 1, s->flags, flags); + kmemleak_alloc_recursive(object, s->object_size, 1, s->flags, flags); } static inline void slab_free_hook(struct kmem_cache *s, void *x) @@ -966,13 +966,13 @@ static inline void slab_free_hook(struct kmem_cache *s, void *x) unsigned long flags; local_irq_save(flags); - kmemcheck_slab_free(s, x, s->objsize); - debug_check_no_locks_freed(x, s->objsize); + kmemcheck_slab_free(s, x, s->object_size); + debug_check_no_locks_freed(x, s->object_size); local_irq_restore(flags); } #endif if (!(s->flags & SLAB_DEBUG_OBJECTS)) - debug_check_no_obj_freed(x, s->objsize); + debug_check_no_obj_freed(x, s->object_size); } /* @@ -1207,7 +1207,7 @@ out: __setup("slub_debug", setup_slub_debug); -static unsigned long kmem_cache_flags(unsigned long objsize, +static unsigned long kmem_cache_flags(unsigned long object_size, unsigned long flags, const char *name, void (*ctor)(void *)) { @@ -1237,7 +1237,7 @@ static inline int check_object(struct kmem_cache *s, struct page *page, static inline void add_full(struct kmem_cache *s, struct kmem_cache_node *n, struct page *page) {} static inline void remove_full(struct kmem_cache *s, struct page *page) {} -static inline unsigned long kmem_cache_flags(unsigned long objsize, +static inline unsigned long kmem_cache_flags(unsigned long object_size, unsigned long flags, const char *name, void (*ctor)(void *)) { @@ -2098,10 +2098,10 @@ slab_out_of_memory(struct kmem_cache *s, gfp_t gfpflags, int nid) "SLUB: Unable to allocate memory on node %d (gfp=0x%x)\n", nid, gfpflags); printk(KERN_WARNING " cache: %s, object size: %d, buffer size: %d, " - "default order: %d, min order: %d\n", s->name, s->objsize, + "default order: %d, min order: %d\n", s->name, s->object_size, s->size, oo_order(s->oo), oo_order(s->min)); - if (oo_order(s->min) > get_order(s->objsize)) + if (oo_order(s->min) > get_order(s->object_size)) printk(KERN_WARNING " %s debugging increased min order, use " "slub_debug=O to disable.\n", s->name); @@ -2374,7 +2374,7 @@ redo: } if (unlikely(gfpflags & __GFP_ZERO) && object) - memset(object, 0, s->objsize); + memset(object, 0, s->object_size); slab_post_alloc_hook(s, gfpflags, object); @@ -2385,7 +2385,7 @@ void *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfpflags) { void *ret = slab_alloc(s, gfpflags, NUMA_NO_NODE, _RET_IP_); - trace_kmem_cache_alloc(_RET_IP_, ret, s->objsize, s->size, gfpflags); + trace_kmem_cache_alloc(_RET_IP_, ret, s->object_size, s->size, gfpflags); return ret; } @@ -2415,7 +2415,7 @@ void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node) void *ret = slab_alloc(s, gfpflags, node, _RET_IP_); trace_kmem_cache_alloc_node(_RET_IP_, ret, - s->objsize, s->size, gfpflags, node); + s->object_size, s->size, gfpflags, node); return ret; } @@ -2910,7 +2910,7 @@ static void set_min_partial(struct kmem_cache *s, unsigned long min) static int calculate_sizes(struct kmem_cache *s, int forced_order) { unsigned long flags = s->flags; - unsigned long size = s->objsize; + unsigned long size = s->object_size; unsigned long align = s->align; int order; @@ -2939,7 +2939,7 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order) * end of the object and the free pointer. If not then add an * additional word to have some bytes to store Redzone information. */ - if ((flags & SLAB_RED_ZONE) && size == s->objsize) + if ((flags & SLAB_RED_ZONE) && size == s->object_size) size += sizeof(void *); #endif @@ -2987,7 +2987,7 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order) * user specified and the dynamic determination of cache line size * on bootup. */ - align = calculate_alignment(flags, align, s->objsize); + align = calculate_alignment(flags, align, s->object_size); s->align = align; /* @@ -3035,7 +3035,7 @@ static int kmem_cache_open(struct kmem_cache *s, memset(s, 0, kmem_size); s->name = name; s->ctor = ctor; - s->objsize = size; + s->object_size = size; s->align = align; s->flags = kmem_cache_flags(size, flags, name, ctor); s->reserved = 0; @@ -3050,7 +3050,7 @@ static int kmem_cache_open(struct kmem_cache *s, * Disable debugging flags that store metadata if the min slab * order increased. */ - if (get_order(s->size) > get_order(s->objsize)) { + if (get_order(s->size) > get_order(s->object_size)) { s->flags &= ~DEBUG_METADATA_FLAGS; s->offset = 0; if (!calculate_sizes(s, -1)) @@ -3124,7 +3124,7 @@ error: */ unsigned int kmem_cache_size(struct kmem_cache *s) { - return s->objsize; + return s->object_size; } EXPORT_SYMBOL(kmem_cache_size); @@ -3853,11 +3853,11 @@ void __init kmem_cache_init(void) if (s && s->size) { char *name = kasprintf(GFP_NOWAIT, - "dma-kmalloc-%d", s->objsize); + "dma-kmalloc-%d", s->object_size); BUG_ON(!name); kmalloc_dma_caches[i] = create_kmalloc_cache(name, - s->objsize, SLAB_CACHE_DMA); + s->object_size, SLAB_CACHE_DMA); } } #endif @@ -3951,7 +3951,7 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t size, * Adjust the object sizes so that we clear * the complete object on kzalloc. */ - s->objsize = max(s->objsize, (int)size); + s->object_size = max(s->object_size, (int)size); s->inuse = max_t(int, s->inuse, ALIGN(size, sizeof(void *))); if (sysfs_slab_alias(s, name)) { @@ -4634,7 +4634,7 @@ SLAB_ATTR_RO(align); static ssize_t object_size_show(struct kmem_cache *s, char *buf) { - return sprintf(buf, "%d\n", s->objsize); + return sprintf(buf, "%d\n", s->object_size); } SLAB_ATTR_RO(object_size); @@ -5438,7 +5438,7 @@ __initcall(slab_sysfs_init); static void print_slabinfo_header(struct seq_file *m) { seq_puts(m, "slabinfo - version: 2.1\n"); - seq_puts(m, "# name " + seq_puts(m, "# name " " "); seq_puts(m, " : tunables "); seq_puts(m, " : slabdata "); -- cgit v1.2.3 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') 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 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') 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 From 846248136dd3a6723135b8515ed7dc4c52a7b2ae Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 4 Jun 2012 14:42:54 -0700 Subject: drm/i915/context: create & destroy ioctls Add the interfaces to allow user space to create and destroy contexts. Contexts are destroyed automatically if the file descriptor for the dri device is closed. Following convention as usual here causes checkpatch warnings. v2: with is_initialized, no longer need to init at create drop the context switch on create (daniel) v3: Use interruptible lock (Chris) return -ENODEV in !GEM case (Chris) Signed-off-by: Ben Widawsky --- drivers/gpu/drm/i915/i915_dma.c | 2 ++ drivers/gpu/drm/i915/i915_drv.h | 4 +++ drivers/gpu/drm/i915/i915_gem_context.c | 55 +++++++++++++++++++++++++++++++++ include/drm/i915_drm.h | 15 +++++++++ 4 files changed, 76 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 3f3aca8c3767..ba75af12f1fd 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1848,6 +1848,8 @@ struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_SET_SPRITE_COLORKEY, intel_sprite_set_colorkey, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I915_GET_SPRITE_COLORKEY, intel_sprite_get_colorkey, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I915_GEM_WAIT, i915_gem_wait_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_CREATE, i915_gem_context_create_ioctl, DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_DESTROY, i915_gem_context_destroy_ioctl, DRM_UNLOCKED), }; int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 6edc02b6dda4..03e7f9e683e3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1386,6 +1386,10 @@ void i915_gem_context_open(struct drm_device *dev, struct drm_file *file); void i915_gem_context_close(struct drm_device *dev, struct drm_file *file); int i915_switch_context(struct intel_ring_buffer *ring, struct drm_file *file, int to_id); +int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); +int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); /* i915_gem_gtt.c */ int __must_check i915_gem_init_aliasing_ppgtt(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 6a7b67d9f43f..5642c4019b53 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -489,3 +489,58 @@ int i915_switch_context(struct intel_ring_buffer *ring, drm_gem_object_unreference(&from_obj->base); return ret; } + +int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_context_create *args = data; + struct drm_i915_file_private *file_priv = file->driver_priv; + struct i915_hw_context *ctx; + int ret; + + if (!(dev->driver->driver_features & DRIVER_GEM)) + return -ENODEV; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + ret = create_hw_context(dev, file_priv, &ctx); + mutex_unlock(&dev->struct_mutex); + + args->ctx_id = ctx->id; + DRM_DEBUG_DRIVER("HW context %d created\n", args->ctx_id); + + return ret; +} + +int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_gem_context_destroy *args = data; + struct drm_i915_file_private *file_priv = file->driver_priv; + struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_hw_context *ctx; + int ret; + + if (!(dev->driver->driver_features & DRIVER_GEM)) + return -ENODEV; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + ctx = i915_gem_context_get(file_priv, args->ctx_id); + if (!ctx) { + mutex_unlock(&dev->struct_mutex); + return -EINVAL; + } + + do_destroy(ctx); + + mutex_unlock(&dev->struct_mutex); + + DRM_DEBUG_DRIVER("HW context %d destroyed\n", args->ctx_id); + return 0; +} diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index 5c28ee1d1911..5da73244486a 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -201,6 +201,8 @@ typedef struct _drm_i915_sarea { #define DRM_I915_GET_SPRITE_COLORKEY 0x2a #define DRM_I915_SET_SPRITE_COLORKEY 0x2b #define DRM_I915_GEM_WAIT 0x2c +#define DRM_I915_GEM_CONTEXT_CREATE 0x2d +#define DRM_I915_GEM_CONTEXT_DESTROY 0x2e #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -245,6 +247,8 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_SET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) #define DRM_IOCTL_I915_GET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) #define DRM_IOCTL_I915_GEM_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_WAIT, struct drm_i915_gem_wait) +#define DRM_IOCTL_I915_GEM_CONTEXT_CREATE DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create) +#define DRM_IOCTL_I915_GEM_CONTEXT_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY, struct drm_i915_gem_context_destroy) /* Allow drivers to submit batchbuffers directly to hardware, relying * on the security mechanisms provided by hardware. @@ -897,4 +901,15 @@ struct drm_i915_gem_wait { __s64 timeout_ns; }; +struct drm_i915_gem_context_create { + /* output: id of new context*/ + __u32 ctx_id; + __u32 pad; +}; + +struct drm_i915_gem_context_destroy { + __u32 ctx_id; + __u32 pad; +}; + #endif /* _I915_DRM_H_ */ -- cgit v1.2.3 From 6e0a69dbc81b88f5a42e08344203021571f6fb2f Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 4 Jun 2012 14:42:55 -0700 Subject: drm/i915/context: switch contexts with execbuf2 Use the rsvd1 field in execbuf2 to specify the context ID associated with the workload. This will allow the driver to do the proper context switch when/if needed. v2: Add checks for context switches on rings not supporting contexts. Before the code would silently ignore such requests. Signed-off-by: Ben Widawsky --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 16 ++++++++++++++++ include/drm/i915_drm.h | 8 +++++++- 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 974a9f1068a3..f32d02464bce 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -1044,6 +1044,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, struct drm_i915_gem_object *batch_obj; struct drm_clip_rect *cliprects = NULL; struct intel_ring_buffer *ring; + u32 ctx_id = i915_execbuffer2_get_context_id(*args); u32 exec_start, exec_len; u32 seqno; u32 mask; @@ -1065,9 +1066,19 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, break; case I915_EXEC_BSD: ring = &dev_priv->ring[VCS]; + if (ctx_id != 0) { + DRM_DEBUG("Ring %s doesn't support contexts\n", + ring->name); + return -EPERM; + } break; case I915_EXEC_BLT: ring = &dev_priv->ring[BCS]; + if (ctx_id != 0) { + DRM_DEBUG("Ring %s doesn't support contexts\n", + ring->name); + return -EPERM; + } break; default: DRM_DEBUG("execbuf with unknown ring: %d\n", @@ -1261,6 +1272,10 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, goto err; } + ret = i915_switch_context(ring, file, ctx_id); + if (ret) + goto err; + trace_i915_gem_ring_dispatch(ring, seqno); exec_start = batch_obj->gtt_offset + args->batch_start_offset; @@ -1367,6 +1382,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, exec2.num_cliprects = args->num_cliprects; exec2.cliprects_ptr = args->cliprects_ptr; exec2.flags = I915_EXEC_RENDER; + i915_execbuffer2_set_context_id(exec2, 0); ret = i915_gem_do_execbuffer(dev, data, file, &exec2, exec2_list); if (!ret) { diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index 5da73244486a..8cc70837f929 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -663,13 +663,19 @@ struct drm_i915_gem_execbuffer2 { #define I915_EXEC_CONSTANTS_ABSOLUTE (1<<6) #define I915_EXEC_CONSTANTS_REL_SURFACE (2<<6) /* gen4/5 only */ __u64 flags; - __u64 rsvd1; + __u64 rsvd1; /* now used for context info */ __u64 rsvd2; }; /** Resets the SO write offset registers for transform feedback on gen7. */ #define I915_EXEC_GEN7_SOL_RESET (1<<8) +#define I915_EXEC_CONTEXT_ID_MASK (0xffffffff) +#define i915_execbuffer2_set_context_id(eb2, context) \ + (eb2).rsvd1 = context & I915_EXEC_CONTEXT_ID_MASK +#define i915_execbuffer2_get_context_id(eb2) \ + ((eb2).rsvd1 & I915_EXEC_CONTEXT_ID_MASK) + struct drm_i915_gem_pin { /** Handle of the buffer to be pinned. */ __u32 handle; -- cgit v1.2.3 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') 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 From 36393395536064e483b73d173f6afc103eadfbc4 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 14 Jun 2012 22:21:46 -0700 Subject: ipv4: Handle PMTU in all ICMP error handlers. With ip_rt_frag_needed() removed, we have to explicitly update PMTU information in every ICMP error handler. Create two helper functions to facilitate this. 1) ipv4_sk_update_pmtu() This updates the PMTU when we have a socket context to work with. 2) ipv4_update_pmtu() Raw version, used when no socket context is available. For this interface, we essentially just pass in explicit arguments for the flow identity information we would have extracted from the socket. And you'll notice that ipv4_sk_update_pmtu() is simply implemented in terms of ipv4_update_pmtu() Note that __ip_route_output_key() is used, rather than something like ip_route_output_flow() or ip_route_output_key(). This is because we absolutely do not want to end up with a route that does IPSEC encapsulation and the like. Instead, we only want the route that would get us to the node described by the outermost IP header. Reported-by: Steffen Klassert Signed-off-by: David S. Miller --- include/net/route.h | 5 ++++- net/ipv4/ah4.c | 1 + net/ipv4/esp4.c | 1 + net/ipv4/ip_gre.c | 14 ++++++++++---- net/ipv4/ipcomp.c | 1 + net/ipv4/ipip.c | 15 +++++++++++---- net/ipv4/ping.c | 1 + net/ipv4/raw.c | 3 +++ net/ipv4/route.c | 28 ++++++++++++++++++++++++++++ net/ipv4/udp.c | 1 + net/ipv6/sit.c | 15 +++++++++++---- 11 files changed, 72 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/route.h b/include/net/route.h index a36ae429ed5d..47eb25ac1f7f 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -215,7 +215,10 @@ static inline int ip_route_input_noref(struct sk_buff *skb, __be32 dst, __be32 s return ip_route_input_common(skb, dst, src, tos, devin, true); } -extern void ip_rt_send_redirect(struct sk_buff *skb); +extern void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu, + int oif, u32 mark, u8 protocol, int flow_flags); +extern void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu); +extern void ip_rt_send_redirect(struct sk_buff *skb); extern unsigned int inet_addr_type(struct net *net, __be32 addr); extern unsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev, __be32 addr); diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index e8f2617ecd47..916d5ecaf6c6 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -408,6 +408,7 @@ static void ah4_err(struct sk_buff *skb, u32 info) return; pr_debug("pmtu discovery on SA AH/%08x/%08x\n", ntohl(ah->spi), ntohl(iph->daddr)); + ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_AH, 0); xfrm_state_put(x); } diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index cb982a61536f..7b95b49a36ce 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -494,6 +494,7 @@ static void esp4_err(struct sk_buff *skb, u32 info) return; NETDEBUG(KERN_DEBUG "pmtu discovery on SA ESP/%08x/%08x\n", ntohl(esph->spi), ntohl(iph->daddr)); + ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_ESP, 0); xfrm_state_put(x); } diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index f49047b79609..594cec35ac4d 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -516,9 +516,6 @@ static void ipgre_err(struct sk_buff *skb, u32 info) case ICMP_PORT_UNREACH: /* Impossible event. */ return; - case ICMP_FRAG_NEEDED: - /* Soft state for pmtu is maintained by IP core. */ - return; default: /* All others are translated to HOST_UNREACH. rfc2003 contains "deep thoughts" about NET_UNREACH, @@ -538,7 +535,16 @@ static void ipgre_err(struct sk_buff *skb, u32 info) flags & GRE_KEY ? *(((__be32 *)p) + (grehlen / 4) - 1) : 0, p[1]); - if (t == NULL || t->parms.iph.daddr == 0 || + if (t == NULL) + goto out; + + if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) { + ipv4_update_pmtu(skb, dev_net(skb->dev), info, + t->parms.link, 0, IPPROTO_GRE, 0); + goto out; + } + + if (t->parms.iph.daddr == 0 || ipv4_is_multicast(t->parms.iph.daddr)) goto out; diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c index 63b64c45a826..b91375482d84 100644 --- a/net/ipv4/ipcomp.c +++ b/net/ipv4/ipcomp.c @@ -42,6 +42,7 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info) return; NETDEBUG(KERN_DEBUG "pmtu discovery on SA IPCOMP/%08x/%pI4\n", spi, &iph->daddr); + ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_COMP, 0); xfrm_state_put(x); } diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 2d0f99bf61b3..715338a1b205 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -348,9 +348,6 @@ static int ipip_err(struct sk_buff *skb, u32 info) case ICMP_PORT_UNREACH: /* Impossible event. */ return 0; - case ICMP_FRAG_NEEDED: - /* Soft state for pmtu is maintained by IP core. */ - return 0; default: /* All others are translated to HOST_UNREACH. rfc2003 contains "deep thoughts" about NET_UNREACH, @@ -369,7 +366,17 @@ static int ipip_err(struct sk_buff *skb, u32 info) rcu_read_lock(); t = ipip_tunnel_lookup(dev_net(skb->dev), iph->daddr, iph->saddr); - if (t == NULL || t->parms.iph.daddr == 0) + if (t == NULL) + goto out; + + if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) { + ipv4_update_pmtu(skb, dev_net(skb->dev), info, + t->dev->ifindex, 0, IPPROTO_IPIP, 0); + err = 0; + goto out; + } + + if (t->parms.iph.daddr == 0) goto out; err = 0; diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 2c00e8bf684d..340fcf29a966 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -371,6 +371,7 @@ void ping_err(struct sk_buff *skb, u32 info) break; case ICMP_DEST_UNREACH: if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ + ipv4_sk_update_pmtu(skb, sk, info); if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) { err = EMSGSIZE; harderr = 1; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 4032b818f3e4..659ddfb10947 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -216,6 +216,9 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info) int err = 0; int harderr = 0; + if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) + ipv4_sk_update_pmtu(skb, sk, info); + /* Report error on raw socket, if: 1. User requested ip_recverr. 2. Socket is connected (otherwise the error indication diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 655506af47ca..41df5297a412 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1711,6 +1711,34 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu) } } +void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu, + int oif, u32 mark, u8 protocol, int flow_flags) +{ + const struct iphdr *iph = (const struct iphdr *)skb->data; + struct flowi4 fl4; + struct rtable *rt; + + flowi4_init_output(&fl4, oif, mark, RT_TOS(iph->tos), RT_SCOPE_UNIVERSE, + protocol, flow_flags | FLOWI_FLAG_PRECOW_METRICS, + iph->daddr, iph->saddr, 0, 0); + rt = __ip_route_output_key(net, &fl4); + if (!IS_ERR(rt)) { + ip_rt_update_pmtu(&rt->dst, mtu); + ip_rt_put(rt); + } +} +EXPORT_SYMBOL_GPL(ipv4_update_pmtu); + +void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) +{ + const struct inet_sock *inet = inet_sk(sk); + + return ipv4_update_pmtu(skb, sock_net(sk), mtu, + sk->sk_bound_dev_if, sk->sk_mark, + inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, + inet_sk_flowi_flags(sk)); +} +EXPORT_SYMBOL_GPL(ipv4_sk_update_pmtu); static void ipv4_validate_peer(struct rtable *rt) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index eaca73644e79..db017efb76ea 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -615,6 +615,7 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) break; case ICMP_DEST_UNREACH: if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ + ipv4_sk_update_pmtu(skb, sk, info); if (inet->pmtudisc != IP_PMTUDISC_DONT) { err = EMSGSIZE; harderr = 1; diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 60415711563f..49aea94c9be3 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -527,9 +527,6 @@ static int ipip6_err(struct sk_buff *skb, u32 info) case ICMP_PORT_UNREACH: /* Impossible event. */ return 0; - case ICMP_FRAG_NEEDED: - /* Soft state for pmtu is maintained by IP core. */ - return 0; default: /* All others are translated to HOST_UNREACH. rfc2003 contains "deep thoughts" about NET_UNREACH, @@ -551,7 +548,17 @@ static int ipip6_err(struct sk_buff *skb, u32 info) skb->dev, iph->daddr, iph->saddr); - if (t == NULL || t->parms.iph.daddr == 0) + if (t == NULL) + goto out; + + if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) { + ipv4_update_pmtu(skb, dev_net(skb->dev), info, + t->dev->ifindex, 0, IPPROTO_IPV6, 0); + err = 0; + goto out; + } + + if (t->parms.iph.daddr == 0) goto out; err = 0; -- cgit v1.2.3 From 0c2498f1660878339350bea8d18550b1b87ca055 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 28 Jan 2011 09:40:40 +0100 Subject: pwm: Add PWM framework support This patch adds framework support for PWM (pulse width modulation) devices. The is a barebone PWM API already in the kernel under include/linux/pwm.h, but it does not allow for multiple drivers as each of them implements the pwm_*() functions. There are other PWM framework patches around from Bill Gatliff. Unlike his framework this one does not change the existing API for PWMs so that this framework can act as a drop in replacement for the existing API. Why another framework? Several people argue that there should not be another framework for PWMs but they should be integrated into one of the existing frameworks like led or hwmon. Unlike these frameworks the PWM framework is agnostic to the purpose of the PWM. In fact, a PWM can drive a LED, but this makes the LED framework a user of a PWM, like already done in leds-pwm.c. The gpio framework also is not suitable for PWMs. Every gpio could be turned into a PWM using timer based toggling, but on the other hand not every PWM hardware device can be turned into a gpio due to the lack of hardware capabilities. This patch does not try to improve the PWM API yet, this could be done in subsequent patches. Signed-off-by: Sascha Hauer Acked-by: Kurt Van Dijck Reviewed-by: Arnd Bergmann Reviewed-by: Matthias Kaehlcke Reviewed-by: Mark Brown Reviewed-by: Shawn Guo [thierry.reding@avionic-design.de: fixup typos, kerneldoc comments] Signed-off-by: Thierry Reding --- Documentation/pwm.txt | 54 ++++++++++++ MAINTAINERS | 6 ++ drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/pwm/Kconfig | 12 +++ drivers/pwm/Makefile | 1 + drivers/pwm/core.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pwm.h | 38 +++++++++ 8 files changed, 341 insertions(+) create mode 100644 Documentation/pwm.txt create mode 100644 drivers/pwm/Kconfig create mode 100644 drivers/pwm/Makefile create mode 100644 drivers/pwm/core.c (limited to 'include') diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt new file mode 100644 index 000000000000..03e39d145911 --- /dev/null +++ b/Documentation/pwm.txt @@ -0,0 +1,54 @@ +Pulse Width Modulation (PWM) interface + +This provides an overview about the Linux PWM interface + +PWMs are commonly used for controlling LEDs, fans or vibrators in +cell phones. PWMs with a fixed purpose have no need implementing +the Linux PWM API (although they could). However, PWMs are often +found as discrete devices on SoCs which have no fixed purpose. It's +up to the board designer to connect them to LEDs or fans. To provide +this kind of flexibility the generic PWM API exists. + +Identifying PWMs +---------------- + +Users of the legacy PWM API use unique IDs to refer to PWM devices. One +goal of the new PWM framework is to get rid of this global namespace. + +Using PWMs +---------- + +A PWM can be requested using pwm_request() and freed after usage with +pwm_free(). After being requested a PWM has to be configured using + +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns); + +To start/stop toggling the PWM output use pwm_enable()/pwm_disable(). + +Implementing a PWM driver +------------------------- + +Currently there are two ways to implement pwm drivers. Traditionally +there only has been the barebone API meaning that each driver has +to implement the pwm_*() functions itself. This means that it's impossible +to have multiple PWM drivers in the system. For this reason it's mandatory +for new drivers to use the generic PWM framework. +A new PWM device can be added using pwmchip_add() and removed again with +pwmchip_remove(). pwmchip_add() takes a filled in struct pwm_chip as +argument which provides the ops and the pwm id to the framework. + +Locking +------- + +The PWM core list manipulations are protected by a mutex, so pwm_request() +and pwm_free() may not be called from an atomic context. Currently the +PWM core does not enforce any locking to pwm_enable(), pwm_disable() and +pwm_config(), so the calling context is currently driver specific. This +is an issue derived from the former barebone API and should be fixed soon. + +Helpers +------- + +Currently a PWM can only be configured with period_ns and duty_ns. For several +use cases freq_hz and duty_percent might be better. Instead of calculating +this in your driver please consider adding appropriate helpers to the framework. diff --git a/MAINTAINERS b/MAINTAINERS index 14bc7071f9df..67d6cb70cb7e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5487,6 +5487,12 @@ S: Maintained F: Documentation/video4linux/README.pvrusb2 F: drivers/media/video/pvrusb2/ +PWM core +M: Sascha Hauer +L: linux-kernel@vger.kernel.org +S: Maintained +F: drivers/pwm/ + PXA2xx/PXA3xx SUPPORT M: Eric Miao M: Russell King diff --git a/drivers/Kconfig b/drivers/Kconfig index bfc918633fd9..805c432c9439 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -148,4 +148,6 @@ source "drivers/iio/Kconfig" source "drivers/vme/Kconfig" +source "drivers/pwm/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 2ba29ffef2cb..bd36f09f2246 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -8,6 +8,7 @@ # GPIO must come after pinctrl as gpios may need to mux pins etc obj-y += pinctrl/ obj-y += gpio/ +obj-y += pwm/ obj-$(CONFIG_PCI) += pci/ obj-$(CONFIG_PARISC) += parisc/ obj-$(CONFIG_RAPIDIO) += rapidio/ diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig new file mode 100644 index 000000000000..93c1052291a3 --- /dev/null +++ b/drivers/pwm/Kconfig @@ -0,0 +1,12 @@ +menuconfig PWM + bool "PWM Support" + help + This enables PWM support through the generic PWM framework. + You only need to enable this, if you also want to enable + one or more of the PWM drivers below. + + If unsure, say N. + +if PWM + +endif diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile new file mode 100644 index 000000000000..3469c3d28b7a --- /dev/null +++ b/drivers/pwm/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PWM) += core.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c new file mode 100644 index 000000000000..0b8a38eca23c --- /dev/null +++ b/drivers/pwm/core.c @@ -0,0 +1,227 @@ +/* + * Generic pwmlib implementation + * + * Copyright (C) 2011 Sascha Hauer + * + * 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, 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct pwm_device { + struct pwm_chip *chip; + const char *label; + unsigned long flags; +#define FLAG_REQUESTED 0 +#define FLAG_ENABLED 1 + struct list_head node; +}; + +static LIST_HEAD(pwm_list); + +static DEFINE_MUTEX(pwm_lock); + +static struct pwm_device *_find_pwm(int pwm_id) +{ + struct pwm_device *pwm; + + list_for_each_entry(pwm, &pwm_list, node) { + if (pwm->chip->pwm_id == pwm_id) + return pwm; + } + + return NULL; +} + +/** + * pwmchip_add() - register a new PWM chip + * @chip: the PWM chip to add + */ +int pwmchip_add(struct pwm_chip *chip) +{ + struct pwm_device *pwm; + int ret = 0; + + pwm = kzalloc(sizeof(*pwm), GFP_KERNEL); + if (!pwm) + return -ENOMEM; + + pwm->chip = chip; + + mutex_lock(&pwm_lock); + + if (chip->pwm_id >= 0 && _find_pwm(chip->pwm_id)) { + ret = -EBUSY; + goto out; + } + + list_add_tail(&pwm->node, &pwm_list); +out: + mutex_unlock(&pwm_lock); + + if (ret) + kfree(pwm); + + return ret; +} +EXPORT_SYMBOL_GPL(pwmchip_add); + +/** + * pwmchip_remove() - remove a PWM chip + * @chip: the PWM chip to remove + * + * Removes a PWM chip. This function may return busy if the PWM chip provides + * a PWM device that is still requested. + */ +int pwmchip_remove(struct pwm_chip *chip) +{ + struct pwm_device *pwm; + int ret = 0; + + mutex_lock(&pwm_lock); + + pwm = _find_pwm(chip->pwm_id); + if (!pwm) { + ret = -ENOENT; + goto out; + } + + if (test_bit(FLAG_REQUESTED, &pwm->flags)) { + ret = -EBUSY; + goto out; + } + + list_del(&pwm->node); + + kfree(pwm); +out: + mutex_unlock(&pwm_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(pwmchip_remove); + +/** + * pwm_request() - request a PWM device + * @pwm_id: global PWM device index + * @label: PWM device label + */ +struct pwm_device *pwm_request(int pwm_id, const char *label) +{ + struct pwm_device *pwm; + int ret; + + mutex_lock(&pwm_lock); + + pwm = _find_pwm(pwm_id); + if (!pwm) { + pwm = ERR_PTR(-ENOENT); + goto out; + } + + if (test_bit(FLAG_REQUESTED, &pwm->flags)) { + pwm = ERR_PTR(-EBUSY); + goto out; + } + + if (!try_module_get(pwm->chip->ops->owner)) { + pwm = ERR_PTR(-ENODEV); + goto out; + } + + if (pwm->chip->ops->request) { + ret = pwm->chip->ops->request(pwm->chip); + if (ret) { + pwm = ERR_PTR(ret); + goto out_put; + } + } + + pwm->label = label; + set_bit(FLAG_REQUESTED, &pwm->flags); + + goto out; + +out_put: + module_put(pwm->chip->ops->owner); +out: + mutex_unlock(&pwm_lock); + + return pwm; +} +EXPORT_SYMBOL_GPL(pwm_request); + +/** + * pwm_free() - free a PWM device + * @pwm: PWM device + */ +void pwm_free(struct pwm_device *pwm) +{ + mutex_lock(&pwm_lock); + + if (!test_and_clear_bit(FLAG_REQUESTED, &pwm->flags)) { + pr_warning("PWM device already freed\n"); + goto out; + } + + pwm->label = NULL; + + module_put(pwm->chip->ops->owner); +out: + mutex_unlock(&pwm_lock); +} +EXPORT_SYMBOL_GPL(pwm_free); + +/** + * pwm_config() - change a PWM device configuration + * @pwm: PWM device + * @duty_ns: "on" time (in nanoseconds) + * @period_ns: duration (in nanoseconds) of one cycle + */ +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +{ + return pwm->chip->ops->config(pwm->chip, duty_ns, period_ns); +} +EXPORT_SYMBOL_GPL(pwm_config); + +/** + * pwm_enable() - start a PWM output toggling + * @pwm: PWM device + */ +int pwm_enable(struct pwm_device *pwm) +{ + if (!test_and_set_bit(FLAG_ENABLED, &pwm->flags)) + return pwm->chip->ops->enable(pwm->chip); + + return 0; +} +EXPORT_SYMBOL_GPL(pwm_enable); + +/** + * pwm_disable() - stop a PWM output toggling + * @pwm: PWM device + */ +void pwm_disable(struct pwm_device *pwm) +{ + if (test_and_clear_bit(FLAG_ENABLED, &pwm->flags)) + pwm->chip->ops->disable(pwm->chip); +} +EXPORT_SYMBOL_GPL(pwm_disable); diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 7c775751392c..1f308a13105f 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -28,4 +28,42 @@ int pwm_enable(struct pwm_device *pwm); */ void pwm_disable(struct pwm_device *pwm); +#ifdef CONFIG_PWM +struct pwm_chip; + +/** + * struct pwm_ops - PWM controller operations + * @request: optional hook for requesting a PWM + * @free: optional hook for freeing a PWM + * @config: configure duty cycles and period length for this PWM + * @enable: enable PWM output toggling + * @disable: disable PWM output toggling + * @owner: helps prevent removal of modules exporting active PWMs + */ +struct pwm_ops { + int (*request)(struct pwm_chip *chip); + void (*free)(struct pwm_chip *chip); + int (*config)(struct pwm_chip *chip, int duty_ns, + int period_ns); + int (*enable)(struct pwm_chip *chip); + void (*disable)(struct pwm_chip *chip); + struct module *owner; +}; + +/** + * struct pwm_chip - abstract a PWM + * @pwm_id: global PWM device index + * @label: PWM device label + * @ops: controller operations + */ +struct pwm_chip { + int pwm_id; + const char *label; + struct pwm_ops *ops; +}; + +int pwmchip_add(struct pwm_chip *chip); +int pwmchip_remove(struct pwm_chip *chip); +#endif + #endif /* __LINUX_PWM_H */ -- cgit v1.2.3 From f051c466cf690ac661d713d3ceb56b4efcecc853 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 14 Dec 2011 11:12:23 +0100 Subject: pwm: Allow chips to support multiple PWMs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many PWM controllers provide access to more than a single PWM output and may even share some resource among them. Allowing a PWM chip to provide multiple PWM devices enables better sharing of those resources. As a side-effect this change allows easy integration with the device tree where a given PWM can be looked up based on the PWM chip's phandle and a corresponding index. This commit modifies the PWM core to support multiple PWMs per struct pwm_chip. It achieves this in a similar way to how gpiolib works, by allowing PWM ranges to be requested dynamically (pwm_chip.base == -1) or starting at a given offset (pwm_chip.base >= 0). A chip specifies how many PWMs it controls using the npwm member. Each of the functions in the pwm_ops structure gets an additional argument that specified the PWM number (it can be converted to a per-chip index by subtracting the chip's base). The total maximum number of PWM devices is currently fixed to 1024 while the data is actually stored in a radix tree, thus saving resources if not all of them are used. Reviewed-by: Mark Brown Reviewed-by: Shawn Guo [eric@eukrea.com: fix error handling in pwmchip_add] Signed-off-by: Eric Bénard Signed-off-by: Thierry Reding --- Documentation/pwm.txt | 9 +- drivers/pwm/core.c | 267 +++++++++++++++++++++++++++++++++++--------------- include/linux/pwm.h | 71 +++++++++++--- 3 files changed, 254 insertions(+), 93 deletions(-) (limited to 'include') diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt index 03e39d145911..48f598acdd16 100644 --- a/Documentation/pwm.txt +++ b/Documentation/pwm.txt @@ -33,9 +33,12 @@ there only has been the barebone API meaning that each driver has to implement the pwm_*() functions itself. This means that it's impossible to have multiple PWM drivers in the system. For this reason it's mandatory for new drivers to use the generic PWM framework. -A new PWM device can be added using pwmchip_add() and removed again with -pwmchip_remove(). pwmchip_add() takes a filled in struct pwm_chip as -argument which provides the ops and the pwm id to the framework. + +A new PWM controller/chip can be added using pwmchip_add() and removed +again with pwmchip_remove(). pwmchip_add() takes a filled in struct +pwm_chip as argument which provides a description of the PWM chip, the +number of PWM devices provider by the chip and the chip-specific +implementation of the supported PWM operations to the framework. Locking ------- diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 0b8a38eca23c..a447be128328 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -2,6 +2,7 @@ * Generic pwmlib implementation * * Copyright (C) 2011 Sascha Hauer + * Copyright (C) 2011-2012 Avionic Design GmbH * * 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 @@ -20,66 +21,161 @@ #include #include +#include #include #include #include #include #include -struct pwm_device { - struct pwm_chip *chip; - const char *label; - unsigned long flags; -#define FLAG_REQUESTED 0 -#define FLAG_ENABLED 1 - struct list_head node; -}; - -static LIST_HEAD(pwm_list); +#define MAX_PWMS 1024 static DEFINE_MUTEX(pwm_lock); +static LIST_HEAD(pwm_chips); +static DECLARE_BITMAP(allocated_pwms, MAX_PWMS); +static RADIX_TREE(pwm_tree, GFP_KERNEL); -static struct pwm_device *_find_pwm(int pwm_id) +static struct pwm_device *pwm_to_device(unsigned int pwm) { - struct pwm_device *pwm; + return radix_tree_lookup(&pwm_tree, pwm); +} + +static int alloc_pwms(int pwm, unsigned int count) +{ + unsigned int from = 0; + unsigned int start; + + if (pwm >= MAX_PWMS) + return -EINVAL; + + if (pwm >= 0) + from = pwm; - list_for_each_entry(pwm, &pwm_list, node) { - if (pwm->chip->pwm_id == pwm_id) - return pwm; + start = bitmap_find_next_zero_area(allocated_pwms, MAX_PWMS, from, + count, 0); + + if (pwm >= 0 && start != pwm) + return -EEXIST; + + if (start + count > MAX_PWMS) + return -ENOSPC; + + return start; +} + +static void free_pwms(struct pwm_chip *chip) +{ + unsigned int i; + + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; + radix_tree_delete(&pwm_tree, pwm->pwm); } - return NULL; + bitmap_clear(allocated_pwms, chip->base, chip->npwm); + + kfree(chip->pwms); + chip->pwms = NULL; +} + +static int pwm_device_request(struct pwm_device *pwm, const char *label) +{ + int err; + + if (test_bit(PWMF_REQUESTED, &pwm->flags)) + return -EBUSY; + + if (!try_module_get(pwm->chip->ops->owner)) + return -ENODEV; + + if (pwm->chip->ops->request) { + err = pwm->chip->ops->request(pwm->chip, pwm); + if (err) { + module_put(pwm->chip->ops->owner); + return err; + } + } + + set_bit(PWMF_REQUESTED, &pwm->flags); + pwm->label = label; + + return 0; +} + +/** + * pwm_set_chip_data() - set private chip data for a PWM + * @pwm: PWM device + * @data: pointer to chip-specific data + */ +int pwm_set_chip_data(struct pwm_device *pwm, void *data) +{ + if (!pwm) + return -EINVAL; + + pwm->chip_data = data; + + return 0; +} + +/** + * pwm_get_chip_data() - get private chip data for a PWM + * @pwm: PWM device + */ +void *pwm_get_chip_data(struct pwm_device *pwm) +{ + return pwm ? pwm->chip_data : NULL; } /** * pwmchip_add() - register a new PWM chip * @chip: the PWM chip to add + * + * Register a new PWM chip. If chip->base < 0 then a dynamically assigned base + * will be used. */ int pwmchip_add(struct pwm_chip *chip) { struct pwm_device *pwm; - int ret = 0; - - pwm = kzalloc(sizeof(*pwm), GFP_KERNEL); - if (!pwm) - return -ENOMEM; + unsigned int i; + int ret; - pwm->chip = chip; + if (!chip || !chip->dev || !chip->ops || !chip->ops->config || + !chip->ops->enable || !chip->ops->disable) + return -EINVAL; mutex_lock(&pwm_lock); - if (chip->pwm_id >= 0 && _find_pwm(chip->pwm_id)) { - ret = -EBUSY; + ret = alloc_pwms(chip->base, chip->npwm); + if (ret < 0) + goto out; + + chip->pwms = kzalloc(chip->npwm * sizeof(*pwm), GFP_KERNEL); + if (!chip->pwms) { + ret = -ENOMEM; goto out; } - list_add_tail(&pwm->node, &pwm_list); -out: - mutex_unlock(&pwm_lock); + chip->base = ret; + + for (i = 0; i < chip->npwm; i++) { + pwm = &chip->pwms[i]; + + pwm->chip = chip; + pwm->pwm = chip->base + i; + pwm->hwpwm = i; - if (ret) - kfree(pwm); + radix_tree_insert(&pwm_tree, pwm->pwm, pwm); + } + + bitmap_set(allocated_pwms, chip->base, chip->npwm); + + INIT_LIST_HEAD(&chip->list); + list_add(&chip->list, &pwm_chips); + ret = 0; + +out: + mutex_unlock(&pwm_lock); return ret; } EXPORT_SYMBOL_GPL(pwmchip_add); @@ -93,28 +189,25 @@ EXPORT_SYMBOL_GPL(pwmchip_add); */ int pwmchip_remove(struct pwm_chip *chip) { - struct pwm_device *pwm; + unsigned int i; int ret = 0; mutex_lock(&pwm_lock); - pwm = _find_pwm(chip->pwm_id); - if (!pwm) { - ret = -ENOENT; - goto out; - } + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; - if (test_bit(FLAG_REQUESTED, &pwm->flags)) { - ret = -EBUSY; - goto out; + if (test_bit(PWMF_REQUESTED, &pwm->flags)) { + ret = -EBUSY; + goto out; + } } - list_del(&pwm->node); + list_del_init(&chip->list); + free_pwms(chip); - kfree(pwm); out: mutex_unlock(&pwm_lock); - return ret; } EXPORT_SYMBOL_GPL(pwmchip_remove); @@ -124,50 +217,64 @@ EXPORT_SYMBOL_GPL(pwmchip_remove); * @pwm_id: global PWM device index * @label: PWM device label */ -struct pwm_device *pwm_request(int pwm_id, const char *label) +struct pwm_device *pwm_request(int pwm, const char *label) { - struct pwm_device *pwm; - int ret; + struct pwm_device *dev; + int err; + + if (pwm < 0 || pwm >= MAX_PWMS) + return ERR_PTR(-EINVAL); mutex_lock(&pwm_lock); - pwm = _find_pwm(pwm_id); - if (!pwm) { - pwm = ERR_PTR(-ENOENT); + dev = pwm_to_device(pwm); + if (!dev) { + dev = ERR_PTR(-EPROBE_DEFER); goto out; } - if (test_bit(FLAG_REQUESTED, &pwm->flags)) { - pwm = ERR_PTR(-EBUSY); - goto out; - } + err = pwm_device_request(dev, label); + if (err < 0) + dev = ERR_PTR(err); - if (!try_module_get(pwm->chip->ops->owner)) { - pwm = ERR_PTR(-ENODEV); - goto out; - } +out: + mutex_unlock(&pwm_lock); - if (pwm->chip->ops->request) { - ret = pwm->chip->ops->request(pwm->chip); - if (ret) { - pwm = ERR_PTR(ret); - goto out_put; - } - } + return dev; +} +EXPORT_SYMBOL_GPL(pwm_request); - pwm->label = label; - set_bit(FLAG_REQUESTED, &pwm->flags); +/** + * pwm_request_from_chip() - request a PWM device relative to a PWM chip + * @chip: PWM chip + * @index: per-chip index of the PWM to request + * @label: a literal description string of this PWM + * + * Returns the PWM at the given index of the given PWM chip. A negative error + * code is returned if the index is not valid for the specified PWM chip or + * if the PWM device cannot be requested. + */ +struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, + unsigned int index, + const char *label) +{ + struct pwm_device *pwm; + int err; - goto out; + if (!chip || index >= chip->npwm) + return ERR_PTR(-EINVAL); -out_put: - module_put(pwm->chip->ops->owner); -out: - mutex_unlock(&pwm_lock); + mutex_lock(&pwm_lock); + pwm = &chip->pwms[index]; + err = pwm_device_request(pwm, label); + if (err < 0) + pwm = ERR_PTR(err); + + mutex_unlock(&pwm_lock); return pwm; } -EXPORT_SYMBOL_GPL(pwm_request); +EXPORT_SYMBOL_GPL(pwm_request_from_chip); /** * pwm_free() - free a PWM device @@ -177,11 +284,14 @@ void pwm_free(struct pwm_device *pwm) { mutex_lock(&pwm_lock); - if (!test_and_clear_bit(FLAG_REQUESTED, &pwm->flags)) { + if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) { pr_warning("PWM device already freed\n"); goto out; } + if (pwm->chip->ops->free) + pwm->chip->ops->free(pwm->chip, pwm); + pwm->label = NULL; module_put(pwm->chip->ops->owner); @@ -198,7 +308,10 @@ EXPORT_SYMBOL_GPL(pwm_free); */ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) { - return pwm->chip->ops->config(pwm->chip, duty_ns, period_ns); + if (!pwm || period_ns == 0 || duty_ns > period_ns) + return -EINVAL; + + return pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns); } EXPORT_SYMBOL_GPL(pwm_config); @@ -208,10 +321,10 @@ EXPORT_SYMBOL_GPL(pwm_config); */ int pwm_enable(struct pwm_device *pwm) { - if (!test_and_set_bit(FLAG_ENABLED, &pwm->flags)) - return pwm->chip->ops->enable(pwm->chip); + if (pwm && !test_and_set_bit(PWMF_ENABLED, &pwm->flags)) + return pwm->chip->ops->enable(pwm->chip, pwm); - return 0; + return pwm ? 0 : -EINVAL; } EXPORT_SYMBOL_GPL(pwm_enable); @@ -221,7 +334,7 @@ EXPORT_SYMBOL_GPL(pwm_enable); */ void pwm_disable(struct pwm_device *pwm) { - if (test_and_clear_bit(FLAG_ENABLED, &pwm->flags)) - pwm->chip->ops->disable(pwm->chip); + if (pwm && test_and_clear_bit(PWMF_ENABLED, &pwm->flags)) + pwm->chip->ops->disable(pwm->chip, pwm); } EXPORT_SYMBOL_GPL(pwm_disable); diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 1f308a13105f..57103911f4c7 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -31,6 +31,33 @@ void pwm_disable(struct pwm_device *pwm); #ifdef CONFIG_PWM struct pwm_chip; +enum { + PWMF_REQUESTED = 1 << 0, + PWMF_ENABLED = 1 << 1, +}; + +struct pwm_device { + const char *label; + unsigned long flags; + unsigned int hwpwm; + unsigned int pwm; + struct pwm_chip *chip; + void *chip_data; + + unsigned int period; /* in nanoseconds */ +}; + +static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period) +{ + if (pwm) + pwm->period = period; +} + +static inline unsigned int pwm_get_period(struct pwm_device *pwm) +{ + return pwm ? pwm->period : 0; +} + /** * struct pwm_ops - PWM controller operations * @request: optional hook for requesting a PWM @@ -41,29 +68,47 @@ struct pwm_chip; * @owner: helps prevent removal of modules exporting active PWMs */ struct pwm_ops { - int (*request)(struct pwm_chip *chip); - void (*free)(struct pwm_chip *chip); - int (*config)(struct pwm_chip *chip, int duty_ns, - int period_ns); - int (*enable)(struct pwm_chip *chip); - void (*disable)(struct pwm_chip *chip); + int (*request)(struct pwm_chip *chip, + struct pwm_device *pwm); + void (*free)(struct pwm_chip *chip, + struct pwm_device *pwm); + int (*config)(struct pwm_chip *chip, + struct pwm_device *pwm, + int duty_ns, int period_ns); + int (*enable)(struct pwm_chip *chip, + struct pwm_device *pwm); + void (*disable)(struct pwm_chip *chip, + struct pwm_device *pwm); struct module *owner; }; /** - * struct pwm_chip - abstract a PWM - * @pwm_id: global PWM device index - * @label: PWM device label - * @ops: controller operations + * struct pwm_chip - abstract a PWM controller + * @dev: device providing the PWMs + * @list: list node for internal use + * @ops: callbacks for this PWM controller + * @base: number of first PWM controlled by this chip + * @npwm: number of PWMs controlled by this chip + * @pwms: array of PWM devices allocated by the framework */ struct pwm_chip { - int pwm_id; - const char *label; - struct pwm_ops *ops; + struct device *dev; + struct list_head list; + const struct pwm_ops *ops; + int base; + unsigned int npwm; + + struct pwm_device *pwms; }; +int pwm_set_chip_data(struct pwm_device *pwm, void *data); +void *pwm_get_chip_data(struct pwm_device *pwm); + int pwmchip_add(struct pwm_chip *chip); int pwmchip_remove(struct pwm_chip *chip); +struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, + unsigned int index, + const char *label); #endif #endif /* __LINUX_PWM_H */ -- cgit v1.2.3 From 62099abf67a20cfb98d4c031fb1925e10a78ee1b Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 26 Mar 2012 09:31:48 +0200 Subject: pwm: Add debugfs interface This commit adds a debugfs interface that can be used to list the current internal state of the PWM devices registered with the PWM framework. Reviewed-by: Mark Brown Reviewed-by: Shawn Guo Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pwm.h | 6 ++++ 2 files changed, 96 insertions(+) (limited to 'include') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index a447be128328..aadc1d797449 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #define MAX_PWMS 1024 @@ -338,3 +340,91 @@ void pwm_disable(struct pwm_device *pwm) pwm->chip->ops->disable(pwm->chip, pwm); } EXPORT_SYMBOL_GPL(pwm_disable); + +#ifdef CONFIG_DEBUG_FS +static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) +{ + unsigned int i; + + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; + + seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label); + + if (test_bit(PWMF_REQUESTED, &pwm->flags)) + seq_printf(s, " requested"); + + if (test_bit(PWMF_ENABLED, &pwm->flags)) + seq_printf(s, " enabled"); + + seq_printf(s, "\n"); + } +} + +static void *pwm_seq_start(struct seq_file *s, loff_t *pos) +{ + mutex_lock(&pwm_lock); + s->private = ""; + + return seq_list_start(&pwm_chips, *pos); +} + +static void *pwm_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + s->private = "\n"; + + return seq_list_next(v, &pwm_chips, pos); +} + +static void pwm_seq_stop(struct seq_file *s, void *v) +{ + mutex_unlock(&pwm_lock); +} + +static int pwm_seq_show(struct seq_file *s, void *v) +{ + struct pwm_chip *chip = list_entry(v, struct pwm_chip, list); + + seq_printf(s, "%s%s/%s, %d PWM device%s\n", (char *)s->private, + chip->dev->bus ? chip->dev->bus->name : "no-bus", + dev_name(chip->dev), chip->npwm, + (chip->npwm != 1) ? "s" : ""); + + if (chip->ops->dbg_show) + chip->ops->dbg_show(chip, s); + else + pwm_dbg_show(chip, s); + + return 0; +} + +static const struct seq_operations pwm_seq_ops = { + .start = pwm_seq_start, + .next = pwm_seq_next, + .stop = pwm_seq_stop, + .show = pwm_seq_show, +}; + +static int pwm_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &pwm_seq_ops); +} + +static const struct file_operations pwm_debugfs_ops = { + .owner = THIS_MODULE, + .open = pwm_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init pwm_debugfs_init(void) +{ + debugfs_create_file("pwm", S_IFREG | S_IRUGO, NULL, NULL, + &pwm_debugfs_ops); + + return 0; +} + +subsys_initcall(pwm_debugfs_init); +#endif /* CONFIG_DEBUG_FS */ diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 57103911f4c7..047cd5351a3b 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -2,6 +2,7 @@ #define __LINUX_PWM_H struct pwm_device; +struct seq_file; /* * pwm_request - request a PWM device @@ -65,6 +66,7 @@ static inline unsigned int pwm_get_period(struct pwm_device *pwm) * @config: configure duty cycles and period length for this PWM * @enable: enable PWM output toggling * @disable: disable PWM output toggling + * @dbg_show: optional routine to show contents in debugfs * @owner: helps prevent removal of modules exporting active PWMs */ struct pwm_ops { @@ -79,6 +81,10 @@ struct pwm_ops { struct pwm_device *pwm); void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm); +#ifdef CONFIG_DEBUG_FS + void (*dbg_show)(struct pwm_chip *chip, + struct seq_file *s); +#endif struct module *owner; }; -- cgit v1.2.3 From 8138d2ddbcca2a100482dac390133f83c5a60f94 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 26 Mar 2012 08:42:48 +0200 Subject: pwm: Add table-based lookup for static mappings In order to get rid of the global namespace for PWM devices, this commit provides an alternative method, similar to that of the regulator or clock frameworks, for registering a static mapping for PWM devices. This works by providing a table with a provider/consumer map in the board setup code. With the new pwm_get() and pwm_put() functions available, usage of pwm_request() and pwm_free() becomes deprecated. Reviewed-by: Shawn Guo Reviewed-by: Mark Brown Signed-off-by: Thierry Reding --- Documentation/pwm.txt | 27 ++++++-- drivers/pwm/core.c | 169 +++++++++++++++++++++++++++++++++++++++++++++----- include/linux/pwm.h | 22 +++++++ 3 files changed, 199 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt index 48f598acdd16..554290ebab94 100644 --- a/Documentation/pwm.txt +++ b/Documentation/pwm.txt @@ -12,14 +12,33 @@ this kind of flexibility the generic PWM API exists. Identifying PWMs ---------------- -Users of the legacy PWM API use unique IDs to refer to PWM devices. One -goal of the new PWM framework is to get rid of this global namespace. +Users of the legacy PWM API use unique IDs to refer to PWM devices. + +Instead of referring to a PWM device via its unique ID, board setup code +should instead register a static mapping that can be used to match PWM +consumers to providers, as given in the following example: + + static struct pwm_lookup board_pwm_lookup[] = { + PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight", NULL), + }; + + static void __init board_init(void) + { + ... + pwm_add_table(board_pwm_lookup, ARRAY_SIZE(board_pwm_lookup)); + ... + } Using PWMs ---------- -A PWM can be requested using pwm_request() and freed after usage with -pwm_free(). After being requested a PWM has to be configured using +Legacy users can request a PWM device using pwm_request() and free it +after usage with pwm_free(). + +New users should use the pwm_get() function and pass to it the consumer +device or a consumer name. pwm_put() is used to free the PWM device. + +After being requested a PWM has to be configured using: int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns); diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index aadc1d797449..a2af599e61ae 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -32,6 +32,8 @@ #define MAX_PWMS 1024 +static DEFINE_MUTEX(pwm_lookup_lock); +static LIST_HEAD(pwm_lookup_list); static DEFINE_MUTEX(pwm_lock); static LIST_HEAD(pwm_chips); static DECLARE_BITMAP(allocated_pwms, MAX_PWMS); @@ -80,6 +82,29 @@ static void free_pwms(struct pwm_chip *chip) chip->pwms = NULL; } +static struct pwm_chip *pwmchip_find_by_name(const char *name) +{ + struct pwm_chip *chip; + + if (!name) + return NULL; + + mutex_lock(&pwm_lock); + + list_for_each_entry(chip, &pwm_chips, list) { + const char *chip_name = dev_name(chip->dev); + + if (chip_name && strcmp(chip_name, name) == 0) { + mutex_unlock(&pwm_lock); + return chip; + } + } + + mutex_unlock(&pwm_lock); + + return NULL; +} + static int pwm_device_request(struct pwm_device *pwm, const char *label) { int err; @@ -218,6 +243,8 @@ EXPORT_SYMBOL_GPL(pwmchip_remove); * pwm_request() - request a PWM device * @pwm_id: global PWM device index * @label: PWM device label + * + * This function is deprecated, use pwm_get() instead. */ struct pwm_device *pwm_request(int pwm, const char *label) { @@ -281,24 +308,12 @@ EXPORT_SYMBOL_GPL(pwm_request_from_chip); /** * pwm_free() - free a PWM device * @pwm: PWM device + * + * This function is deprecated, use pwm_put() instead. */ void pwm_free(struct pwm_device *pwm) { - mutex_lock(&pwm_lock); - - if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) { - pr_warning("PWM device already freed\n"); - goto out; - } - - if (pwm->chip->ops->free) - pwm->chip->ops->free(pwm->chip, pwm); - - pwm->label = NULL; - - module_put(pwm->chip->ops->owner); -out: - mutex_unlock(&pwm_lock); + pwm_put(pwm); } EXPORT_SYMBOL_GPL(pwm_free); @@ -341,6 +356,130 @@ void pwm_disable(struct pwm_device *pwm) } EXPORT_SYMBOL_GPL(pwm_disable); +/** + * pwm_add_table() - register PWM device consumers + * @table: array of consumers to register + * @num: number of consumers in table + */ +void __init pwm_add_table(struct pwm_lookup *table, size_t num) +{ + mutex_lock(&pwm_lookup_lock); + + while (num--) { + list_add_tail(&table->list, &pwm_lookup_list); + table++; + } + + mutex_unlock(&pwm_lookup_lock); +} + +/** + * pwm_get() - look up and request a PWM device + * @dev: device for PWM consumer + * @con_id: consumer name + * + * Look up a PWM chip and a relative index via a table supplied by board setup + * code (see pwm_add_table()). + * + * Once a PWM chip has been found the specified PWM device will be requested + * and is ready to be used. + */ +struct pwm_device *pwm_get(struct device *dev, const char *con_id) +{ + struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER); + const char *dev_id = dev ? dev_name(dev): NULL; + struct pwm_chip *chip = NULL; + unsigned int best = 0; + struct pwm_lookup *p; + unsigned int index; + unsigned int match; + + /* + * We look up the provider in the static table typically provided by + * board setup code. We first try to lookup the consumer device by + * name. If the consumer device was passed in as NULL or if no match + * was found, we try to find the consumer by directly looking it up + * by name. + * + * If a match is found, the provider PWM chip is looked up by name + * and a PWM device is requested using the PWM device per-chip index. + * + * The lookup algorithm was shamelessly taken from the clock + * framework: + * + * We do slightly fuzzy matching here: + * An entry with a NULL ID is assumed to be a wildcard. + * If an entry has a device ID, it must match + * If an entry has a connection ID, it must match + * Then we take the most specific entry - with the following order + * of precedence: dev+con > dev only > con only. + */ + mutex_lock(&pwm_lookup_lock); + + list_for_each_entry(p, &pwm_lookup_list, list) { + match = 0; + + if (p->dev_id) { + if (!dev_id || strcmp(p->dev_id, dev_id)) + continue; + + match += 2; + } + + if (p->con_id) { + if (!con_id || strcmp(p->con_id, con_id)) + continue; + + match += 1; + } + + if (match > best) { + chip = pwmchip_find_by_name(p->provider); + index = p->index; + + if (match != 3) + best = match; + else + break; + } + } + + if (chip) + pwm = pwm_request_from_chip(chip, index, con_id ?: dev_id); + + mutex_unlock(&pwm_lookup_lock); + + return pwm; +} +EXPORT_SYMBOL_GPL(pwm_get); + +/** + * pwm_put() - release a PWM device + * @pwm: PWM device + */ +void pwm_put(struct pwm_device *pwm) +{ + if (!pwm) + return; + + mutex_lock(&pwm_lock); + + if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) { + pr_warning("PWM device already freed\n"); + goto out; + } + + if (pwm->chip->ops->free) + pwm->chip->ops->free(pwm->chip, pwm); + + pwm->label = NULL; + + module_put(pwm->chip->ops->owner); +out: + mutex_unlock(&pwm_lock); +} +EXPORT_SYMBOL_GPL(pwm_put); + #ifdef CONFIG_DEBUG_FS static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) { diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 047cd5351a3b..2947a4fea6ad 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -115,6 +115,28 @@ int pwmchip_remove(struct pwm_chip *chip); struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, unsigned int index, const char *label); + +struct pwm_device *pwm_get(struct device *dev, const char *consumer); +void pwm_put(struct pwm_device *pwm); + +struct pwm_lookup { + struct list_head list; + const char *provider; + unsigned int index; + const char *dev_id; + const char *con_id; +}; + +#define PWM_LOOKUP(_provider, _index, _dev_id, _con_id) \ + { \ + .provider = _provider, \ + .index = _index, \ + .dev_id = _dev_id, \ + .con_id = _con_id, \ + } + +void pwm_add_table(struct pwm_lookup *table, size_t num); + #endif #endif /* __LINUX_PWM_H */ -- cgit v1.2.3 From bd3d5500f0c41a30149cb184362716096a17bc75 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 13 Apr 2012 16:18:34 +0200 Subject: dt: Add empty of_property_match_string() function This commit adds an empty of_property_match_string() function for !CONFIG_OF builds. Acked-by: Rob Herring Signed-off-by: Thierry Reding --- include/linux/of.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/of.h b/include/linux/of.h index 2ec1083af7ff..597e571110a2 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -377,6 +377,13 @@ static inline int of_property_read_u64(const struct device_node *np, return -ENOSYS; } +static inline int of_property_match_string(struct device_node *np, + const char *propname, + const char *string) +{ + return -ENOSYS; +} + static inline struct device_node *of_parse_phandle(struct device_node *np, const char *phandle_name, int index) -- cgit v1.2.3 From e05e5070f0ec2557d2d2ff3655ba03f29e297151 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 13 Apr 2012 16:19:21 +0200 Subject: dt: Add empty of_parse_phandle_with_args() function This commit adds an empty of_parse_phandle_with_args() function for !CONFIG_OF builds. Acked-by: Rob Herring Signed-off-by: Thierry Reding --- include/linux/of.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/linux/of.h b/include/linux/of.h index 597e571110a2..d5dd5c06efd3 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -391,6 +391,15 @@ static inline struct device_node *of_parse_phandle(struct device_node *np, return NULL; } +static inline int of_parse_phandle_with_args(struct device_node *np, + const char *list_name, + const char *cells_name, + int index, + struct of_phandle_args *out_args) +{ + return -ENOSYS; +} + static inline int of_alias_get_id(struct device_node *np, const char *stem) { return -ENOSYS; -- cgit v1.2.3 From 7299ab70e68e20e70cb45fe4ab4b6029fe964acd Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 14 Dec 2011 11:10:32 +0100 Subject: pwm: Add device tree support This patch adds helpers to support device tree bindings for the generic PWM API. Device tree binding documentation for PWM controllers is also provided. Acked-by: Arnd Bergmann Reviewed-by: Shawn Guo Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/pwm/pwm.txt | 57 ++++++++++ drivers/pwm/core.c | 148 +++++++++++++++++++++++++- include/linux/pwm.h | 6 ++ 3 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/pwm/pwm.txt (limited to 'include') diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt new file mode 100644 index 000000000000..73ec962bfe8c --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm.txt @@ -0,0 +1,57 @@ +Specifying PWM information for devices +====================================== + +1) PWM user nodes +----------------- + +PWM users should specify a list of PWM devices that they want to use +with a property containing a 'pwm-list': + + pwm-list ::= [pwm-list] + single-pwm ::= + pwm-phandle : phandle to PWM controller node + pwm-specifier : array of #pwm-cells specifying the given PWM + (controller specific) + +PWM properties should be named "pwms". The exact meaning of each pwms +property must be documented in the device tree binding for each device. +An optional property "pwm-names" may contain a list of strings to label +each of the PWM devices listed in the "pwms" property. If no "pwm-names" +property is given, the name of the user node will be used as fallback. + +Drivers for devices that use more than a single PWM device can use the +"pwm-names" property to map the name of the PWM device requested by the +pwm_get() call to an index into the list given by the "pwms" property. + +The following example could be used to describe a PWM-based backlight +device: + + pwm: pwm { + #pwm-cells = <2>; + }; + + [...] + + bl: backlight { + pwms = <&pwm 0 5000000>; + pwm-names = "backlight"; + }; + +pwm-specifier typically encodes the chip-relative PWM number and the PWM +period in nanoseconds. Note that in the example above, specifying the +"pwm-names" is redundant because the name "backlight" would be used as +fallback anyway. + +2) PWM controller nodes +----------------------- + +PWM controller nodes must specify the number of cells used for the +specifier using the '#pwm-cells' property. + +An example PWM controller might look like this: + + pwm: pwm@7000a000 { + compatible = "nvidia,tegra20-pwm"; + reg = <0x7000a000 0x100>; + #pwm-cells = <2>; + }; diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index a2af599e61ae..dbab53005da8 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -129,6 +129,45 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) return 0; } +static struct pwm_device *of_pwm_simple_xlate(struct pwm_chip *pc, + const struct of_phandle_args *args) +{ + struct pwm_device *pwm; + + if (pc->of_pwm_n_cells < 2) + return ERR_PTR(-EINVAL); + + if (args->args[0] >= pc->npwm) + return ERR_PTR(-EINVAL); + + pwm = pwm_request_from_chip(pc, args->args[0], NULL); + if (IS_ERR(pwm)) + return pwm; + + pwm_set_period(pwm, args->args[1]); + + return pwm; +} + +void of_pwmchip_add(struct pwm_chip *chip) +{ + if (!chip->dev || !chip->dev->of_node) + return; + + if (!chip->of_xlate) { + chip->of_xlate = of_pwm_simple_xlate; + chip->of_pwm_n_cells = 2; + } + + of_node_get(chip->dev->of_node); +} + +void of_pwmchip_remove(struct pwm_chip *chip) +{ + if (chip->dev && chip->dev->of_node) + of_node_put(chip->dev->of_node); +} + /** * pwm_set_chip_data() - set private chip data for a PWM * @pwm: PWM device @@ -201,6 +240,9 @@ int pwmchip_add(struct pwm_chip *chip) ret = 0; + if (IS_ENABLED(CONFIG_OF)) + of_pwmchip_add(chip); + out: mutex_unlock(&pwm_lock); return ret; @@ -231,6 +273,10 @@ int pwmchip_remove(struct pwm_chip *chip) } list_del_init(&chip->list); + + if (IS_ENABLED(CONFIG_OF)) + of_pwmchip_remove(chip); + free_pwms(chip); out: @@ -356,6 +402,99 @@ void pwm_disable(struct pwm_device *pwm) } EXPORT_SYMBOL_GPL(pwm_disable); +static struct pwm_chip *of_node_to_pwmchip(struct device_node *np) +{ + struct pwm_chip *chip; + + mutex_lock(&pwm_lock); + + list_for_each_entry(chip, &pwm_chips, list) + if (chip->dev && chip->dev->of_node == np) { + mutex_unlock(&pwm_lock); + return chip; + } + + mutex_unlock(&pwm_lock); + + return ERR_PTR(-EPROBE_DEFER); +} + +/** + * of_pwm_request() - request a PWM via the PWM framework + * @np: device node to get the PWM from + * @con_id: consumer name + * + * Returns the PWM device parsed from the phandle and index specified in the + * "pwms" property of a device tree node or a negative error-code on failure. + * Values parsed from the device tree are stored in the returned PWM device + * object. + * + * If con_id is NULL, the first PWM device listed in the "pwms" property will + * be requested. Otherwise the "pwm-names" property is used to do a reverse + * lookup of the PWM index. This also means that the "pwm-names" property + * becomes mandatory for devices that look up the PWM device via the con_id + * parameter. + */ +static struct pwm_device *of_pwm_request(struct device_node *np, + const char *con_id) +{ + struct pwm_device *pwm = NULL; + struct of_phandle_args args; + struct pwm_chip *pc; + int index = 0; + int err; + + if (con_id) { + index = of_property_match_string(np, "pwm-names", con_id); + if (index < 0) + return ERR_PTR(index); + } + + err = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", index, + &args); + if (err) { + pr_debug("%s(): can't parse \"pwms\" property\n", __func__); + return ERR_PTR(err); + } + + pc = of_node_to_pwmchip(args.np); + if (IS_ERR(pc)) { + pr_debug("%s(): PWM chip not found\n", __func__); + pwm = ERR_CAST(pc); + goto put; + } + + if (args.args_count != pc->of_pwm_n_cells) { + pr_debug("%s: wrong #pwm-cells for %s\n", np->full_name, + args.np->full_name); + pwm = ERR_PTR(-EINVAL); + goto put; + } + + pwm = pc->of_xlate(pc, &args); + if (IS_ERR(pwm)) + goto put; + + /* + * If a consumer name was not given, try to look it up from the + * "pwm-names" property if it exists. Otherwise use the name of + * the user device node. + */ + if (!con_id) { + err = of_property_read_string_index(np, "pwm-names", index, + &con_id); + if (err < 0) + con_id = np->name; + } + + pwm->label = con_id; + +put: + of_node_put(args.np); + + return pwm; +} + /** * pwm_add_table() - register PWM device consumers * @table: array of consumers to register @@ -378,8 +517,9 @@ void __init pwm_add_table(struct pwm_lookup *table, size_t num) * @dev: device for PWM consumer * @con_id: consumer name * - * Look up a PWM chip and a relative index via a table supplied by board setup - * code (see pwm_add_table()). + * Lookup is first attempted using DT. If the device was not instantiated from + * a device tree, a PWM chip and a relative index is looked up via a table + * supplied by board setup code (see pwm_add_table()). * * Once a PWM chip has been found the specified PWM device will be requested * and is ready to be used. @@ -394,6 +534,10 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) unsigned int index; unsigned int match; + /* look up via DT first */ + if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) + return of_pwm_request(dev->of_node, con_id); + /* * We look up the provider in the static table typically provided by * board setup code. We first try to lookup the consumer device by diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 2947a4fea6ad..21d076c5089e 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -1,6 +1,8 @@ #ifndef __LINUX_PWM_H #define __LINUX_PWM_H +#include + struct pwm_device; struct seq_file; @@ -105,6 +107,10 @@ struct pwm_chip { unsigned int npwm; struct pwm_device *pwms; + + struct pwm_device * (*of_xlate)(struct pwm_chip *pc, + const struct of_phandle_args *args); + unsigned int of_pwm_n_cells; }; int pwm_set_chip_data(struct pwm_device *pwm, void *data); -- cgit v1.2.3 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') 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 From efd68e7254503f3207805f674a1ea1d743f5dfe2 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sun, 3 Jun 2012 22:04:33 -0700 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 --- 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') 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 0915b1ad66ce..aab5fbc924e6 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 d9bfd49b1935..9282d4cc1091 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 From 5ca4db61e859526b2dbee3bcea3626d3de49a0b2 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Sun, 3 Jun 2012 22:04:34 -0700 Subject: irqdomain: Simple NUMA awareness. While common irqdesc allocation is node aware, the irqdomain code is not. Presently we observe a number of regressions/inconsistencies on NUMA-capable platforms: - Platforms using irqdomains with legacy mappings, where the irq_descs are allocated node-local and the irqdomain data structure is not. - Drivers implementing irqdomains will lose node locality regardless of the underlying struct device's node id. This plugs in NUMA node id proliferation across the various allocation callsites by way of_node_to_nid() node lookup. While of_node_to_nid() does the right thing for OF-capable platforms it doesn't presently handle the non-DT case. This is trivially dealt with by simply wraping in to numa_node_id() unconditionally. Signed-off-by: Paul Mundt Cc: Benjamin Herrenschmidt Cc: Thomas Gleixner Cc: Rob Herring Signed-off-by: Grant Likely --- include/linux/of.h | 15 ++++++++++----- kernel/irq/irqdomain.c | 13 ++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/of.h b/include/linux/of.h index 1012377cae92..76930ee78db5 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -158,11 +159,6 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size) #define OF_BAD_ADDR ((u64)-1) -#ifndef of_node_to_nid -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 : ""; @@ -412,6 +408,15 @@ static inline int of_machine_is_compatible(const char *compat) while (0) #endif /* CONFIG_OF */ +#ifndef of_node_to_nid +static inline int of_node_to_nid(struct device_node *np) +{ + return numa_node_id(); +} + +#define of_node_to_nid of_node_to_nid +#endif + /** * of_property_read_bool - Findfrom a property * @np: device node from which the property value is to be read. diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 38c5eb839c92..79ae0ebb90ba 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -45,7 +46,8 @@ static struct irq_domain *irq_domain_alloc(struct device_node *of_node, { struct irq_domain *domain; - domain = kzalloc(sizeof(*domain), GFP_KERNEL); + domain = kzalloc_node(sizeof(*domain), GFP_KERNEL, + of_node_to_nid(of_node)); if (WARN_ON(!domain)) return NULL; @@ -229,7 +231,8 @@ struct irq_domain *irq_domain_add_linear(struct device_node *of_node, struct irq_domain *domain; unsigned int *revmap; - revmap = kzalloc(sizeof(*revmap) * size, GFP_KERNEL); + revmap = kzalloc_node(sizeof(*revmap) * size, GFP_KERNEL, + of_node_to_nid(of_node)); if (WARN_ON(!revmap)) return NULL; @@ -367,7 +370,7 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain) BUG_ON(domain == NULL); WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_NOMAP); - virq = irq_alloc_desc_from(1, 0); + virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node)); if (!virq) { pr_debug("create_direct virq allocation failed\n"); return 0; @@ -433,9 +436,9 @@ unsigned int irq_create_mapping(struct irq_domain *domain, hint = hwirq % nr_irqs; if (hint == 0) hint++; - virq = irq_alloc_desc_from(hint, 0); + virq = irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node)); if (virq <= 0) - virq = irq_alloc_desc_from(1, 0); + virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node)); if (virq <= 0) { pr_debug("-> virq allocation failed\n"); return 0; -- cgit v1.2.3 From 33e0c249801c6913b2bd102683ab65ab61e12952 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Fri, 15 Jun 2012 19:25:25 +0200 Subject: iio: iio/machine.h typo Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/machine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/machine.h b/include/linux/iio/machine.h index 0b1f19bfdc44..400a453ff67b 100644 --- a/include/linux/iio/machine.h +++ b/include/linux/iio/machine.h @@ -14,7 +14,7 @@ * This is matched against the datasheet_name element * of struct iio_chan_spec. * @consumer_dev_name: Name to uniquely identify the consumer device. - * @consumer_channel: Unique name used to idenitify the channel on the + * @consumer_channel: Unique name used to identify the channel on the * consumer side. */ struct iio_map { -- cgit v1.2.3 From fb1c4bcd721fcc30d6e43f60a244483dc0d07056 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Fri, 15 Jun 2012 19:25:28 +0200 Subject: iio: iio/events.h typos Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/events.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/iio/events.h b/include/linux/iio/events.h index b5acbf93c5da..13ce220c7003 100644 --- a/include/linux/iio/events.h +++ b/include/linux/iio/events.h @@ -46,7 +46,7 @@ enum iio_event_direction { * @diff: Whether the event is for an differential channel or not. * @modifier: Modifier for the channel. Should be one of enum iio_modifier. * @direction: Direction of the event. One of enum iio_event_direction. - * @type: Type of the event. Should be one enum iio_event_type. + * @type: Type of the event. Should be one of enum iio_event_type. * @chan: Channel number for non-differential channels. * @chan1: First channel number for differential channels. * @chan2: Second channel number for differential channels. @@ -69,7 +69,7 @@ enum iio_event_direction { * @chan_type: Type of the channel. Should be one of enum iio_chan_type. * @number: Channel number. * @modifier: Modifier for the channel. Should be one of enum iio_modifier. - * @type: Type of the event. Should be one enum iio_event_type. + * @type: Type of the event. Should be one of enum iio_event_type. * @direction: Direction of the event. One of enum iio_event_direction. */ @@ -81,7 +81,7 @@ enum iio_event_direction { * IIO_UNMOD_EVENT_CODE() - create event identifier for unmodified channels * @chan_type: Type of the channel. Should be one of enum iio_chan_type. * @number: Channel number. - * @type: Type of the event. Should be one enum iio_event_type. + * @type: Type of the event. Should be one of enum iio_event_type. * @direction: Direction of the event. One of enum iio_event_direction. */ -- cgit v1.2.3 From 81aded24675ebda5de8a68843250ad15584ac38a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 15 Jun 2012 14:54:11 -0700 Subject: ipv6: Handle PMTU in ICMP error handlers. One tricky issue on the ipv6 side vs. ipv4 is that the ICMP callouts to handle the error pass the 32-bit info cookie in network byte order whereas ipv4 passes it around in host byte order. Like the ipv4 side, we have two helper functions. One for when we have a socket context and one for when we do not. ip6ip6 tunnels are not handled here, because they handle PMTU events by essentially relaying another ICMP packet-too-big message back to the original sender. This patch allows us to get rid of rt6_do_pmtu_disc(). It handles all kinds of situations that simply cannot happen when we do the PMTU update directly using a fully resolved route. In fact, the "plen == 128" check in ip6_rt_update_pmtu() can very likely be removed or changed into a BUG_ON() check. We should never have a prefixed ipv6 route when we get there. Another piece of strange history here is that TCP and DCCP, unlike in ipv4, never invoke the update_pmtu() method from their ICMP error handlers. This is incredibly astonishing since this is the context where we have the most accurate context in which to make a PMTU update, namely we have a fully connected socket and associated cached socket route. Signed-off-by: David S. Miller --- include/net/ip6_route.h | 8 +-- net/dccp/ipv6.c | 2 + net/ipv6/ah6.c | 3 +- net/ipv6/esp6.c | 2 + net/ipv6/icmp.c | 6 +- net/ipv6/ipcomp6.c | 2 + net/ipv6/raw.c | 5 +- net/ipv6/route.c | 143 +++++++++++------------------------------------- net/ipv6/tcp_ipv6.c | 2 + net/ipv6/udp.c | 3 + 10 files changed, 54 insertions(+), 122 deletions(-) (limited to 'include') diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index a2cda240ca95..58cb3fc34879 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -140,10 +140,10 @@ extern void rt6_redirect(const struct in6_addr *dest, u8 *lladdr, int on_link); -extern void rt6_pmtu_discovery(const struct in6_addr *daddr, - const struct in6_addr *saddr, - struct net_device *dev, - u32 pmtu); +extern void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, + int oif, u32 mark); +extern void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, + __be32 mtu); struct netlink_callback; diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index fa9512d86f3b..9991be083ad0 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -165,6 +165,8 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, } else dst_hold(dst); + dst->ops->update_pmtu(dst, ntohl(info)); + if (inet_csk(sk)->icsk_pmtu_cookie > dst_mtu(dst)) { dccp_sync_mss(sk, dst_mtu(dst)); } /* else let the usual retransmit timer handle it */ diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index f1a4a2c28ed3..49d4d26bda88 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -621,7 +622,7 @@ static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, NETDEBUG(KERN_DEBUG "pmtu discovery on SA AH/%08x/%pI6\n", ntohl(ah->spi), &iph->daddr); - + ip6_update_pmtu(skb, net, info, 0, 0); xfrm_state_put(x); } diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index db1521fcda5b..89a615ba84f8 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -442,6 +443,7 @@ static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, return; pr_debug("pmtu discovery on SA ESP/%08x/%pI6\n", ntohl(esph->spi), &iph->daddr); + ip6_update_pmtu(skb, net, info, 0, 0); xfrm_state_put(x); } diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index ed89bba745a1..5247d5c211f9 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -649,7 +649,6 @@ static int icmpv6_rcv(struct sk_buff *skb) struct net_device *dev = skb->dev; struct inet6_dev *idev = __in6_dev_get(dev); const struct in6_addr *saddr, *daddr; - const struct ipv6hdr *orig_hdr; struct icmp6hdr *hdr; u8 type; @@ -661,7 +660,7 @@ static int icmpv6_rcv(struct sk_buff *skb) XFRM_STATE_ICMP)) goto drop_no_count; - if (!pskb_may_pull(skb, sizeof(*hdr) + sizeof(*orig_hdr))) + if (!pskb_may_pull(skb, sizeof(*hdr) + sizeof(struct ipv6hdr))) goto drop_no_count; nh = skb_network_offset(skb); @@ -722,9 +721,6 @@ static int icmpv6_rcv(struct sk_buff *skb) if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) goto discard_it; hdr = icmp6_hdr(skb); - orig_hdr = (struct ipv6hdr *) (hdr + 1); - rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev, - ntohl(hdr->icmp6_mtu)); /* * Drop through to notify diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index 5cb75bfe45b1..92832385a8ef 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,7 @@ static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, pr_debug("pmtu discovery on SA IPCOMP/%08x/%pI6\n", spi, &iph->daddr); + ip6_update_pmtu(skb, net, info, 0, 0); xfrm_state_put(x); } diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 93d69836fded..43b0042f15f4 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -328,9 +328,10 @@ static void rawv6_err(struct sock *sk, struct sk_buff *skb, return; harderr = icmpv6_err_convert(type, code, &err); - if (type == ICMPV6_PKT_TOOBIG) + if (type == ICMPV6_PKT_TOOBIG) { + ip6_sk_update_pmtu(skb, sk, info); harderr = (np->pmtudisc == IPV6_PMTUDISC_DO); - + } if (np->recverr) { u8 *payload = skb->data; if (!inet->hdrincl) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 58a3ec23da2f..0d41f68daff2 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1049,7 +1049,10 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu) { struct rt6_info *rt6 = (struct rt6_info*)dst; + dst_confirm(dst); if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) { + struct net *net = dev_net(dst->dev); + rt6->rt6i_flags |= RTF_MODIFIED; if (mtu < IPV6_MIN_MTU) { u32 features = dst_metric(dst, RTAX_FEATURES); @@ -1058,9 +1061,39 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu) dst_metric_set(dst, RTAX_FEATURES, features); } dst_metric_set(dst, RTAX_MTU, mtu); + rt6_update_expires(rt6, net->ipv6.sysctl.ip6_rt_mtu_expires); } } +void ip6_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu, + int oif, __be32 mark) +{ + const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; + struct dst_entry *dst; + struct flowi6 fl6; + + memset(&fl6, 0, sizeof(fl6)); + fl6.flowi6_oif = oif; + fl6.flowi6_mark = mark; + fl6.flowi6_flags = FLOWI_FLAG_PRECOW_METRICS; + fl6.daddr = iph->daddr; + fl6.saddr = iph->saddr; + fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK; + + dst = ip6_route_output(net, NULL, &fl6); + if (!dst->error) + ip6_rt_update_pmtu(dst, ntohl(mtu)); + dst_release(dst); +} +EXPORT_SYMBOL_GPL(ip6_update_pmtu); + +void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) +{ + ip6_update_pmtu(skb, sock_net(sk), mtu, + sk->sk_bound_dev_if, sk->sk_mark); +} +EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); + static unsigned int ip6_default_advmss(const struct dst_entry *dst) { struct net_device *dev = dst->dev; @@ -1703,116 +1736,6 @@ out: dst_release(&rt->dst); } -/* - * Handle ICMP "packet too big" messages - * i.e. Path MTU discovery - */ - -static void rt6_do_pmtu_disc(const struct in6_addr *daddr, const struct in6_addr *saddr, - struct net *net, u32 pmtu, int ifindex) -{ - struct rt6_info *rt, *nrt; - int allfrag = 0; -again: - rt = rt6_lookup(net, daddr, saddr, ifindex, 0); - if (!rt) - return; - - if (rt6_check_expired(rt)) { - ip6_del_rt(rt); - goto again; - } - - if (pmtu >= dst_mtu(&rt->dst)) - goto out; - - if (pmtu < IPV6_MIN_MTU) { - /* - * According to RFC2460, PMTU is set to the IPv6 Minimum Link - * MTU (1280) and a fragment header should always be included - * after a node receiving Too Big message reporting PMTU is - * less than the IPv6 Minimum Link MTU. - */ - pmtu = IPV6_MIN_MTU; - allfrag = 1; - } - - /* New mtu received -> path was valid. - They are sent only in response to data packets, - so that this nexthop apparently is reachable. --ANK - */ - dst_confirm(&rt->dst); - - /* Host route. If it is static, it would be better - not to override it, but add new one, so that - when cache entry will expire old pmtu - would return automatically. - */ - if (rt->rt6i_flags & RTF_CACHE) { - dst_metric_set(&rt->dst, RTAX_MTU, pmtu); - if (allfrag) { - u32 features = dst_metric(&rt->dst, RTAX_FEATURES); - features |= RTAX_FEATURE_ALLFRAG; - dst_metric_set(&rt->dst, RTAX_FEATURES, features); - } - rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); - rt->rt6i_flags |= RTF_MODIFIED; - goto out; - } - - /* Network route. - Two cases are possible: - 1. It is connected route. Action: COW - 2. It is gatewayed route or NONEXTHOP route. Action: clone it. - */ - if (!dst_get_neighbour_noref_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP)) - nrt = rt6_alloc_cow(rt, daddr, saddr); - else - nrt = rt6_alloc_clone(rt, daddr); - - if (nrt) { - dst_metric_set(&nrt->dst, RTAX_MTU, pmtu); - if (allfrag) { - u32 features = dst_metric(&nrt->dst, RTAX_FEATURES); - features |= RTAX_FEATURE_ALLFRAG; - dst_metric_set(&nrt->dst, RTAX_FEATURES, features); - } - - /* According to RFC 1981, detecting PMTU increase shouldn't be - * happened within 5 mins, the recommended timer is 10 mins. - * Here this route expiration time is set to ip6_rt_mtu_expires - * which is 10 mins. After 10 mins the decreased pmtu is expired - * and detecting PMTU increase will be automatically happened. - */ - rt6_update_expires(nrt, net->ipv6.sysctl.ip6_rt_mtu_expires); - nrt->rt6i_flags |= RTF_DYNAMIC; - ip6_ins_rt(nrt); - } -out: - dst_release(&rt->dst); -} - -void rt6_pmtu_discovery(const struct in6_addr *daddr, const struct in6_addr *saddr, - struct net_device *dev, u32 pmtu) -{ - struct net *net = dev_net(dev); - - /* - * RFC 1981 states that a node "MUST reduce the size of the packets it - * is sending along the path" that caused the Packet Too Big message. - * Since it's not possible in the general case to determine which - * interface was used to send the original packet, we update the MTU - * on the interface that will be used to send future packets. We also - * update the MTU on the interface that received the Packet Too Big in - * case the original packet was forced out that interface with - * SO_BINDTODEVICE or similar. This is the next best thing to the - * correct behaviour, which would be to update the MTU on all - * interfaces. - */ - rt6_do_pmtu_disc(daddr, saddr, net, pmtu, 0); - rt6_do_pmtu_disc(daddr, saddr, net, pmtu, dev->ifindex); -} - /* * Misc support functions */ diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index f91b0bfd12d5..26a88623940b 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -415,6 +415,8 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, } else dst_hold(dst); + dst->ops->update_pmtu(dst, ntohl(info)); + if (inet_csk(sk)->icsk_pmtu_cookie > dst_mtu(dst)) { tcp_sync_mss(sk, dst_mtu(dst)); tcp_simple_retransmit(sk); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index f05099fc5901..051ad481973f 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -479,6 +479,9 @@ void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (sk == NULL) return; + if (type == ICMPV6_PKT_TOOBIG) + ip6_sk_update_pmtu(skb, sk, info); + np = inet6_sk(sk); if (!icmpv6_err_convert(type, code, &err) && !np->recverr) -- cgit v1.2.3 From 3a8fc53a45c444400259e2e285ba414a87061e3d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 15 Jan 2012 16:34:08 +0100 Subject: netfilter: nf_ct_helper: allocate 16 bytes for the helper and policy names This patch modifies the struct nf_conntrack_helper to allocate the room for the helper name. The maximum length is 16 bytes (this was already introduced in 2.6.24). For the maximum length for expectation policy names, I have also selected 16 bytes. This patch is required by the follow-up patch to support user-space connection tracking helpers. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_expect.h | 4 +++- include/net/netfilter/nf_conntrack_helper.h | 2 +- net/netfilter/nf_conntrack_ftp.c | 8 ++------ net/netfilter/nf_conntrack_irc.c | 8 ++------ net/netfilter/nf_conntrack_sane.c | 8 ++------ net/netfilter/nf_conntrack_sip.c | 7 ++----- net/netfilter/nf_conntrack_tftp.c | 8 ++------ 7 files changed, 14 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index 4619caadd9d1..983f00263243 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h @@ -59,10 +59,12 @@ static inline struct net *nf_ct_exp_net(struct nf_conntrack_expect *exp) return nf_ct_net(exp->master); } +#define NF_CT_EXP_POLICY_NAME_LEN 16 + struct nf_conntrack_expect_policy { unsigned int max_expected; unsigned int timeout; - const char *name; + char name[NF_CT_EXP_POLICY_NAME_LEN]; }; #define NF_CT_EXPECT_CLASS_DEFAULT 0 diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 1d1889409b9e..5f5a4d9d4df5 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -19,7 +19,7 @@ struct module; struct nf_conntrack_helper { struct hlist_node hnode; /* Internal use. */ - const char *name; /* name of the module */ + char name[NF_CT_HELPER_NAME_LEN]; /* name of the module */ struct module *me; /* pointer to self */ const struct nf_conntrack_expect_policy *expect_policy; diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index 8c5c95c6d34f..44e47c9e14fb 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -512,7 +512,6 @@ out_update_nl: } static struct nf_conntrack_helper ftp[MAX_PORTS][2] __read_mostly; -static char ftp_names[MAX_PORTS][2][sizeof("ftp-65535")] __read_mostly; static const struct nf_conntrack_expect_policy ftp_exp_policy = { .max_expected = 1, @@ -541,7 +540,6 @@ static void nf_conntrack_ftp_fini(void) static int __init nf_conntrack_ftp_init(void) { int i, j = -1, ret = 0; - char *tmpname; ftp_buffer = kmalloc(65536, GFP_KERNEL); if (!ftp_buffer) @@ -561,12 +559,10 @@ static int __init nf_conntrack_ftp_init(void) ftp[i][j].expect_policy = &ftp_exp_policy; ftp[i][j].me = THIS_MODULE; ftp[i][j].help = help; - tmpname = &ftp_names[i][j][0]; if (ports[i] == FTP_PORT) - sprintf(tmpname, "ftp"); + sprintf(ftp[i][j].name, "ftp"); else - sprintf(tmpname, "ftp-%d", ports[i]); - ftp[i][j].name = tmpname; + sprintf(ftp[i][j].name, "ftp-%d", ports[i]); pr_debug("nf_ct_ftp: registering helper for pf: %d " "port: %d\n", diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index 81366c118271..009c52cfd1ec 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -221,7 +221,6 @@ static int help(struct sk_buff *skb, unsigned int protoff, } static struct nf_conntrack_helper irc[MAX_PORTS] __read_mostly; -static char irc_names[MAX_PORTS][sizeof("irc-65535")] __read_mostly; static struct nf_conntrack_expect_policy irc_exp_policy; static void nf_conntrack_irc_fini(void); @@ -229,7 +228,6 @@ static void nf_conntrack_irc_fini(void); static int __init nf_conntrack_irc_init(void) { int i, ret; - char *tmpname; if (max_dcc_channels < 1) { printk(KERN_ERR "nf_ct_irc: max_dcc_channels must not be zero\n"); @@ -255,12 +253,10 @@ static int __init nf_conntrack_irc_init(void) irc[i].me = THIS_MODULE; irc[i].help = help; - tmpname = &irc_names[i][0]; if (ports[i] == IRC_PORT) - sprintf(tmpname, "irc"); + sprintf(irc[i].name, "irc"); else - sprintf(tmpname, "irc-%u", i); - irc[i].name = tmpname; + sprintf(irc[i].name, "irc-%u", i); ret = nf_conntrack_helper_register(&irc[i]); if (ret) { diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c index 8501823b3f9b..ec3fc18c4ef6 100644 --- a/net/netfilter/nf_conntrack_sane.c +++ b/net/netfilter/nf_conntrack_sane.c @@ -163,7 +163,6 @@ out: } static struct nf_conntrack_helper sane[MAX_PORTS][2] __read_mostly; -static char sane_names[MAX_PORTS][2][sizeof("sane-65535")] __read_mostly; static const struct nf_conntrack_expect_policy sane_exp_policy = { .max_expected = 1, @@ -190,7 +189,6 @@ static void nf_conntrack_sane_fini(void) static int __init nf_conntrack_sane_init(void) { int i, j = -1, ret = 0; - char *tmpname; sane_buffer = kmalloc(65536, GFP_KERNEL); if (!sane_buffer) @@ -210,12 +208,10 @@ static int __init nf_conntrack_sane_init(void) sane[i][j].expect_policy = &sane_exp_policy; sane[i][j].me = THIS_MODULE; sane[i][j].help = help; - tmpname = &sane_names[i][j][0]; if (ports[i] == SANE_PORT) - sprintf(tmpname, "sane"); + sprintf(sane[i][j].name, "sane"); else - sprintf(tmpname, "sane-%d", ports[i]); - sane[i][j].name = tmpname; + sprintf(sane[i][j].name, "sane-%d", ports[i]); pr_debug("nf_ct_sane: registering helper for pf: %d " "port: %d\n", diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 93faf6a3a637..dfd3ff382243 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1556,7 +1556,6 @@ static void nf_conntrack_sip_fini(void) static int __init nf_conntrack_sip_init(void) { int i, j, ret; - char *tmpname; if (ports_c == 0) ports[ports_c++] = SIP_PORT; @@ -1584,12 +1583,10 @@ static int __init nf_conntrack_sip_init(void) sip[i][j].expect_class_max = SIP_EXPECT_MAX; sip[i][j].me = THIS_MODULE; - tmpname = &sip_names[i][j][0]; if (ports[i] == SIP_PORT) - sprintf(tmpname, "sip"); + sprintf(sip_names[i][j], "sip"); else - sprintf(tmpname, "sip-%u", i); - sip[i][j].name = tmpname; + sprintf(sip_names[i][j], "sip-%u", i); pr_debug("port #%u: %u\n", i, ports[i]); diff --git a/net/netfilter/nf_conntrack_tftp.c b/net/netfilter/nf_conntrack_tftp.c index 75466fd72f4f..81fc61c05263 100644 --- a/net/netfilter/nf_conntrack_tftp.c +++ b/net/netfilter/nf_conntrack_tftp.c @@ -92,7 +92,6 @@ static int tftp_help(struct sk_buff *skb, } static struct nf_conntrack_helper tftp[MAX_PORTS][2] __read_mostly; -static char tftp_names[MAX_PORTS][2][sizeof("tftp-65535")] __read_mostly; static const struct nf_conntrack_expect_policy tftp_exp_policy = { .max_expected = 1, @@ -112,7 +111,6 @@ static void nf_conntrack_tftp_fini(void) static int __init nf_conntrack_tftp_init(void) { int i, j, ret; - char *tmpname; if (ports_c == 0) ports[ports_c++] = TFTP_PORT; @@ -129,12 +127,10 @@ static int __init nf_conntrack_tftp_init(void) tftp[i][j].me = THIS_MODULE; tftp[i][j].help = tftp_help; - tmpname = &tftp_names[i][j][0]; if (ports[i] == TFTP_PORT) - sprintf(tmpname, "tftp"); + sprintf(tftp[i][j].name, "tftp"); else - sprintf(tmpname, "tftp-%u", i); - tftp[i][j].name = tmpname; + sprintf(tftp[i][j].name, "tftp-%u", i); ret = nf_conntrack_helper_register(&tftp[i][j]); if (ret) { -- cgit v1.2.3 From 3cf4c7e381d9a98a44fd86207b950bd8fef55d20 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 1 Feb 2012 16:18:31 +0100 Subject: netfilter: nf_ct_ext: support variable length extensions We can now define conntrack extensions of variable size. This patch is useful to get rid of these unions: union nf_conntrack_help union nf_conntrack_proto union nf_conntrack_nat_help Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_extend.h | 9 ++++++--- net/netfilter/nf_conntrack_extend.c | 16 +++++++++------- 2 files changed, 15 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 96755c3798a5..8b4d1fc29096 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -80,10 +80,13 @@ static inline void nf_ct_ext_free(struct nf_conn *ct) } /* Add this type, returns pointer to data or NULL. */ -void * -__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp); +void *__nf_ct_ext_add_length(struct nf_conn *ct, enum nf_ct_ext_id id, + size_t var_alloc_len, gfp_t gfp); + #define nf_ct_ext_add(ct, id, gfp) \ - ((id##_TYPE *)__nf_ct_ext_add((ct), (id), (gfp))) + ((id##_TYPE *)__nf_ct_ext_add_length((ct), (id), 0, (gfp))) +#define nf_ct_ext_add_length(ct, id, len, gfp) \ + ((id##_TYPE *)__nf_ct_ext_add_length((ct), (id), (len), (gfp))) #define NF_CT_EXT_F_PREALLOC 0x0001 diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index 641ff5f96718..1a9545965c0d 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -44,7 +44,8 @@ void __nf_ct_ext_destroy(struct nf_conn *ct) EXPORT_SYMBOL(__nf_ct_ext_destroy); static void * -nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, gfp_t gfp) +nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, + size_t var_alloc_len, gfp_t gfp) { unsigned int off, len; struct nf_ct_ext_type *t; @@ -54,8 +55,8 @@ nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, gfp_t gfp) t = rcu_dereference(nf_ct_ext_types[id]); BUG_ON(t == NULL); off = ALIGN(sizeof(struct nf_ct_ext), t->align); - len = off + t->len; - alloc_size = t->alloc_size; + len = off + t->len + var_alloc_len; + alloc_size = t->alloc_size + var_alloc_len; rcu_read_unlock(); *ext = kzalloc(alloc_size, gfp); @@ -68,7 +69,8 @@ nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, gfp_t gfp) return (void *)(*ext) + off; } -void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) +void *__nf_ct_ext_add_length(struct nf_conn *ct, enum nf_ct_ext_id id, + size_t var_alloc_len, gfp_t gfp) { struct nf_ct_ext *old, *new; int i, newlen, newoff; @@ -79,7 +81,7 @@ void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) old = ct->ext; if (!old) - return nf_ct_ext_create(&ct->ext, id, gfp); + return nf_ct_ext_create(&ct->ext, id, var_alloc_len, gfp); if (__nf_ct_ext_exist(old, id)) return NULL; @@ -89,7 +91,7 @@ void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) BUG_ON(t == NULL); newoff = ALIGN(old->len, t->align); - newlen = newoff + t->len; + newlen = newoff + t->len + var_alloc_len; rcu_read_unlock(); new = __krealloc(old, newlen, gfp); @@ -117,7 +119,7 @@ void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) memset((void *)new + newoff, 0, newlen - newoff); return (void *)new + newoff; } -EXPORT_SYMBOL(__nf_ct_ext_add); +EXPORT_SYMBOL(__nf_ct_ext_add_length); static void update_alloc_size(struct nf_ct_ext_type *type) { -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 From 7f95e1880e70bb351b992b01ef70ff083fc00d30 Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sat, 16 Jun 2012 15:14:49 +0200 Subject: include/net/dst.h: neaten asterisk placement Fix code style - place the asterisk where it belongs. Signed-off-by: Eldad Zack Signed-off-by: David S. Miller --- include/net/dst.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 8197eadca819..f0bf3b8d5911 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -48,8 +48,8 @@ struct dst_entry { #else void *__pad1; #endif - int (*input)(struct sk_buff*); - int (*output)(struct sk_buff*); + int (*input)(struct sk_buff *); + int (*output)(struct sk_buff *); int flags; #define DST_HOST 0x0001 @@ -241,7 +241,7 @@ dst_metric_locked(const struct dst_entry *dst, int metric) return dst_metric(dst, RTAX_LOCK) & (1<lastuse = time; } -static inline -struct dst_entry * dst_clone(struct dst_entry * dst) +static inline struct dst_entry *dst_clone(struct dst_entry *dst) { if (dst) atomic_inc(&dst->__refcnt); @@ -371,12 +370,12 @@ static inline struct dst_entry *skb_dst_pop(struct sk_buff *skb) } extern int dst_discard(struct sk_buff *skb); -extern void *dst_alloc(struct dst_ops * ops, struct net_device *dev, +extern void *dst_alloc(struct dst_ops *ops, struct net_device *dev, int initial_ref, int initial_obsolete, int flags); -extern void __dst_free(struct dst_entry * dst); -extern struct dst_entry *dst_destroy(struct dst_entry * dst); +extern void __dst_free(struct dst_entry *dst); +extern struct dst_entry *dst_destroy(struct dst_entry *dst); -static inline void dst_free(struct dst_entry * dst) +static inline void dst_free(struct dst_entry *dst) { if (dst->obsolete > 1) return; -- cgit v1.2.3 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') 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 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') 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 From 3be330bf8860dc6079da5acc81295787a04cf4c9 Mon Sep 17 00:00:00 2001 From: Jenny TC Date: Wed, 9 May 2012 20:36:47 +0530 Subject: power_supply: Register battery as a thermal zone Battery and charger contribute to Thermals in most of the embedded devices. So, it makes sense to identify them as Thermal zones in a particular platform. This patch registers a thermal zone if the power supply is reporting a temperature property. The thermal zone will be used by platform's thermal management solution. Signed-off-by: Jenny TC Signed-off-by: Anton Vorontsov --- drivers/power/power_supply_core.c | 65 +++++++++++++++++++++++++++++++++++++++ include/linux/power_supply.h | 3 ++ 2 files changed, 68 insertions(+) (limited to 'include') diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 6ad612726785..ff990d26a0c0 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "power_supply.h" /* exported for the APM Power driver, APM emulation */ @@ -169,6 +170,63 @@ static void power_supply_dev_release(struct device *dev) kfree(dev); } +#ifdef CONFIG_THERMAL +static int power_supply_read_temp(struct thermal_zone_device *tzd, + unsigned long *temp) +{ + struct power_supply *psy; + union power_supply_propval val; + int ret; + + WARN_ON(tzd == NULL); + psy = tzd->devdata; + ret = psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &val); + + /* Convert tenths of degree Celsius to milli degree Celsius. */ + if (!ret) + *temp = val.intval * 100; + + return ret; +} + +static struct thermal_zone_device_ops psy_tzd_ops = { + .get_temp = power_supply_read_temp, +}; + +static int psy_register_thermal(struct power_supply *psy) +{ + int i; + + /* Register battery zone device psy reports temperature */ + for (i = 0; i < psy->num_properties; i++) { + if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) { + psy->tzd = thermal_zone_device_register(psy->name, 0, + psy, &psy_tzd_ops, 0, 0, 0, 0); + if (IS_ERR(psy->tzd)) + return PTR_ERR(psy->tzd); + break; + } + } + return 0; +} + +static void psy_unregister_thermal(struct power_supply *psy) +{ + if (IS_ERR_OR_NULL(psy->tzd)) + return; + thermal_zone_device_unregister(psy->tzd); +} +#else +static int psy_register_thermal(struct power_supply *psy) +{ + return 0; +} + +static void psy_unregister_thermal(struct power_supply *psy) +{ +} +#endif + int power_supply_register(struct device *parent, struct power_supply *psy) { struct device *dev; @@ -197,6 +255,10 @@ int power_supply_register(struct device *parent, struct power_supply *psy) if (rc) goto device_add_failed; + rc = psy_register_thermal(psy); + if (rc) + goto register_thermal_failed; + rc = power_supply_create_triggers(psy); if (rc) goto create_triggers_failed; @@ -206,6 +268,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy) goto success; create_triggers_failed: + psy_unregister_thermal(psy); +register_thermal_failed: device_del(dev); kobject_set_name_failed: device_add_failed: @@ -220,6 +284,7 @@ void power_supply_unregister(struct power_supply *psy) cancel_work_sync(&psy->changed_work); sysfs_remove_link(&psy->dev->kobj, "powers"); power_supply_remove_triggers(psy); + psy_unregister_thermal(psy); device_unregister(psy->dev); } EXPORT_SYMBOL_GPL(power_supply_unregister); diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 3b912bee28d1..59ed2dd9dba9 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -173,6 +173,9 @@ struct power_supply { /* private */ struct device *dev; struct work_struct changed_work; +#ifdef CONFIG_THERMAL + struct thermal_zone_device *tzd; +#endif #ifdef CONFIG_LEDS_TRIGGERS struct led_trigger *charging_full_trig; -- cgit v1.2.3 From 4be77a530be1ea62574f31c20dd9848e7e2ab0f6 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 15 Jun 2012 16:35:28 +0100 Subject: ALSA: pcm: Add snd_pcm_rate_bit_to_rate() This is essentially the reverse of snd_pcm_rate_to_rate_bit(). This is generally useful as the Compress API uses the rate bit directly and it helps to be able to map back to the actual sample rate. Signed-off-by: Dimitris Papastamos Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 1 + sound/core/pcm_misc.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 0d1112815be3..68372bc1e11b 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -893,6 +893,7 @@ extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates; int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime); unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate); +unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit); static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substream, struct snd_dma_buffer *bufp) diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 9c9eff9afbac..d4fc1bfbe457 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -488,3 +488,21 @@ unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate) return SNDRV_PCM_RATE_KNOT; } EXPORT_SYMBOL(snd_pcm_rate_to_rate_bit); + +/** + * snd_pcm_rate_bit_to_rate - converts SNDRV_PCM_RATE_xxx bit to sample rate + * @rate_bit: the rate bit to convert + * + * Returns the sample rate that corresponds to the given SNDRV_PCM_RATE_xxx flag + * or 0 for an unknown rate bit + */ +unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit) +{ + unsigned int i; + + for (i = 0; i < snd_pcm_known_rates.count; i++) + if ((1u << i) == rate_bit) + return snd_pcm_known_rates.list[i]; + return 0; +} +EXPORT_SYMBOL(snd_pcm_rate_bit_to_rate); -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 From a1e4ccb990447df0fe83d164d9a7bc2e6c4b7db7 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Fri, 15 Jun 2012 15:07:13 -0400 Subject: KVM: Introduce __KVM_HAVE_IRQ_LINE This is a preparatory patch for the KVM/ARM implementation. KVM/ARM will use the KVM_IRQ_LINE ioctl, which is currently conditional on __KVM_HAVE_IOAPIC, but ARM obviously doesn't have any IOAPIC support and we need a separate define. Signed-off-by: Christoffer Dall Signed-off-by: Avi Kivity --- arch/ia64/include/asm/kvm.h | 1 + arch/x86/include/asm/kvm.h | 1 + include/trace/events/kvm.h | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/ia64/include/asm/kvm.h b/arch/ia64/include/asm/kvm.h index b9f82c84f093..ec6c6b301238 100644 --- a/arch/ia64/include/asm/kvm.h +++ b/arch/ia64/include/asm/kvm.h @@ -26,6 +26,7 @@ /* Select x86 specific features in */ #define __KVM_HAVE_IOAPIC +#define __KVM_HAVE_IRQ_LINE #define __KVM_HAVE_DEVICE_ASSIGNMENT /* Architectural interrupt line count. */ diff --git a/arch/x86/include/asm/kvm.h b/arch/x86/include/asm/kvm.h index e7d1c194d272..246617efd67f 100644 --- a/arch/x86/include/asm/kvm.h +++ b/arch/x86/include/asm/kvm.h @@ -12,6 +12,7 @@ /* Select x86 specific features in */ #define __KVM_HAVE_PIT #define __KVM_HAVE_IOAPIC +#define __KVM_HAVE_IRQ_LINE #define __KVM_HAVE_DEVICE_ASSIGNMENT #define __KVM_HAVE_MSI #define __KVM_HAVE_USER_NMI diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h index 3df5925fe641..7ef9e759f499 100644 --- a/include/trace/events/kvm.h +++ b/include/trace/events/kvm.h @@ -37,7 +37,7 @@ TRACE_EVENT(kvm_userspace_exit, __entry->errno < 0 ? -__entry->errno : __entry->reason) ); -#if defined(__KVM_HAVE_IOAPIC) +#if defined(__KVM_HAVE_IRQ_LINE) TRACE_EVENT(kvm_set_irq, TP_PROTO(unsigned int gsi, int level, int irq_source_id), TP_ARGS(gsi, level, irq_source_id), @@ -57,7 +57,9 @@ TRACE_EVENT(kvm_set_irq, TP_printk("gsi %u level %d source %d", __entry->gsi, __entry->level, __entry->irq_source_id) ); +#endif +#if defined(__KVM_HAVE_IOAPIC) #define kvm_deliver_mode \ {0x0, "Fixed"}, \ {0x1, "LowPrio"}, \ -- cgit v1.2.3 From fec1868e23099023bc545e199b78d99840b1abc9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 18 Jun 2012 15:38:22 -0700 Subject: USB: properly pad out usb_device_id.driver_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some systems, struct usb_device_id doesn't align properly due to the recent changes in it. So pad out the driver_info field to align on a boundry that systems can handle it. Reported-by: Geert Uytterhoeven Acked-by: Geert Uytterhoeven Cc: Bjørn Mork Signed-off-by: Greg Kroah-Hartman --- include/linux/mod_devicetable.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 7771d453e5f3..6955045199b0 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -122,7 +122,8 @@ struct usb_device_id { __u8 bInterfaceNumber; /* not matched against */ - kernel_ulong_t driver_info; + kernel_ulong_t driver_info + __attribute__((aligned(sizeof(kernel_ulong_t)))); }; /* Some useful macros to use to create struct usb_device_id */ -- cgit v1.2.3 From 86b0905516460b87542686248690337e1d703544 Mon Sep 17 00:00:00 2001 From: Albert Wang Date: Thu, 7 Jun 2012 05:42:24 -0300 Subject: [media] videobuf2: correct the #ifndef text mistake in videobuf2-dma-contig.h It should be a mistake due to copy & paste in header file Correct it in videobuf2-dma-config.h for avoiding duplicate include it Change-Id: I1f71fcec2889c033c7db380c58d9a1369c5afb35 Signed-off-by: Albert Wang Signed-off-by: Mauro Carvalho Chehab --- include/media/videobuf2-dma-contig.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/media/videobuf2-dma-contig.h b/include/media/videobuf2-dma-contig.h index 19ae1e350567..8197f87d6c61 100644 --- a/include/media/videobuf2-dma-contig.h +++ b/include/media/videobuf2-dma-contig.h @@ -1,5 +1,5 @@ /* - * videobuf2-dma-coherent.h - DMA coherent memory allocator for videobuf2 + * videobuf2-dma-contig.h - DMA contig memory allocator for videobuf2 * * Copyright (C) 2010 Samsung Electronics * @@ -10,8 +10,8 @@ * the Free Software Foundation. */ -#ifndef _MEDIA_VIDEOBUF2_DMA_COHERENT_H -#define _MEDIA_VIDEOBUF2_DMA_COHERENT_H +#ifndef _MEDIA_VIDEOBUF2_DMA_CONTIG_H +#define _MEDIA_VIDEOBUF2_DMA_CONTIG_H #include #include -- cgit v1.2.3 From 6aeec0eacd3c6ac259e38d95a9398ac258470b4c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 27 May 2012 06:54:29 -0300 Subject: [media] videodev2.h: add new hwseek capability bits Tell the application whether the hardware seek is bounded and/or wraps around. Signed-off-by: Hans Verkuil Acked-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 370d11106c11..2339678de092 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -2039,6 +2039,8 @@ struct v4l2_modulator { /* Flags for the 'capability' field */ #define V4L2_TUNER_CAP_LOW 0x0001 #define V4L2_TUNER_CAP_NORM 0x0002 +#define V4L2_TUNER_CAP_HWSEEK_BOUNDED 0x0004 +#define V4L2_TUNER_CAP_HWSEEK_WRAP 0x0008 #define V4L2_TUNER_CAP_STEREO 0x0010 #define V4L2_TUNER_CAP_LANG2 0x0020 #define V4L2_TUNER_CAP_SAP 0x0020 -- cgit v1.2.3 From 23f2d735a932c7833d2d00da5e3ecdf4a6836210 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 18 Jun 2012 18:33:48 +0200 Subject: iio: Add helper function for initializing triggered buffers Add a helper function for executing the common tasks which are usually involved in setting up a simple software ringbuffer. It will allocate the buffer, allocate the pollfunc and register the buffer. Signed-off-by: Lars-Peter Clausen Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/Kconfig | 7 ++ drivers/iio/Makefile | 1 + drivers/iio/industrialio-triggered-buffer.c | 110 ++++++++++++++++++++++++++++ include/linux/iio/triggered_buffer.h | 15 ++++ 4 files changed, 133 insertions(+) create mode 100644 drivers/iio/industrialio-triggered-buffer.c create mode 100644 include/linux/iio/triggered_buffer.h (limited to 'include') diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 103349f2b3b5..612073f6c540 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -30,6 +30,13 @@ config IIO_KFIFO_BUF no buffer events so it is up to userspace to work out how often to read from the buffer. +config IIO_TRIGGERED_BUFFER + tristate + select IIO_TRIGGER + select IIO_KFIFO_BUF + help + Provides helper functions for setting up triggered buffers. + endif # IIO_BUFFER config IIO_TRIGGER diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index c38fa2a40af2..34309abb7979 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -7,6 +7,7 @@ industrialio-y := industrialio-core.o industrialio-event.o inkern.o industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o +obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o obj-y += adc/ diff --git a/drivers/iio/industrialio-triggered-buffer.c b/drivers/iio/industrialio-triggered-buffer.c new file mode 100644 index 000000000000..46c619b0d8c5 --- /dev/null +++ b/drivers/iio/industrialio-triggered-buffer.c @@ -0,0 +1,110 @@ + /* + * Copyright (c) 2012 Analog Devices, Inc. + * Author: Lars-Peter Clausen + * + * 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 + +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { + .preenable = &iio_sw_buffer_preenable, + .postenable = &iio_triggered_buffer_postenable, + .predisable = &iio_triggered_buffer_predisable, +}; + +/** + * iio_triggered_buffer_setup() - Setup triggered buffer and pollfunc + * @indio_dev: IIO device structure + * @pollfunc_bh: Function which will be used as pollfunc bottom half + * @pollfunc_th: Function which will be used as pollfunc top half + * @setup_ops: Buffer setup functions to use for this device. + * If NULL the default setup functions for triggered + * buffers will be used. + * + * This function combines some common tasks which will normally be performed + * when setting up a triggered buffer. It will allocate the buffer and the + * pollfunc, as well as register the buffer with the IIO core. + * + * Before calling this function the indio_dev structure should already be + * completely initialized, but not yet registered. In practice this means that + * this function should be called right before iio_device_register(). + * + * To free the resources allocated by this function call + * iio_triggered_buffer_cleanup(). + */ +int iio_triggered_buffer_setup(struct iio_dev *indio_dev, + irqreturn_t (*pollfunc_bh)(int irq, void *p), + irqreturn_t (*pollfunc_th)(int irq, void *p), + const struct iio_buffer_setup_ops *setup_ops) +{ + int ret; + + indio_dev->buffer = iio_kfifo_allocate(indio_dev); + if (!indio_dev->buffer) { + ret = -ENOMEM; + goto error_ret; + } + + indio_dev->pollfunc = iio_alloc_pollfunc(pollfunc_bh, + pollfunc_th, + IRQF_ONESHOT, + indio_dev, + "%s_consumer%d", + indio_dev->name, + indio_dev->id); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_kfifo_free; + } + + /* Ring buffer functions - here trigger setup related */ + if (setup_ops) + indio_dev->setup_ops = setup_ops; + else + indio_dev->setup_ops = &iio_triggered_buffer_setup_ops; + + /* Flag that polled ring buffering is possible */ + indio_dev->modes |= INDIO_BUFFER_TRIGGERED; + + ret = iio_buffer_register(indio_dev, + indio_dev->channels, + indio_dev->num_channels); + if (ret) + goto error_dealloc_pollfunc; + + return 0; + +error_dealloc_pollfunc: + iio_dealloc_pollfunc(indio_dev->pollfunc); +error_kfifo_free: + iio_kfifo_free(indio_dev->buffer); +error_ret: + return ret; +} +EXPORT_SYMBOL(iio_triggered_buffer_setup); + +/** + * iio_triggered_buffer_cleanup() - Free resources allocated by iio_triggered_buffer_setup() + * @indio_dev: IIO device structure + */ +void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev) +{ + iio_buffer_unregister(indio_dev); + iio_dealloc_pollfunc(indio_dev->pollfunc); + iio_kfifo_free(indio_dev->buffer); +} +EXPORT_SYMBOL(iio_triggered_buffer_cleanup); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("IIO helper functions for setting up triggered buffers"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/iio/triggered_buffer.h b/include/linux/iio/triggered_buffer.h new file mode 100644 index 000000000000..c378ebec605e --- /dev/null +++ b/include/linux/iio/triggered_buffer.h @@ -0,0 +1,15 @@ +#ifndef _LINUX_IIO_TRIGGERED_BUFFER_H_ +#define _LINUX_IIO_TRIGGERED_BUFFER_H_ + +#include + +struct iio_dev; +struct iio_buffer_setup_ops; + +int iio_triggered_buffer_setup(struct iio_dev *indio_dev, + irqreturn_t (*pollfunc_bh)(int irq, void *p), + irqreturn_t (*pollfunc_th)(int irq, void *p), + const struct iio_buffer_setup_ops *setup_ops); +void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev); + +#endif -- cgit v1.2.3 From 9cc113bc84e683435c110268712d25aea05b8198 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Mon, 18 Jun 2012 20:33:01 +0200 Subject: iio: typo in iio_chan_spec.ext_info comment Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/iio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index ae0cad908182..2afbb6f01afc 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -214,7 +214,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, * @event_mask: What events can this channel produce. * @ext_info: Array of extended info attributes for this channel. * The array is NULL terminated, the last element should - * have it's name field set to NULL. + * have its name field set to NULL. * @extend_name: Allows labeling of channel attributes with an * informative name. Note this has no effect codes etc, * unlike modifiers. -- cgit v1.2.3 From b90406f1e5f4cb673e331ce6d435f4bdbf2136a2 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Mon, 18 Jun 2012 20:33:02 +0200 Subject: iio: correct documentation for IIO_CONST_ATTR_SAMP_FREQ_AVAIL, match name of #define Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/sysfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/sysfs.h b/include/linux/iio/sysfs.h index bfedb73b850e..b7a934b9431b 100644 --- a/include/linux/iio/sysfs.h +++ b/include/linux/iio/sysfs.h @@ -97,7 +97,7 @@ struct iio_const_attr { #define IIO_DEV_ATTR_SAMP_FREQ_AVAIL(_show) \ IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO, _show, NULL, 0) /** - * IIO_CONST_ATTR_AVAIL_SAMP_FREQ - list available sampling frequencies + * IIO_CONST_ATTR_SAMP_FREQ_AVAIL - list available sampling frequencies * @_string: frequency string for the attribute * * Constant version -- cgit v1.2.3 From 19ea4752fc1f45c778ae66443c1023aced14ba05 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Mon, 18 Jun 2012 20:33:03 +0200 Subject: iio: remove extra ; after function definition Signed-off-by: Peter Meerwald Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- include/linux/iio/buffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index fb0fe46fd659..ad4fb1af0f7d 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -184,7 +184,7 @@ static inline int iio_buffer_register(struct iio_dev *indio_dev, } static inline void iio_buffer_unregister(struct iio_dev *indio_dev) -{}; +{} #endif /* CONFIG_IIO_BUFFER */ -- cgit v1.2.3 From 7c62234547255ce4c385a218915965bc2f14fe45 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 19 Jun 2012 02:10:57 +0200 Subject: netfilter: nfnetlink_queue: fix compilation with NF_CONNTRACK disabled In "9cb0176 netfilter: add glue code to integrate nfnetlink_queue and ctnetlink" the compilation with NF_CONNTRACK disabled is broken. This patch fixes this issue. I have moved the conntrack part into nfnetlink_queue_ct.c to avoid peppering the entire nfnetlink_queue.c code with ifdefs. I also needed to rename nfnetlink_queue.c to nfnetlink_queue_pkt.c to update the net/netfilter/Makefile to support conditional compilation of the conntrack integration. This patch also adds CONFIG_NETFILTER_QUEUE_CT in case you want to explicitly disable the integration between nf_conntrack and nfnetlink_queue. Reported-by: Andrew Morton Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nfnetlink_queue.h | 43 ++ net/netfilter/Kconfig | 9 + net/netfilter/Makefile | 2 + net/netfilter/nf_conntrack_netlink.c | 11 +- net/netfilter/nfnetlink_queue.c | 1121 ------------------------------- net/netfilter/nfnetlink_queue_core.c | 1090 ++++++++++++++++++++++++++++++ net/netfilter/nfnetlink_queue_ct.c | 97 +++ 7 files changed, 1245 insertions(+), 1128 deletions(-) create mode 100644 include/net/netfilter/nfnetlink_queue.h delete mode 100644 net/netfilter/nfnetlink_queue.c create mode 100644 net/netfilter/nfnetlink_queue_core.c create mode 100644 net/netfilter/nfnetlink_queue_ct.c (limited to 'include') diff --git a/include/net/netfilter/nfnetlink_queue.h b/include/net/netfilter/nfnetlink_queue.h new file mode 100644 index 000000000000..9f8095c108e4 --- /dev/null +++ b/include/net/netfilter/nfnetlink_queue.h @@ -0,0 +1,43 @@ +#ifndef _NET_NFNL_QUEUE_H_ +#define _NET_NFNL_QUEUE_H_ + +#include + +struct nf_conn; + +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +struct nf_conn *nfqnl_ct_get(struct sk_buff *entskb, size_t *size, + enum ip_conntrack_info *ctinfo); +struct nf_conn *nfqnl_ct_parse(const struct sk_buff *skb, + const struct nlattr *attr, + enum ip_conntrack_info *ctinfo); +int nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo); +void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, int diff); +#else +inline struct nf_conn * +nfqnl_ct_get(struct sk_buff *entskb, size_t *size, enum ip_conntrack_info *ctinfo) +{ + return NULL; +} + +inline struct nf_conn *nfqnl_ct_parse(const struct sk_buff *skb, + const struct nlattr *attr, + enum ip_conntrack_info *ctinfo) +{ + return NULL; +} + +inline int +nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo) +{ + return 0; +} + +inline void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, int diff) +{ +} +#endif /* NF_CONNTRACK */ +#endif diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index f1a52ba3e4c5..c19b214ffd57 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -340,6 +340,7 @@ config NF_CT_NETLINK_HELPER select NETFILTER_NETLINK depends on NF_CT_NETLINK depends on NETFILTER_NETLINK_QUEUE + depends on NETFILTER_NETLINK_QUEUE_CT depends on NETFILTER_ADVANCED help This option enables the user-space connection tracking helpers @@ -347,6 +348,14 @@ config NF_CT_NETLINK_HELPER If unsure, say `N'. +config NETFILTER_NETLINK_QUEUE_CT + bool "NFQUEUE integration with Connection Tracking" + default n + depends on NETFILTER_NETLINK_QUEUE + help + If this option is enabled, NFQUEUE can include Connection Tracking + information together with the packet is the enqueued via NFNETLINK. + endif # NF_CONNTRACK # transparent proxy support diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 7cc20199fe8c..1c5160f2278e 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -9,6 +9,8 @@ obj-$(CONFIG_NETFILTER) = netfilter.o obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o obj-$(CONFIG_NETFILTER_NETLINK_ACCT) += nfnetlink_acct.o +nfnetlink_queue-y := nfnetlink_queue_core.o +nfnetlink_queue-$(CONFIG_NETFILTER_NETLINK_QUEUE_CT) += nfnetlink_queue_ct.o obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 76271a1301a5..31d1d8f3a6ce 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1627,8 +1627,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, return err; } -#if defined(CONFIG_NETFILTER_NETLINK_QUEUE) || \ - defined(CONFIG_NETFILTER_NETLINK_QUEUE_MODULE) +#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT static size_t ctnetlink_nfqueue_build_size(const struct nf_conn *ct) { @@ -1762,7 +1761,7 @@ static struct nfq_ct_hook ctnetlink_nfqueue_hook = { .seq_adjust = nf_nat_tcp_seq_adjust, #endif }; -#endif /* CONFIG_NETFILTER_NETLINK_QUEUE */ +#endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */ /*********************************************************************** * EXPECT @@ -2568,8 +2567,7 @@ 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) +#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT /* setup interaction between nf_queue and nf_conntrack_netlink. */ RCU_INIT_POINTER(nfq_ct_hook, &ctnetlink_nfqueue_hook); #endif @@ -2590,8 +2588,7 @@ 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) +#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT RCU_INIT_POINTER(nfq_ct_hook, NULL); #endif } diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c deleted file mode 100644 index ff82c7933dfd..000000000000 --- a/net/netfilter/nfnetlink_queue.c +++ /dev/null @@ -1,1121 +0,0 @@ -/* - * This is a module which is used for queueing packets and communicating with - * userspace via nfnetlink. - * - * (C) 2005 by Harald Welte - * (C) 2007 by Patrick McHardy - * - * Based on the old ipv4-only ip_queue.c: - * (C) 2000-2002 James Morris - * (C) 2003-2005 Netfilter Core Team - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef CONFIG_BRIDGE_NETFILTER -#include "../bridge/br_private.h" -#endif - -#define NFQNL_QMAX_DEFAULT 1024 - -struct nfqnl_instance { - struct hlist_node hlist; /* global list of queues */ - struct rcu_head rcu; - - int peer_pid; - unsigned int queue_maxlen; - unsigned int copy_range; - unsigned int queue_dropped; - unsigned int queue_user_dropped; - - - 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. - */ - spinlock_t lock; - unsigned int queue_total; - unsigned int id_sequence; /* 'sequence' of pkt ids */ - struct list_head queue_list; /* packets in queue */ -}; - -typedef int (*nfqnl_cmpfn)(struct nf_queue_entry *, unsigned long); - -static DEFINE_SPINLOCK(instances_lock); - -#define INSTANCE_BUCKETS 16 -static struct hlist_head instance_table[INSTANCE_BUCKETS] __read_mostly; - -static inline u_int8_t instance_hashfn(u_int16_t queue_num) -{ - return ((queue_num >> 8) | queue_num) % INSTANCE_BUCKETS; -} - -static struct nfqnl_instance * -instance_lookup(u_int16_t queue_num) -{ - struct hlist_head *head; - struct hlist_node *pos; - struct nfqnl_instance *inst; - - head = &instance_table[instance_hashfn(queue_num)]; - hlist_for_each_entry_rcu(inst, pos, head, hlist) { - if (inst->queue_num == queue_num) - return inst; - } - return NULL; -} - -static struct nfqnl_instance * -instance_create(u_int16_t queue_num, int pid) -{ - struct nfqnl_instance *inst; - unsigned int h; - int err; - - spin_lock(&instances_lock); - if (instance_lookup(queue_num)) { - err = -EEXIST; - goto out_unlock; - } - - inst = kzalloc(sizeof(*inst), GFP_ATOMIC); - if (!inst) { - err = -ENOMEM; - goto out_unlock; - } - - inst->queue_num = queue_num; - inst->peer_pid = pid; - inst->queue_maxlen = NFQNL_QMAX_DEFAULT; - inst->copy_range = 0xfffff; - inst->copy_mode = NFQNL_COPY_NONE; - spin_lock_init(&inst->lock); - INIT_LIST_HEAD(&inst->queue_list); - - if (!try_module_get(THIS_MODULE)) { - err = -EAGAIN; - goto out_free; - } - - h = instance_hashfn(queue_num); - hlist_add_head_rcu(&inst->hlist, &instance_table[h]); - - spin_unlock(&instances_lock); - - return inst; - -out_free: - kfree(inst); -out_unlock: - spin_unlock(&instances_lock); - return ERR_PTR(err); -} - -static void nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, - unsigned long data); - -static void -instance_destroy_rcu(struct rcu_head *head) -{ - struct nfqnl_instance *inst = container_of(head, struct nfqnl_instance, - rcu); - - nfqnl_flush(inst, NULL, 0); - kfree(inst); - module_put(THIS_MODULE); -} - -static void -__instance_destroy(struct nfqnl_instance *inst) -{ - hlist_del_rcu(&inst->hlist); - call_rcu(&inst->rcu, instance_destroy_rcu); -} - -static void -instance_destroy(struct nfqnl_instance *inst) -{ - spin_lock(&instances_lock); - __instance_destroy(inst); - spin_unlock(&instances_lock); -} - -static inline void -__enqueue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry) -{ - list_add_tail(&entry->list, &queue->queue_list); - queue->queue_total++; -} - -static void -__dequeue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry) -{ - list_del(&entry->list); - queue->queue_total--; -} - -static struct nf_queue_entry * -find_dequeue_entry(struct nfqnl_instance *queue, unsigned int id) -{ - struct nf_queue_entry *entry = NULL, *i; - - spin_lock_bh(&queue->lock); - - list_for_each_entry(i, &queue->queue_list, list) { - if (i->id == id) { - entry = i; - break; - } - } - - if (entry) - __dequeue_entry(queue, entry); - - spin_unlock_bh(&queue->lock); - - return entry; -} - -static void -nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data) -{ - struct nf_queue_entry *entry, *next; - - spin_lock_bh(&queue->lock); - list_for_each_entry_safe(entry, next, &queue->queue_list, list) { - if (!cmpfn || cmpfn(entry, data)) { - list_del(&entry->list); - queue->queue_total--; - nf_reinject(entry, NF_DROP); - } - } - spin_unlock_bh(&queue->lock); -} - -static struct sk_buff * -nfqnl_build_packet_message(struct nfqnl_instance *queue, - struct nf_queue_entry *entry, - __be32 **packet_id_ptr) -{ - sk_buff_data_t old_tail; - size_t size; - size_t data_len = 0; - struct sk_buff *skb; - struct nlattr *nla; - struct nfqnl_msg_packet_hdr *pmsg; - struct nlmsghdr *nlh; - struct nfgenmsg *nfmsg; - 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)) - + nla_total_size(sizeof(u_int32_t)) /* ifindex */ - + nla_total_size(sizeof(u_int32_t)) /* ifindex */ -#ifdef CONFIG_BRIDGE_NETFILTER - + nla_total_size(sizeof(u_int32_t)) /* ifindex */ - + nla_total_size(sizeof(u_int32_t)) /* ifindex */ -#endif - + nla_total_size(sizeof(u_int32_t)) /* mark */ - + nla_total_size(sizeof(struct nfqnl_msg_packet_hw)) - + nla_total_size(sizeof(struct nfqnl_msg_packet_timestamp)); - - outdev = entry->outdev; - - switch ((enum nfqnl_config_mode)ACCESS_ONCE(queue->copy_mode)) { - case NFQNL_COPY_META: - case NFQNL_COPY_NONE: - break; - - case NFQNL_COPY_PACKET: - if (entskb->ip_summed == CHECKSUM_PARTIAL && - skb_checksum_help(entskb)) - return NULL; - - data_len = ACCESS_ONCE(queue->copy_range); - if (data_len == 0 || data_len > entskb->len) - data_len = entskb->len; - - size += nla_total_size(data_len); - 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) - goto nlmsg_failure; - - old_tail = skb->tail; - nlh = NLMSG_PUT(skb, 0, 0, - NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET, - sizeof(struct nfgenmsg)); - nfmsg = NLMSG_DATA(nlh); - nfmsg->nfgen_family = entry->pf; - nfmsg->version = NFNETLINK_V0; - nfmsg->res_id = htons(queue->queue_num); - - nla = __nla_reserve(skb, NFQA_PACKET_HDR, sizeof(*pmsg)); - pmsg = nla_data(nla); - pmsg->hw_protocol = entskb->protocol; - pmsg->hook = entry->hook; - *packet_id_ptr = &pmsg->packet_id; - - indev = entry->indev; - if (indev) { -#ifndef CONFIG_BRIDGE_NETFILTER - if (nla_put_be32(skb, NFQA_IFINDEX_INDEV, htonl(indev->ifindex))) - goto nla_put_failure; -#else - if (entry->pf == PF_BRIDGE) { - /* Case 1: indev is physical input device, we need to - * look for bridge group (when called from - * netfilter_bridge) */ - if (nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV, - htonl(indev->ifindex)) || - /* this is the bridge group "brX" */ - /* rcu_read_lock()ed by __nf_queue */ - nla_put_be32(skb, NFQA_IFINDEX_INDEV, - htonl(br_port_get_rcu(indev)->br->dev->ifindex))) - goto nla_put_failure; - } else { - /* Case 2: indev is bridge group, we need to look for - * physical device (when called from ipv4) */ - if (nla_put_be32(skb, NFQA_IFINDEX_INDEV, - htonl(indev->ifindex))) - goto nla_put_failure; - if (entskb->nf_bridge && entskb->nf_bridge->physindev && - nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV, - htonl(entskb->nf_bridge->physindev->ifindex))) - goto nla_put_failure; - } -#endif - } - - if (outdev) { -#ifndef CONFIG_BRIDGE_NETFILTER - if (nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, htonl(outdev->ifindex))) - goto nla_put_failure; -#else - if (entry->pf == PF_BRIDGE) { - /* Case 1: outdev is physical output device, we need to - * look for bridge group (when called from - * netfilter_bridge) */ - if (nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV, - htonl(outdev->ifindex)) || - /* this is the bridge group "brX" */ - /* rcu_read_lock()ed by __nf_queue */ - nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, - htonl(br_port_get_rcu(outdev)->br->dev->ifindex))) - goto nla_put_failure; - } else { - /* Case 2: outdev is bridge group, we need to look for - * physical output device (when called from ipv4) */ - if (nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, - htonl(outdev->ifindex))) - goto nla_put_failure; - if (entskb->nf_bridge && entskb->nf_bridge->physoutdev && - nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV, - htonl(entskb->nf_bridge->physoutdev->ifindex))) - goto nla_put_failure; - } -#endif - } - - if (entskb->mark && - nla_put_be32(skb, NFQA_MARK, htonl(entskb->mark))) - goto nla_put_failure; - - if (indev && entskb->dev && - entskb->mac_header != entskb->network_header) { - struct nfqnl_msg_packet_hw phw; - int len = dev_parse_header(entskb, phw.hw_addr); - if (len) { - phw.hw_addrlen = htons(len); - if (nla_put(skb, NFQA_HWADDR, sizeof(phw), &phw)) - goto nla_put_failure; - } - } - - if (entskb->tstamp.tv64) { - struct nfqnl_msg_packet_timestamp ts; - struct timeval tv = ktime_to_timeval(entskb->tstamp); - ts.sec = cpu_to_be64(tv.tv_sec); - ts.usec = cpu_to_be64(tv.tv_usec); - - if (nla_put(skb, NFQA_TIMESTAMP, sizeof(ts), &ts)) - goto nla_put_failure; - } - - if (data_len) { - struct nlattr *nla; - int sz = nla_attr_size(data_len); - - if (skb_tailroom(skb) < nla_total_size(data_len)) { - printk(KERN_WARNING "nf_queue: no tailroom!\n"); - goto nlmsg_failure; - } - - nla = (struct nlattr *)skb_put(skb, nla_total_size(data_len)); - nla->nla_type = NFQA_PAYLOAD; - nla->nla_len = sz; - - if (skb_copy_bits(entskb, 0, nla_data(nla), data_len)) - 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; - -nlmsg_failure: -nla_put_failure: - if (skb) - kfree_skb(skb); - net_err_ratelimited("nf_queue: error creating packet message\n"); - return NULL; -} - -static int -nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) -{ - struct sk_buff *nskb; - 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); - if (!queue) { - err = -ESRCH; - goto err_out; - } - - if (queue->copy_mode == NFQNL_COPY_NONE) { - err = -EINVAL; - goto err_out; - } - - nskb = nfqnl_build_packet_message(queue, entry, &packet_id_ptr); - if (nskb == NULL) { - err = -ENOMEM; - goto err_out; - } - spin_lock_bh(&queue->lock); - - if (!queue->peer_pid) { - err = -EINVAL; - goto err_out_free_nskb; - } - if (queue->queue_total >= queue->queue_maxlen) { - 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; - *packet_id_ptr = htonl(entry->id); - - /* nfnetlink_unicast will either free the nskb or add it to a socket */ - err = nfnetlink_unicast(nskb, &init_net, queue->peer_pid, MSG_DONTWAIT); - if (err < 0) { - queue->queue_user_dropped++; - goto err_out_unlock; - } - - __enqueue_entry(queue, entry); - - spin_unlock_bh(&queue->lock); - return 0; - -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; -} - -static int -nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e, int diff) -{ - struct sk_buff *nskb; - - if (diff < 0) { - if (pskb_trim(e->skb, data_len)) - return -ENOMEM; - } else if (diff > 0) { - if (data_len > 0xFFFF) - return -EINVAL; - if (diff > skb_tailroom(e->skb)) { - nskb = skb_copy_expand(e->skb, skb_headroom(e->skb), - diff, GFP_ATOMIC); - if (!nskb) { - printk(KERN_WARNING "nf_queue: OOM " - "in mangle, dropping packet\n"); - return -ENOMEM; - } - kfree_skb(e->skb); - e->skb = nskb; - } - skb_put(e->skb, diff); - } - if (!skb_make_writable(e->skb, data_len)) - return -ENOMEM; - skb_copy_to_linear_data(e->skb, data, data_len); - e->skb->ip_summed = CHECKSUM_NONE; - return 0; -} - -static int -nfqnl_set_mode(struct nfqnl_instance *queue, - unsigned char mode, unsigned int range) -{ - int status = 0; - - spin_lock_bh(&queue->lock); - switch (mode) { - case NFQNL_COPY_NONE: - case NFQNL_COPY_META: - queue->copy_mode = mode; - queue->copy_range = 0; - break; - - case NFQNL_COPY_PACKET: - queue->copy_mode = mode; - /* we're using struct nlattr which has 16bit nla_len */ - if (range > 0xffff) - queue->copy_range = 0xffff; - else - queue->copy_range = range; - break; - - default: - status = -EINVAL; - - } - spin_unlock_bh(&queue->lock); - - return status; -} - -static int -dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex) -{ - if (entry->indev) - if (entry->indev->ifindex == ifindex) - return 1; - if (entry->outdev) - if (entry->outdev->ifindex == ifindex) - return 1; -#ifdef CONFIG_BRIDGE_NETFILTER - if (entry->skb->nf_bridge) { - if (entry->skb->nf_bridge->physindev && - entry->skb->nf_bridge->physindev->ifindex == ifindex) - return 1; - if (entry->skb->nf_bridge->physoutdev && - entry->skb->nf_bridge->physoutdev->ifindex == ifindex) - return 1; - } -#endif - return 0; -} - -/* drop all packets with either indev or outdev == ifindex from all queue - * instances */ -static void -nfqnl_dev_drop(int ifindex) -{ - int i; - - rcu_read_lock(); - - for (i = 0; i < INSTANCE_BUCKETS; i++) { - struct hlist_node *tmp; - struct nfqnl_instance *inst; - struct hlist_head *head = &instance_table[i]; - - hlist_for_each_entry_rcu(inst, tmp, head, hlist) - nfqnl_flush(inst, dev_cmp, ifindex); - } - - rcu_read_unlock(); -} - -#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) - -static int -nfqnl_rcv_dev_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct net_device *dev = ptr; - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - /* Drop any packets associated with the downed device */ - if (event == NETDEV_DOWN) - nfqnl_dev_drop(dev->ifindex); - return NOTIFY_DONE; -} - -static struct notifier_block nfqnl_dev_notifier = { - .notifier_call = nfqnl_rcv_dev_event, -}; - -static int -nfqnl_rcv_nl_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct netlink_notify *n = ptr; - - if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) { - int i; - - /* destroy all instances for this pid */ - spin_lock(&instances_lock); - for (i = 0; i < INSTANCE_BUCKETS; i++) { - struct hlist_node *tmp, *t2; - struct nfqnl_instance *inst; - struct hlist_head *head = &instance_table[i]; - - hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) { - if ((n->net == &init_net) && - (n->pid == inst->peer_pid)) - __instance_destroy(inst); - } - } - spin_unlock(&instances_lock); - } - return NOTIFY_DONE; -} - -static struct notifier_block nfqnl_rtnl_notifier = { - .notifier_call = nfqnl_rcv_nl_event, -}; - -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] = { - [NFQA_VERDICT_HDR] = { .len = sizeof(struct nfqnl_msg_verdict_hdr) }, - [NFQA_MARK] = { .type = NLA_U32 }, -}; - -static struct nfqnl_instance *verdict_instance_lookup(u16 queue_num, int nlpid) -{ - struct nfqnl_instance *queue; - - queue = instance_lookup(queue_num); - if (!queue) - return ERR_PTR(-ENODEV); - - if (queue->peer_pid != nlpid) - return ERR_PTR(-EPERM); - - return queue; -} - -static struct nfqnl_msg_verdict_hdr* -verdicthdr_get(const struct nlattr * const nfqa[]) -{ - struct nfqnl_msg_verdict_hdr *vhdr; - unsigned int verdict; - - if (!nfqa[NFQA_VERDICT_HDR]) - return NULL; - - vhdr = nla_data(nfqa[NFQA_VERDICT_HDR]); - verdict = ntohl(vhdr->verdict) & NF_VERDICT_MASK; - if (verdict > NF_MAX_VERDICT || verdict == NF_STOLEN) - return NULL; - return vhdr; -} - -static int nfq_id_after(unsigned int id, unsigned int max) -{ - return (int)(id - max) > 0; -} - -static int -nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nfqa[]) -{ - struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); - struct nf_queue_entry *entry, *tmp; - unsigned int verdict, maxid; - struct nfqnl_msg_verdict_hdr *vhdr; - struct nfqnl_instance *queue; - LIST_HEAD(batch_list); - u16 queue_num = ntohs(nfmsg->res_id); - - queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).pid); - if (IS_ERR(queue)) - return PTR_ERR(queue); - - vhdr = verdicthdr_get(nfqa); - if (!vhdr) - return -EINVAL; - - verdict = ntohl(vhdr->verdict); - maxid = ntohl(vhdr->id); - - spin_lock_bh(&queue->lock); - - list_for_each_entry_safe(entry, tmp, &queue->queue_list, list) { - if (nfq_id_after(entry->id, maxid)) - break; - __dequeue_entry(queue, entry); - list_add_tail(&entry->list, &batch_list); - } - - spin_unlock_bh(&queue->lock); - - if (list_empty(&batch_list)) - return -ENOENT; - - list_for_each_entry_safe(entry, tmp, &batch_list, list) { - if (nfqa[NFQA_MARK]) - entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); - nf_reinject(entry, verdict); - } - return 0; -} - -static int -nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nfqa[]) -{ - struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); - u_int16_t queue_num = ntohs(nfmsg->res_id); - - struct nfqnl_msg_verdict_hdr *vhdr; - struct nfqnl_instance *queue; - 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) - - queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).pid); - if (IS_ERR(queue)) - return PTR_ERR(queue); - - vhdr = verdicthdr_get(nfqa); - if (!vhdr) - return -EINVAL; - - verdict = ntohl(vhdr->verdict); - - entry = find_dequeue_entry(queue, ntohl(vhdr->id)); - 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]) { - ct = nf_ct_get(entry->skb, &ctinfo); - if (ct && !nf_ct_is_untracked(ct)) - nfq_ct->parse(nfqa[NFQA_CT], ct); - } - - 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]), - 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])); - - nf_reinject(entry, verdict); - return 0; -} - -static int -nfqnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nfqa[]) -{ - return -ENOTSUPP; -} - -static const struct nla_policy nfqa_cfg_policy[NFQA_CFG_MAX+1] = { - [NFQA_CFG_CMD] = { .len = sizeof(struct nfqnl_msg_config_cmd) }, - [NFQA_CFG_PARAMS] = { .len = sizeof(struct nfqnl_msg_config_params) }, -}; - -static const struct nf_queue_handler nfqh = { - .name = "nf_queue", - .outfn = &nfqnl_enqueue_packet, -}; - -static int -nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const nfqa[]) -{ - struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); - u_int16_t queue_num = ntohs(nfmsg->res_id); - struct nfqnl_instance *queue; - struct nfqnl_msg_config_cmd *cmd = NULL; - int ret = 0; - - if (nfqa[NFQA_CFG_CMD]) { - cmd = nla_data(nfqa[NFQA_CFG_CMD]); - - /* Commands without queue context - might sleep */ - switch (cmd->command) { - case NFQNL_CFG_CMD_PF_BIND: - return nf_register_queue_handler(ntohs(cmd->pf), - &nfqh); - case NFQNL_CFG_CMD_PF_UNBIND: - return nf_unregister_queue_handler(ntohs(cmd->pf), - &nfqh); - } - } - - rcu_read_lock(); - queue = instance_lookup(queue_num); - if (queue && queue->peer_pid != NETLINK_CB(skb).pid) { - ret = -EPERM; - goto err_out_unlock; - } - - if (cmd != NULL) { - switch (cmd->command) { - case NFQNL_CFG_CMD_BIND: - if (queue) { - ret = -EBUSY; - goto err_out_unlock; - } - queue = instance_create(queue_num, NETLINK_CB(skb).pid); - if (IS_ERR(queue)) { - ret = PTR_ERR(queue); - goto err_out_unlock; - } - break; - case NFQNL_CFG_CMD_UNBIND: - if (!queue) { - ret = -ENODEV; - goto err_out_unlock; - } - instance_destroy(queue); - break; - case NFQNL_CFG_CMD_PF_BIND: - case NFQNL_CFG_CMD_PF_UNBIND: - break; - default: - ret = -ENOTSUPP; - break; - } - } - - if (nfqa[NFQA_CFG_PARAMS]) { - struct nfqnl_msg_config_params *params; - - if (!queue) { - ret = -ENODEV; - goto err_out_unlock; - } - params = nla_data(nfqa[NFQA_CFG_PARAMS]); - nfqnl_set_mode(queue, params->copy_mode, - ntohl(params->copy_range)); - } - - if (nfqa[NFQA_CFG_QUEUE_MAXLEN]) { - __be32 *queue_maxlen; - - if (!queue) { - ret = -ENODEV; - goto err_out_unlock; - } - queue_maxlen = nla_data(nfqa[NFQA_CFG_QUEUE_MAXLEN]); - spin_lock_bh(&queue->lock); - queue->queue_maxlen = ntohl(*queue_maxlen); - 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; -} - -static const struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = { - [NFQNL_MSG_PACKET] = { .call_rcu = nfqnl_recv_unsupp, - .attr_count = NFQA_MAX, }, - [NFQNL_MSG_VERDICT] = { .call_rcu = nfqnl_recv_verdict, - .attr_count = NFQA_MAX, - .policy = nfqa_verdict_policy }, - [NFQNL_MSG_CONFIG] = { .call = nfqnl_recv_config, - .attr_count = NFQA_CFG_MAX, - .policy = nfqa_cfg_policy }, - [NFQNL_MSG_VERDICT_BATCH]={ .call_rcu = nfqnl_recv_verdict_batch, - .attr_count = NFQA_MAX, - .policy = nfqa_verdict_batch_policy }, -}; - -static const struct nfnetlink_subsystem nfqnl_subsys = { - .name = "nf_queue", - .subsys_id = NFNL_SUBSYS_QUEUE, - .cb_count = NFQNL_MSG_MAX, - .cb = nfqnl_cb, -}; - -#ifdef CONFIG_PROC_FS -struct iter_state { - unsigned int bucket; -}; - -static struct hlist_node *get_first(struct seq_file *seq) -{ - struct iter_state *st = seq->private; - - if (!st) - return NULL; - - for (st->bucket = 0; st->bucket < INSTANCE_BUCKETS; st->bucket++) { - if (!hlist_empty(&instance_table[st->bucket])) - return instance_table[st->bucket].first; - } - return NULL; -} - -static struct hlist_node *get_next(struct seq_file *seq, struct hlist_node *h) -{ - struct iter_state *st = seq->private; - - h = h->next; - while (!h) { - if (++st->bucket >= INSTANCE_BUCKETS) - return NULL; - - h = instance_table[st->bucket].first; - } - return h; -} - -static struct hlist_node *get_idx(struct seq_file *seq, loff_t pos) -{ - struct hlist_node *head; - head = get_first(seq); - - if (head) - while (pos && (head = get_next(seq, head))) - pos--; - return pos ? NULL : head; -} - -static void *seq_start(struct seq_file *seq, loff_t *pos) - __acquires(instances_lock) -{ - spin_lock(&instances_lock); - return get_idx(seq, *pos); -} - -static void *seq_next(struct seq_file *s, void *v, loff_t *pos) -{ - (*pos)++; - return get_next(s, v); -} - -static void seq_stop(struct seq_file *s, void *v) - __releases(instances_lock) -{ - spin_unlock(&instances_lock); -} - -static int seq_show(struct seq_file *s, void *v) -{ - const struct nfqnl_instance *inst = v; - - return seq_printf(s, "%5d %6d %5d %1d %5d %5d %5d %8d %2d\n", - inst->queue_num, - inst->peer_pid, inst->queue_total, - inst->copy_mode, inst->copy_range, - inst->queue_dropped, inst->queue_user_dropped, - inst->id_sequence, 1); -} - -static const struct seq_operations nfqnl_seq_ops = { - .start = seq_start, - .next = seq_next, - .stop = seq_stop, - .show = seq_show, -}; - -static int nfqnl_open(struct inode *inode, struct file *file) -{ - return seq_open_private(file, &nfqnl_seq_ops, - sizeof(struct iter_state)); -} - -static const struct file_operations nfqnl_file_ops = { - .owner = THIS_MODULE, - .open = nfqnl_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; - -#endif /* PROC_FS */ - -static int __init nfnetlink_queue_init(void) -{ - int i, status = -ENOMEM; - - for (i = 0; i < INSTANCE_BUCKETS; i++) - INIT_HLIST_HEAD(&instance_table[i]); - - netlink_register_notifier(&nfqnl_rtnl_notifier); - status = nfnetlink_subsys_register(&nfqnl_subsys); - if (status < 0) { - printk(KERN_ERR "nf_queue: failed to create netlink socket\n"); - goto cleanup_netlink_notifier; - } - -#ifdef CONFIG_PROC_FS - if (!proc_create("nfnetlink_queue", 0440, - proc_net_netfilter, &nfqnl_file_ops)) - goto cleanup_subsys; -#endif - - register_netdevice_notifier(&nfqnl_dev_notifier); - return status; - -#ifdef CONFIG_PROC_FS -cleanup_subsys: - nfnetlink_subsys_unregister(&nfqnl_subsys); -#endif -cleanup_netlink_notifier: - netlink_unregister_notifier(&nfqnl_rtnl_notifier); - return status; -} - -static void __exit nfnetlink_queue_fini(void) -{ - nf_unregister_queue_handlers(&nfqh); - unregister_netdevice_notifier(&nfqnl_dev_notifier); -#ifdef CONFIG_PROC_FS - remove_proc_entry("nfnetlink_queue", proc_net_netfilter); -#endif - nfnetlink_subsys_unregister(&nfqnl_subsys); - netlink_unregister_notifier(&nfqnl_rtnl_notifier); - - rcu_barrier(); /* Wait for completion of call_rcu()'s */ -} - -MODULE_DESCRIPTION("netfilter packet queue handler"); -MODULE_AUTHOR("Harald Welte "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_QUEUE); - -module_init(nfnetlink_queue_init); -module_exit(nfnetlink_queue_fini); diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c new file mode 100644 index 000000000000..d36b95ea8ca3 --- /dev/null +++ b/net/netfilter/nfnetlink_queue_core.c @@ -0,0 +1,1090 @@ +/* + * This is a module which is used for queueing packets and communicating with + * userspace via nfnetlink. + * + * (C) 2005 by Harald Welte + * (C) 2007 by Patrick McHardy + * + * Based on the old ipv4-only ip_queue.c: + * (C) 2000-2002 James Morris + * (C) 2003-2005 Netfilter Core Team + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef CONFIG_BRIDGE_NETFILTER +#include "../bridge/br_private.h" +#endif + +#define NFQNL_QMAX_DEFAULT 1024 + +struct nfqnl_instance { + struct hlist_node hlist; /* global list of queues */ + struct rcu_head rcu; + + int peer_pid; + unsigned int queue_maxlen; + unsigned int copy_range; + unsigned int queue_dropped; + unsigned int queue_user_dropped; + + + 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. + */ + spinlock_t lock; + unsigned int queue_total; + unsigned int id_sequence; /* 'sequence' of pkt ids */ + struct list_head queue_list; /* packets in queue */ +}; + +typedef int (*nfqnl_cmpfn)(struct nf_queue_entry *, unsigned long); + +static DEFINE_SPINLOCK(instances_lock); + +#define INSTANCE_BUCKETS 16 +static struct hlist_head instance_table[INSTANCE_BUCKETS] __read_mostly; + +static inline u_int8_t instance_hashfn(u_int16_t queue_num) +{ + return ((queue_num >> 8) | queue_num) % INSTANCE_BUCKETS; +} + +static struct nfqnl_instance * +instance_lookup(u_int16_t queue_num) +{ + struct hlist_head *head; + struct hlist_node *pos; + struct nfqnl_instance *inst; + + head = &instance_table[instance_hashfn(queue_num)]; + hlist_for_each_entry_rcu(inst, pos, head, hlist) { + if (inst->queue_num == queue_num) + return inst; + } + return NULL; +} + +static struct nfqnl_instance * +instance_create(u_int16_t queue_num, int pid) +{ + struct nfqnl_instance *inst; + unsigned int h; + int err; + + spin_lock(&instances_lock); + if (instance_lookup(queue_num)) { + err = -EEXIST; + goto out_unlock; + } + + inst = kzalloc(sizeof(*inst), GFP_ATOMIC); + if (!inst) { + err = -ENOMEM; + goto out_unlock; + } + + inst->queue_num = queue_num; + inst->peer_pid = pid; + inst->queue_maxlen = NFQNL_QMAX_DEFAULT; + inst->copy_range = 0xfffff; + inst->copy_mode = NFQNL_COPY_NONE; + spin_lock_init(&inst->lock); + INIT_LIST_HEAD(&inst->queue_list); + + if (!try_module_get(THIS_MODULE)) { + err = -EAGAIN; + goto out_free; + } + + h = instance_hashfn(queue_num); + hlist_add_head_rcu(&inst->hlist, &instance_table[h]); + + spin_unlock(&instances_lock); + + return inst; + +out_free: + kfree(inst); +out_unlock: + spin_unlock(&instances_lock); + return ERR_PTR(err); +} + +static void nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, + unsigned long data); + +static void +instance_destroy_rcu(struct rcu_head *head) +{ + struct nfqnl_instance *inst = container_of(head, struct nfqnl_instance, + rcu); + + nfqnl_flush(inst, NULL, 0); + kfree(inst); + module_put(THIS_MODULE); +} + +static void +__instance_destroy(struct nfqnl_instance *inst) +{ + hlist_del_rcu(&inst->hlist); + call_rcu(&inst->rcu, instance_destroy_rcu); +} + +static void +instance_destroy(struct nfqnl_instance *inst) +{ + spin_lock(&instances_lock); + __instance_destroy(inst); + spin_unlock(&instances_lock); +} + +static inline void +__enqueue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry) +{ + list_add_tail(&entry->list, &queue->queue_list); + queue->queue_total++; +} + +static void +__dequeue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry) +{ + list_del(&entry->list); + queue->queue_total--; +} + +static struct nf_queue_entry * +find_dequeue_entry(struct nfqnl_instance *queue, unsigned int id) +{ + struct nf_queue_entry *entry = NULL, *i; + + spin_lock_bh(&queue->lock); + + list_for_each_entry(i, &queue->queue_list, list) { + if (i->id == id) { + entry = i; + break; + } + } + + if (entry) + __dequeue_entry(queue, entry); + + spin_unlock_bh(&queue->lock); + + return entry; +} + +static void +nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data) +{ + struct nf_queue_entry *entry, *next; + + spin_lock_bh(&queue->lock); + list_for_each_entry_safe(entry, next, &queue->queue_list, list) { + if (!cmpfn || cmpfn(entry, data)) { + list_del(&entry->list); + queue->queue_total--; + nf_reinject(entry, NF_DROP); + } + } + spin_unlock_bh(&queue->lock); +} + +static struct sk_buff * +nfqnl_build_packet_message(struct nfqnl_instance *queue, + struct nf_queue_entry *entry, + __be32 **packet_id_ptr) +{ + sk_buff_data_t old_tail; + size_t size; + size_t data_len = 0; + struct sk_buff *skb; + struct nlattr *nla; + struct nfqnl_msg_packet_hdr *pmsg; + struct nlmsghdr *nlh; + struct nfgenmsg *nfmsg; + struct sk_buff *entskb = entry->skb; + struct net_device *indev; + struct net_device *outdev; + 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)) + + nla_total_size(sizeof(u_int32_t)) /* ifindex */ + + nla_total_size(sizeof(u_int32_t)) /* ifindex */ +#ifdef CONFIG_BRIDGE_NETFILTER + + nla_total_size(sizeof(u_int32_t)) /* ifindex */ + + nla_total_size(sizeof(u_int32_t)) /* ifindex */ +#endif + + nla_total_size(sizeof(u_int32_t)) /* mark */ + + nla_total_size(sizeof(struct nfqnl_msg_packet_hw)) + + nla_total_size(sizeof(struct nfqnl_msg_packet_timestamp)); + + outdev = entry->outdev; + + switch ((enum nfqnl_config_mode)ACCESS_ONCE(queue->copy_mode)) { + case NFQNL_COPY_META: + case NFQNL_COPY_NONE: + break; + + case NFQNL_COPY_PACKET: + if (entskb->ip_summed == CHECKSUM_PARTIAL && + skb_checksum_help(entskb)) + return NULL; + + data_len = ACCESS_ONCE(queue->copy_range); + if (data_len == 0 || data_len > entskb->len) + data_len = entskb->len; + + size += nla_total_size(data_len); + break; + } + + if (queue->flags & NFQA_CFG_F_CONNTRACK) + ct = nfqnl_ct_get(entskb, &size, &ctinfo); + + skb = alloc_skb(size, GFP_ATOMIC); + if (!skb) + goto nlmsg_failure; + + old_tail = skb->tail; + nlh = NLMSG_PUT(skb, 0, 0, + NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET, + sizeof(struct nfgenmsg)); + nfmsg = NLMSG_DATA(nlh); + nfmsg->nfgen_family = entry->pf; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = htons(queue->queue_num); + + nla = __nla_reserve(skb, NFQA_PACKET_HDR, sizeof(*pmsg)); + pmsg = nla_data(nla); + pmsg->hw_protocol = entskb->protocol; + pmsg->hook = entry->hook; + *packet_id_ptr = &pmsg->packet_id; + + indev = entry->indev; + if (indev) { +#ifndef CONFIG_BRIDGE_NETFILTER + if (nla_put_be32(skb, NFQA_IFINDEX_INDEV, htonl(indev->ifindex))) + goto nla_put_failure; +#else + if (entry->pf == PF_BRIDGE) { + /* Case 1: indev is physical input device, we need to + * look for bridge group (when called from + * netfilter_bridge) */ + if (nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV, + htonl(indev->ifindex)) || + /* this is the bridge group "brX" */ + /* rcu_read_lock()ed by __nf_queue */ + nla_put_be32(skb, NFQA_IFINDEX_INDEV, + htonl(br_port_get_rcu(indev)->br->dev->ifindex))) + goto nla_put_failure; + } else { + /* Case 2: indev is bridge group, we need to look for + * physical device (when called from ipv4) */ + if (nla_put_be32(skb, NFQA_IFINDEX_INDEV, + htonl(indev->ifindex))) + goto nla_put_failure; + if (entskb->nf_bridge && entskb->nf_bridge->physindev && + nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV, + htonl(entskb->nf_bridge->physindev->ifindex))) + goto nla_put_failure; + } +#endif + } + + if (outdev) { +#ifndef CONFIG_BRIDGE_NETFILTER + if (nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, htonl(outdev->ifindex))) + goto nla_put_failure; +#else + if (entry->pf == PF_BRIDGE) { + /* Case 1: outdev is physical output device, we need to + * look for bridge group (when called from + * netfilter_bridge) */ + if (nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV, + htonl(outdev->ifindex)) || + /* this is the bridge group "brX" */ + /* rcu_read_lock()ed by __nf_queue */ + nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, + htonl(br_port_get_rcu(outdev)->br->dev->ifindex))) + goto nla_put_failure; + } else { + /* Case 2: outdev is bridge group, we need to look for + * physical output device (when called from ipv4) */ + if (nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, + htonl(outdev->ifindex))) + goto nla_put_failure; + if (entskb->nf_bridge && entskb->nf_bridge->physoutdev && + nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV, + htonl(entskb->nf_bridge->physoutdev->ifindex))) + goto nla_put_failure; + } +#endif + } + + if (entskb->mark && + nla_put_be32(skb, NFQA_MARK, htonl(entskb->mark))) + goto nla_put_failure; + + if (indev && entskb->dev && + entskb->mac_header != entskb->network_header) { + struct nfqnl_msg_packet_hw phw; + int len = dev_parse_header(entskb, phw.hw_addr); + if (len) { + phw.hw_addrlen = htons(len); + if (nla_put(skb, NFQA_HWADDR, sizeof(phw), &phw)) + goto nla_put_failure; + } + } + + if (entskb->tstamp.tv64) { + struct nfqnl_msg_packet_timestamp ts; + struct timeval tv = ktime_to_timeval(entskb->tstamp); + ts.sec = cpu_to_be64(tv.tv_sec); + ts.usec = cpu_to_be64(tv.tv_usec); + + if (nla_put(skb, NFQA_TIMESTAMP, sizeof(ts), &ts)) + goto nla_put_failure; + } + + if (data_len) { + struct nlattr *nla; + int sz = nla_attr_size(data_len); + + if (skb_tailroom(skb) < nla_total_size(data_len)) { + printk(KERN_WARNING "nf_queue: no tailroom!\n"); + goto nlmsg_failure; + } + + nla = (struct nlattr *)skb_put(skb, nla_total_size(data_len)); + nla->nla_type = NFQA_PAYLOAD; + nla->nla_len = sz; + + if (skb_copy_bits(entskb, 0, nla_data(nla), data_len)) + BUG(); + } + + if (ct && nfqnl_ct_put(skb, ct, ctinfo) < 0) + goto nla_put_failure; + + nlh->nlmsg_len = skb->tail - old_tail; + return skb; + +nlmsg_failure: +nla_put_failure: + if (skb) + kfree_skb(skb); + net_err_ratelimited("nf_queue: error creating packet message\n"); + return NULL; +} + +static int +nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) +{ + struct sk_buff *nskb; + 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); + if (!queue) { + err = -ESRCH; + goto err_out; + } + + if (queue->copy_mode == NFQNL_COPY_NONE) { + err = -EINVAL; + goto err_out; + } + + nskb = nfqnl_build_packet_message(queue, entry, &packet_id_ptr); + if (nskb == NULL) { + err = -ENOMEM; + goto err_out; + } + spin_lock_bh(&queue->lock); + + if (!queue->peer_pid) { + err = -EINVAL; + goto err_out_free_nskb; + } + if (queue->queue_total >= queue->queue_maxlen) { + 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; + *packet_id_ptr = htonl(entry->id); + + /* nfnetlink_unicast will either free the nskb or add it to a socket */ + err = nfnetlink_unicast(nskb, &init_net, queue->peer_pid, MSG_DONTWAIT); + if (err < 0) { + queue->queue_user_dropped++; + goto err_out_unlock; + } + + __enqueue_entry(queue, entry); + + spin_unlock_bh(&queue->lock); + return 0; + +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; +} + +static int +nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e, int diff) +{ + struct sk_buff *nskb; + + if (diff < 0) { + if (pskb_trim(e->skb, data_len)) + return -ENOMEM; + } else if (diff > 0) { + if (data_len > 0xFFFF) + return -EINVAL; + if (diff > skb_tailroom(e->skb)) { + nskb = skb_copy_expand(e->skb, skb_headroom(e->skb), + diff, GFP_ATOMIC); + if (!nskb) { + printk(KERN_WARNING "nf_queue: OOM " + "in mangle, dropping packet\n"); + return -ENOMEM; + } + kfree_skb(e->skb); + e->skb = nskb; + } + skb_put(e->skb, diff); + } + if (!skb_make_writable(e->skb, data_len)) + return -ENOMEM; + skb_copy_to_linear_data(e->skb, data, data_len); + e->skb->ip_summed = CHECKSUM_NONE; + return 0; +} + +static int +nfqnl_set_mode(struct nfqnl_instance *queue, + unsigned char mode, unsigned int range) +{ + int status = 0; + + spin_lock_bh(&queue->lock); + switch (mode) { + case NFQNL_COPY_NONE: + case NFQNL_COPY_META: + queue->copy_mode = mode; + queue->copy_range = 0; + break; + + case NFQNL_COPY_PACKET: + queue->copy_mode = mode; + /* we're using struct nlattr which has 16bit nla_len */ + if (range > 0xffff) + queue->copy_range = 0xffff; + else + queue->copy_range = range; + break; + + default: + status = -EINVAL; + + } + spin_unlock_bh(&queue->lock); + + return status; +} + +static int +dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex) +{ + if (entry->indev) + if (entry->indev->ifindex == ifindex) + return 1; + if (entry->outdev) + if (entry->outdev->ifindex == ifindex) + return 1; +#ifdef CONFIG_BRIDGE_NETFILTER + if (entry->skb->nf_bridge) { + if (entry->skb->nf_bridge->physindev && + entry->skb->nf_bridge->physindev->ifindex == ifindex) + return 1; + if (entry->skb->nf_bridge->physoutdev && + entry->skb->nf_bridge->physoutdev->ifindex == ifindex) + return 1; + } +#endif + return 0; +} + +/* drop all packets with either indev or outdev == ifindex from all queue + * instances */ +static void +nfqnl_dev_drop(int ifindex) +{ + int i; + + rcu_read_lock(); + + for (i = 0; i < INSTANCE_BUCKETS; i++) { + struct hlist_node *tmp; + struct nfqnl_instance *inst; + struct hlist_head *head = &instance_table[i]; + + hlist_for_each_entry_rcu(inst, tmp, head, hlist) + nfqnl_flush(inst, dev_cmp, ifindex); + } + + rcu_read_unlock(); +} + +#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) + +static int +nfqnl_rcv_dev_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *dev = ptr; + + if (!net_eq(dev_net(dev), &init_net)) + return NOTIFY_DONE; + + /* Drop any packets associated with the downed device */ + if (event == NETDEV_DOWN) + nfqnl_dev_drop(dev->ifindex); + return NOTIFY_DONE; +} + +static struct notifier_block nfqnl_dev_notifier = { + .notifier_call = nfqnl_rcv_dev_event, +}; + +static int +nfqnl_rcv_nl_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct netlink_notify *n = ptr; + + if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) { + int i; + + /* destroy all instances for this pid */ + spin_lock(&instances_lock); + for (i = 0; i < INSTANCE_BUCKETS; i++) { + struct hlist_node *tmp, *t2; + struct nfqnl_instance *inst; + struct hlist_head *head = &instance_table[i]; + + hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) { + if ((n->net == &init_net) && + (n->pid == inst->peer_pid)) + __instance_destroy(inst); + } + } + spin_unlock(&instances_lock); + } + return NOTIFY_DONE; +} + +static struct notifier_block nfqnl_rtnl_notifier = { + .notifier_call = nfqnl_rcv_nl_event, +}; + +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] = { + [NFQA_VERDICT_HDR] = { .len = sizeof(struct nfqnl_msg_verdict_hdr) }, + [NFQA_MARK] = { .type = NLA_U32 }, +}; + +static struct nfqnl_instance *verdict_instance_lookup(u16 queue_num, int nlpid) +{ + struct nfqnl_instance *queue; + + queue = instance_lookup(queue_num); + if (!queue) + return ERR_PTR(-ENODEV); + + if (queue->peer_pid != nlpid) + return ERR_PTR(-EPERM); + + return queue; +} + +static struct nfqnl_msg_verdict_hdr* +verdicthdr_get(const struct nlattr * const nfqa[]) +{ + struct nfqnl_msg_verdict_hdr *vhdr; + unsigned int verdict; + + if (!nfqa[NFQA_VERDICT_HDR]) + return NULL; + + vhdr = nla_data(nfqa[NFQA_VERDICT_HDR]); + verdict = ntohl(vhdr->verdict) & NF_VERDICT_MASK; + if (verdict > NF_MAX_VERDICT || verdict == NF_STOLEN) + return NULL; + return vhdr; +} + +static int nfq_id_after(unsigned int id, unsigned int max) +{ + return (int)(id - max) > 0; +} + +static int +nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nfqa[]) +{ + struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); + struct nf_queue_entry *entry, *tmp; + unsigned int verdict, maxid; + struct nfqnl_msg_verdict_hdr *vhdr; + struct nfqnl_instance *queue; + LIST_HEAD(batch_list); + u16 queue_num = ntohs(nfmsg->res_id); + + queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).pid); + if (IS_ERR(queue)) + return PTR_ERR(queue); + + vhdr = verdicthdr_get(nfqa); + if (!vhdr) + return -EINVAL; + + verdict = ntohl(vhdr->verdict); + maxid = ntohl(vhdr->id); + + spin_lock_bh(&queue->lock); + + list_for_each_entry_safe(entry, tmp, &queue->queue_list, list) { + if (nfq_id_after(entry->id, maxid)) + break; + __dequeue_entry(queue, entry); + list_add_tail(&entry->list, &batch_list); + } + + spin_unlock_bh(&queue->lock); + + if (list_empty(&batch_list)) + return -ENOENT; + + list_for_each_entry_safe(entry, tmp, &batch_list, list) { + if (nfqa[NFQA_MARK]) + entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); + nf_reinject(entry, verdict); + } + return 0; +} + +static int +nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nfqa[]) +{ + struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); + u_int16_t queue_num = ntohs(nfmsg->res_id); + + struct nfqnl_msg_verdict_hdr *vhdr; + struct nfqnl_instance *queue; + unsigned int verdict; + struct nf_queue_entry *entry; + enum ip_conntrack_info uninitialized_var(ctinfo); + struct nf_conn *ct = NULL; + + queue = instance_lookup(queue_num); + if (!queue) + + queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).pid); + if (IS_ERR(queue)) + return PTR_ERR(queue); + + vhdr = verdicthdr_get(nfqa); + if (!vhdr) + return -EINVAL; + + verdict = ntohl(vhdr->verdict); + + entry = find_dequeue_entry(queue, ntohl(vhdr->id)); + if (entry == NULL) + return -ENOENT; + + rcu_read_lock(); + if (nfqa[NFQA_CT] && (queue->flags & NFQA_CFG_F_CONNTRACK)) + ct = nfqnl_ct_parse(entry->skb, nfqa[NFQA_CT], &ctinfo); + + 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]), + payload_len, entry, diff) < 0) + verdict = NF_DROP; + + if (ct) + nfqnl_ct_seq_adjust(skb, ct, ctinfo, diff); + } + rcu_read_unlock(); + + if (nfqa[NFQA_MARK]) + entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); + + nf_reinject(entry, verdict); + return 0; +} + +static int +nfqnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nfqa[]) +{ + return -ENOTSUPP; +} + +static const struct nla_policy nfqa_cfg_policy[NFQA_CFG_MAX+1] = { + [NFQA_CFG_CMD] = { .len = sizeof(struct nfqnl_msg_config_cmd) }, + [NFQA_CFG_PARAMS] = { .len = sizeof(struct nfqnl_msg_config_params) }, +}; + +static const struct nf_queue_handler nfqh = { + .name = "nf_queue", + .outfn = &nfqnl_enqueue_packet, +}; + +static int +nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nfqa[]) +{ + struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); + u_int16_t queue_num = ntohs(nfmsg->res_id); + struct nfqnl_instance *queue; + struct nfqnl_msg_config_cmd *cmd = NULL; + int ret = 0; + + if (nfqa[NFQA_CFG_CMD]) { + cmd = nla_data(nfqa[NFQA_CFG_CMD]); + + /* Commands without queue context - might sleep */ + switch (cmd->command) { + case NFQNL_CFG_CMD_PF_BIND: + return nf_register_queue_handler(ntohs(cmd->pf), + &nfqh); + case NFQNL_CFG_CMD_PF_UNBIND: + return nf_unregister_queue_handler(ntohs(cmd->pf), + &nfqh); + } + } + + rcu_read_lock(); + queue = instance_lookup(queue_num); + if (queue && queue->peer_pid != NETLINK_CB(skb).pid) { + ret = -EPERM; + goto err_out_unlock; + } + + if (cmd != NULL) { + switch (cmd->command) { + case NFQNL_CFG_CMD_BIND: + if (queue) { + ret = -EBUSY; + goto err_out_unlock; + } + queue = instance_create(queue_num, NETLINK_CB(skb).pid); + if (IS_ERR(queue)) { + ret = PTR_ERR(queue); + goto err_out_unlock; + } + break; + case NFQNL_CFG_CMD_UNBIND: + if (!queue) { + ret = -ENODEV; + goto err_out_unlock; + } + instance_destroy(queue); + break; + case NFQNL_CFG_CMD_PF_BIND: + case NFQNL_CFG_CMD_PF_UNBIND: + break; + default: + ret = -ENOTSUPP; + break; + } + } + + if (nfqa[NFQA_CFG_PARAMS]) { + struct nfqnl_msg_config_params *params; + + if (!queue) { + ret = -ENODEV; + goto err_out_unlock; + } + params = nla_data(nfqa[NFQA_CFG_PARAMS]); + nfqnl_set_mode(queue, params->copy_mode, + ntohl(params->copy_range)); + } + + if (nfqa[NFQA_CFG_QUEUE_MAXLEN]) { + __be32 *queue_maxlen; + + if (!queue) { + ret = -ENODEV; + goto err_out_unlock; + } + queue_maxlen = nla_data(nfqa[NFQA_CFG_QUEUE_MAXLEN]); + spin_lock_bh(&queue->lock); + queue->queue_maxlen = ntohl(*queue_maxlen); + 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; +} + +static const struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = { + [NFQNL_MSG_PACKET] = { .call_rcu = nfqnl_recv_unsupp, + .attr_count = NFQA_MAX, }, + [NFQNL_MSG_VERDICT] = { .call_rcu = nfqnl_recv_verdict, + .attr_count = NFQA_MAX, + .policy = nfqa_verdict_policy }, + [NFQNL_MSG_CONFIG] = { .call = nfqnl_recv_config, + .attr_count = NFQA_CFG_MAX, + .policy = nfqa_cfg_policy }, + [NFQNL_MSG_VERDICT_BATCH]={ .call_rcu = nfqnl_recv_verdict_batch, + .attr_count = NFQA_MAX, + .policy = nfqa_verdict_batch_policy }, +}; + +static const struct nfnetlink_subsystem nfqnl_subsys = { + .name = "nf_queue", + .subsys_id = NFNL_SUBSYS_QUEUE, + .cb_count = NFQNL_MSG_MAX, + .cb = nfqnl_cb, +}; + +#ifdef CONFIG_PROC_FS +struct iter_state { + unsigned int bucket; +}; + +static struct hlist_node *get_first(struct seq_file *seq) +{ + struct iter_state *st = seq->private; + + if (!st) + return NULL; + + for (st->bucket = 0; st->bucket < INSTANCE_BUCKETS; st->bucket++) { + if (!hlist_empty(&instance_table[st->bucket])) + return instance_table[st->bucket].first; + } + return NULL; +} + +static struct hlist_node *get_next(struct seq_file *seq, struct hlist_node *h) +{ + struct iter_state *st = seq->private; + + h = h->next; + while (!h) { + if (++st->bucket >= INSTANCE_BUCKETS) + return NULL; + + h = instance_table[st->bucket].first; + } + return h; +} + +static struct hlist_node *get_idx(struct seq_file *seq, loff_t pos) +{ + struct hlist_node *head; + head = get_first(seq); + + if (head) + while (pos && (head = get_next(seq, head))) + pos--; + return pos ? NULL : head; +} + +static void *seq_start(struct seq_file *seq, loff_t *pos) + __acquires(instances_lock) +{ + spin_lock(&instances_lock); + return get_idx(seq, *pos); +} + +static void *seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + (*pos)++; + return get_next(s, v); +} + +static void seq_stop(struct seq_file *s, void *v) + __releases(instances_lock) +{ + spin_unlock(&instances_lock); +} + +static int seq_show(struct seq_file *s, void *v) +{ + const struct nfqnl_instance *inst = v; + + return seq_printf(s, "%5d %6d %5d %1d %5d %5d %5d %8d %2d\n", + inst->queue_num, + inst->peer_pid, inst->queue_total, + inst->copy_mode, inst->copy_range, + inst->queue_dropped, inst->queue_user_dropped, + inst->id_sequence, 1); +} + +static const struct seq_operations nfqnl_seq_ops = { + .start = seq_start, + .next = seq_next, + .stop = seq_stop, + .show = seq_show, +}; + +static int nfqnl_open(struct inode *inode, struct file *file) +{ + return seq_open_private(file, &nfqnl_seq_ops, + sizeof(struct iter_state)); +} + +static const struct file_operations nfqnl_file_ops = { + .owner = THIS_MODULE, + .open = nfqnl_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + +#endif /* PROC_FS */ + +static int __init nfnetlink_queue_init(void) +{ + int i, status = -ENOMEM; + + for (i = 0; i < INSTANCE_BUCKETS; i++) + INIT_HLIST_HEAD(&instance_table[i]); + + netlink_register_notifier(&nfqnl_rtnl_notifier); + status = nfnetlink_subsys_register(&nfqnl_subsys); + if (status < 0) { + printk(KERN_ERR "nf_queue: failed to create netlink socket\n"); + goto cleanup_netlink_notifier; + } + +#ifdef CONFIG_PROC_FS + if (!proc_create("nfnetlink_queue", 0440, + proc_net_netfilter, &nfqnl_file_ops)) + goto cleanup_subsys; +#endif + + register_netdevice_notifier(&nfqnl_dev_notifier); + return status; + +#ifdef CONFIG_PROC_FS +cleanup_subsys: + nfnetlink_subsys_unregister(&nfqnl_subsys); +#endif +cleanup_netlink_notifier: + netlink_unregister_notifier(&nfqnl_rtnl_notifier); + return status; +} + +static void __exit nfnetlink_queue_fini(void) +{ + nf_unregister_queue_handlers(&nfqh); + unregister_netdevice_notifier(&nfqnl_dev_notifier); +#ifdef CONFIG_PROC_FS + remove_proc_entry("nfnetlink_queue", proc_net_netfilter); +#endif + nfnetlink_subsys_unregister(&nfqnl_subsys); + netlink_unregister_notifier(&nfqnl_rtnl_notifier); + + rcu_barrier(); /* Wait for completion of call_rcu()'s */ +} + +MODULE_DESCRIPTION("netfilter packet queue handler"); +MODULE_AUTHOR("Harald Welte "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_QUEUE); + +module_init(nfnetlink_queue_init); +module_exit(nfnetlink_queue_fini); diff --git a/net/netfilter/nfnetlink_queue_ct.c b/net/netfilter/nfnetlink_queue_ct.c new file mode 100644 index 000000000000..68ef550066f5 --- /dev/null +++ b/net/netfilter/nfnetlink_queue_ct.c @@ -0,0 +1,97 @@ +/* + * (C) 2012 by 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. + * + */ + +#include +#include +#include +#include +#include + +struct nf_conn *nfqnl_ct_get(struct sk_buff *entskb, size_t *size, + enum ip_conntrack_info *ctinfo) +{ + struct nfq_ct_hook *nfq_ct; + struct nf_conn *ct; + + /* rcu_read_lock()ed by __nf_queue already. */ + nfq_ct = rcu_dereference(nfq_ct_hook); + if (nfq_ct == NULL) + return NULL; + + ct = nf_ct_get(entskb, ctinfo); + if (ct) { + if (!nf_ct_is_untracked(ct)) + *size += nfq_ct->build_size(ct); + else + ct = NULL; + } + return ct; +} + +struct nf_conn * +nfqnl_ct_parse(const struct sk_buff *skb, const struct nlattr *attr, + enum ip_conntrack_info *ctinfo) +{ + struct nfq_ct_hook *nfq_ct; + struct nf_conn *ct; + + /* rcu_read_lock()ed by __nf_queue already. */ + nfq_ct = rcu_dereference(nfq_ct_hook); + if (nfq_ct == NULL) + return NULL; + + ct = nf_ct_get(skb, ctinfo); + if (ct && !nf_ct_is_untracked(ct)) + nfq_ct->parse(attr, ct); + + return ct; +} + +int nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo) +{ + struct nfq_ct_hook *nfq_ct; + struct nlattr *nest_parms; + u_int32_t tmp; + + nfq_ct = rcu_dereference(nfq_ct_hook); + if (nfq_ct == NULL) + return 0; + + 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_be32(skb, NFQA_CT_INFO, htonl(tmp))) + goto nla_put_failure; + + return 0; + +nla_put_failure: + return -1; +} + +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; + + nfq_ct = rcu_dereference(nfq_ct_hook); + if (nfq_ct == NULL) + return; + + if ((ct->status & IPS_NAT_MASK) && diff) + nfq_ct->seq_adjust(skb, ct, ctinfo, diff); +} -- cgit v1.2.3 From 9345d40c580d0f3dfc040add0e6371b1a629c1cc Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 15 Jun 2012 10:36:42 +0300 Subject: Bluetooth: Use AUTO_OFF constant in jiffies Move AUTO_OFF_TIMEOUT to other constants changing name to HCI_AUTO_OFF_TIMEOUT and convert to jiffies. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 3 ++- net/bluetooth/hci_core.c | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7dcd3495edde..ccd723e0f783 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -142,8 +142,9 @@ enum { #define HCI_DISCONN_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ #define HCI_PAIRING_TIMEOUT msecs_to_jiffies(60000) /* 60 seconds */ #define HCI_INIT_TIMEOUT msecs_to_jiffies(10000) /* 10 seconds */ -#define HCI_CMD_TIMEOUT msecs_to_jiffies(1000) /* 1 seconds */ +#define HCI_CMD_TIMEOUT msecs_to_jiffies(1000) /* 1 second */ #define HCI_ACL_TX_TIMEOUT msecs_to_jiffies(45000) /* 45 seconds */ +#define HCI_AUTO_OFF_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ /* HCI data types */ #define HCI_COMMAND_PKT 0x01 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index a2bdf936ed46..32dcb09cdb5d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -33,8 +33,6 @@ #include #include -#define AUTO_OFF_TIMEOUT 2000 - static void hci_rx_work(struct work_struct *work); static void hci_cmd_work(struct work_struct *work); static void hci_tx_work(struct work_struct *work); @@ -1083,8 +1081,7 @@ static void hci_power_on(struct work_struct *work) return; if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - schedule_delayed_work(&hdev->power_off, - msecs_to_jiffies(AUTO_OFF_TIMEOUT)); + schedule_delayed_work(&hdev->power_off, HCI_AUTO_OFF_TIMEOUT); if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) mgmt_index_added(hdev); -- cgit v1.2.3 From 674147e21195a496164e1c7ff70d0f0b45235f25 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 19 Jun 2012 05:25:46 +0200 Subject: netfilter: fix missing symbols if CONFIG_NETFILTER_NETLINK_QUEUE_CT unset ERROR: "nfqnl_ct_parse" [net/netfilter/nfnetlink_queue.ko] undefined! ERROR: "nfqnl_ct_seq_adjust" [net/netfilter/nfnetlink_queue.ko] undefined! ERROR: "nfqnl_ct_put" [net/netfilter/nfnetlink_queue.ko] undefined! ERROR: "nfqnl_ct_get" [net/netfilter/nfnetlink_queue.ko] undefined! We have to use CONFIG_NETFILTER_NETLINK_QUEUE_CT in include/net/netfilter/nfnetlink_queue.h, not CONFIG_NF_CONNTRACK. Signed-off-by: Pablo Neira Ayuso Signed-off-by: David S. Miller --- include/net/netfilter/nfnetlink_queue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/netfilter/nfnetlink_queue.h b/include/net/netfilter/nfnetlink_queue.h index 9f8095c108e4..86267a529514 100644 --- a/include/net/netfilter/nfnetlink_queue.h +++ b/include/net/netfilter/nfnetlink_queue.h @@ -5,7 +5,7 @@ struct nf_conn; -#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT struct nf_conn *nfqnl_ct_get(struct sk_buff *entskb, size_t *size, enum ip_conntrack_info *ctinfo); struct nf_conn *nfqnl_ct_parse(const struct sk_buff *skb, -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 From 3824c47714f28091f74ca2505146514b4da1f390 Mon Sep 17 00:00:00 2001 From: Ramakrishna Pallala Date: Sun, 6 May 2012 18:16:44 +0530 Subject: power_supply: Add constant charge_current and charge_voltage properties Constant Charge Current(CC) is charging parameter which limit the maximum current which can be pumped into the battery during charge cycle. Constant Charge Voltage(CV) is also charging parameter which limit the maximum voltage that battery can reach during charge cycle. It is very common practice that at low or high temperatures we do not charge the batteries upto it's fullest charge voltage to avoid battery and user safety issues. These sysfs properties will be useful for debug and to implement certain user space policies like "Charging limited due to OverTemp". Signed-off-by: Ramakrishna Pallala Signed-off-by: Anton Vorontsov --- Documentation/power/power_supply_class.txt | 4 ++++ drivers/power/power_supply_sysfs.c | 2 ++ include/linux/power_supply.h | 4 ++++ 3 files changed, 10 insertions(+) (limited to 'include') diff --git a/Documentation/power/power_supply_class.txt b/Documentation/power/power_supply_class.txt index 211831d4095f..c0f62ae2b5e0 100644 --- a/Documentation/power/power_supply_class.txt +++ b/Documentation/power/power_supply_class.txt @@ -112,6 +112,10 @@ CHARGE_COUNTER - the current charge counter (in µAh). This could easily be negative; there is no empty or full value. It is only useful for relative, time-based measurements. +CONSTANT_CHARGE_CURRENT - constant charge current programmed by charger. + +CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger. + ENERGY_FULL, ENERGY_EMPTY - same as above but for energy. CAPACITY - capacity in percents. diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 4150747f9186..58846d929b34 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -159,6 +159,8 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(charge_now), POWER_SUPPLY_ATTR(charge_avg), POWER_SUPPLY_ATTR(charge_counter), + POWER_SUPPLY_ATTR(constant_charge_current), + POWER_SUPPLY_ATTR(constant_charge_voltage), POWER_SUPPLY_ATTR(energy_full_design), POWER_SUPPLY_ATTR(energy_empty_design), POWER_SUPPLY_ATTR(energy_full), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 59ed2dd9dba9..53f177db6ac9 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -109,6 +109,8 @@ enum power_supply_property { POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_AVG, POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN, POWER_SUPPLY_PROP_ENERGY_FULL, @@ -239,6 +241,7 @@ static inline bool power_supply_is_amp_property(enum power_supply_property psp) case POWER_SUPPLY_PROP_CHARGE_NOW: case POWER_SUPPLY_PROP_CHARGE_AVG: case POWER_SUPPLY_PROP_CHARGE_COUNTER: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CURRENT_MAX: case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_CURRENT_AVG: @@ -266,6 +269,7 @@ static inline bool power_supply_is_watt_property(enum power_supply_property psp) case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_VOLTAGE_AVG: case POWER_SUPPLY_PROP_VOLTAGE_OCV: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: case POWER_SUPPLY_PROP_POWER_NOW: return 1; default: -- cgit v1.2.3 From f9242b6b28d61295f2bf7e8adfb1060b382e5381 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 19 Jun 2012 18:56:21 -0700 Subject: inet: Sanitize inet{,6} protocol demux. Don't pretend that inet_protos[] and inet6_protos[] are hashes, thay are just a straight arrays. Remove all unnecessary hash masking. Document MAX_INET_PROTOS. Use RAW_HTABLE_SIZE when appropriate. Reported-by: Ben Hutchings Signed-off-by: David S. Miller --- include/net/protocol.h | 7 +++++-- net/ipv4/af_inet.c | 26 ++++++++++++-------------- net/ipv4/icmp.c | 9 ++++----- net/ipv4/ip_input.c | 5 ++--- net/ipv4/protocol.c | 8 +++----- net/ipv6/icmp.c | 7 ++----- net/ipv6/ip6_input.c | 9 +++------ net/ipv6/protocol.c | 8 +++----- net/ipv6/raw.c | 4 ++-- 9 files changed, 36 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/include/net/protocol.h b/include/net/protocol.h index 875f4895b033..a1b1b530c338 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -29,8 +29,11 @@ #include #endif -#define MAX_INET_PROTOS 256 /* Must be a power of 2 */ - +/* This is one larger than the largest protocol value that can be + * found in an ipv4 or ipv6 header. Since in both cases the protocol + * value is presented in a __u8, this is defined to be 256. + */ +#define MAX_INET_PROTOS 256 /* This is used to register protocols. */ struct net_protocol { diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index e4e8e00a2c91..85a3b1763136 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -242,20 +242,18 @@ void build_ehash_secret(void) } EXPORT_SYMBOL(build_ehash_secret); -static inline int inet_netns_ok(struct net *net, int protocol) +static inline int inet_netns_ok(struct net *net, __u8 protocol) { - int hash; const struct net_protocol *ipprot; if (net_eq(net, &init_net)) return 1; - hash = protocol & (MAX_INET_PROTOS - 1); - ipprot = rcu_dereference(inet_protos[hash]); - - if (ipprot == NULL) + ipprot = rcu_dereference(inet_protos[protocol]); + if (ipprot == NULL) { /* raw IP is OK */ return 1; + } return ipprot->netns_ok; } @@ -1216,8 +1214,8 @@ EXPORT_SYMBOL(inet_sk_rebuild_header); static int inet_gso_send_check(struct sk_buff *skb) { - const struct iphdr *iph; const struct net_protocol *ops; + const struct iphdr *iph; int proto; int ihl; int err = -EINVAL; @@ -1236,7 +1234,7 @@ static int inet_gso_send_check(struct sk_buff *skb) __skb_pull(skb, ihl); skb_reset_transport_header(skb); iph = ip_hdr(skb); - proto = iph->protocol & (MAX_INET_PROTOS - 1); + proto = iph->protocol; err = -EPROTONOSUPPORT; rcu_read_lock(); @@ -1253,8 +1251,8 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, netdev_features_t features) { struct sk_buff *segs = ERR_PTR(-EINVAL); - struct iphdr *iph; const struct net_protocol *ops; + struct iphdr *iph; int proto; int ihl; int id; @@ -1286,7 +1284,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, skb_reset_transport_header(skb); iph = ip_hdr(skb); id = ntohs(iph->id); - proto = iph->protocol & (MAX_INET_PROTOS - 1); + proto = iph->protocol; segs = ERR_PTR(-EPROTONOSUPPORT); rcu_read_lock(); @@ -1340,7 +1338,7 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head, goto out; } - proto = iph->protocol & (MAX_INET_PROTOS - 1); + proto = iph->protocol; rcu_read_lock(); ops = rcu_dereference(inet_protos[proto]); @@ -1398,11 +1396,11 @@ out: static int inet_gro_complete(struct sk_buff *skb) { - const struct net_protocol *ops; + __be16 newlen = htons(skb->len - skb_network_offset(skb)); struct iphdr *iph = ip_hdr(skb); - int proto = iph->protocol & (MAX_INET_PROTOS - 1); + const struct net_protocol *ops; + int proto = iph->protocol; int err = -ENOSYS; - __be16 newlen = htons(skb->len - skb_network_offset(skb)); csum_replace2(&iph->check, iph->tot_len, newlen); iph->tot_len = newlen; diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index e1caa1abe5d1..49a74cc79dc8 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -637,12 +637,12 @@ EXPORT_SYMBOL(icmp_send); static void icmp_unreach(struct sk_buff *skb) { + const struct net_protocol *ipprot; const struct iphdr *iph; struct icmphdr *icmph; - int hash, protocol; - const struct net_protocol *ipprot; - u32 info = 0; struct net *net; + u32 info = 0; + int protocol; net = dev_net(skb_dst(skb)->dev); @@ -731,9 +731,8 @@ static void icmp_unreach(struct sk_buff *skb) */ raw_icmp_error(skb, protocol, info); - hash = protocol & (MAX_INET_PROTOS - 1); rcu_read_lock(); - ipprot = rcu_dereference(inet_protos[hash]); + ipprot = rcu_dereference(inet_protos[protocol]); if (ipprot && ipprot->err_handler) ipprot->err_handler(skb, info); rcu_read_unlock(); diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 8590144ca330..c4fe1d271131 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -198,14 +198,13 @@ static int ip_local_deliver_finish(struct sk_buff *skb) rcu_read_lock(); { int protocol = ip_hdr(skb)->protocol; - int hash, raw; const struct net_protocol *ipprot; + int raw; resubmit: raw = raw_local_deliver(skb, protocol); - hash = protocol & (MAX_INET_PROTOS - 1); - ipprot = rcu_dereference(inet_protos[hash]); + ipprot = rcu_dereference(inet_protos[protocol]); if (ipprot != NULL) { int ret; diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c index 9ae5c01cd0b2..8918eff1426d 100644 --- a/net/ipv4/protocol.c +++ b/net/ipv4/protocol.c @@ -36,9 +36,7 @@ const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly; int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol) { - int hash = protocol & (MAX_INET_PROTOS - 1); - - return !cmpxchg((const struct net_protocol **)&inet_protos[hash], + return !cmpxchg((const struct net_protocol **)&inet_protos[protocol], NULL, prot) ? 0 : -1; } EXPORT_SYMBOL(inet_add_protocol); @@ -49,9 +47,9 @@ EXPORT_SYMBOL(inet_add_protocol); int inet_del_protocol(const struct net_protocol *prot, unsigned char protocol) { - int ret, hash = protocol & (MAX_INET_PROTOS - 1); + int ret; - ret = (cmpxchg((const struct net_protocol **)&inet_protos[hash], + ret = (cmpxchg((const struct net_protocol **)&inet_protos[protocol], prot, NULL) == prot) ? 0 : -1; synchronize_net(); diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 5247d5c211f9..c7da1422cbde 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -600,9 +600,8 @@ static void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info) { const struct inet6_protocol *ipprot; int inner_offset; - int hash; - u8 nexthdr; __be16 frag_off; + u8 nexthdr; if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) return; @@ -629,10 +628,8 @@ static void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info) --ANK (980726) */ - hash = nexthdr & (MAX_INET_PROTOS - 1); - rcu_read_lock(); - ipprot = rcu_dereference(inet6_protos[hash]); + ipprot = rcu_dereference(inet6_protos[nexthdr]); if (ipprot && ipprot->err_handler) ipprot->err_handler(skb, NULL, type, code, inner_offset, info); rcu_read_unlock(); diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 21a15dfe4a9e..5ab923e51af3 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -168,13 +168,12 @@ drop: static int ip6_input_finish(struct sk_buff *skb) { + struct net *net = dev_net(skb_dst(skb)->dev); const struct inet6_protocol *ipprot; + struct inet6_dev *idev; unsigned int nhoff; int nexthdr; bool raw; - u8 hash; - struct inet6_dev *idev; - struct net *net = dev_net(skb_dst(skb)->dev); /* * Parse extension headers @@ -189,9 +188,7 @@ resubmit: nexthdr = skb_network_header(skb)[nhoff]; raw = raw6_local_deliver(skb, nexthdr); - - hash = nexthdr & (MAX_INET_PROTOS - 1); - if ((ipprot = rcu_dereference(inet6_protos[hash])) != NULL) { + if ((ipprot = rcu_dereference(inet6_protos[nexthdr])) != NULL) { int ret; if (ipprot->flags & INET6_PROTO_FINAL) { diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c index 9a7978fdc02a..053082dfc93e 100644 --- a/net/ipv6/protocol.c +++ b/net/ipv6/protocol.c @@ -29,9 +29,7 @@ const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS] __read_mostly; int inet6_add_protocol(const struct inet6_protocol *prot, unsigned char protocol) { - int hash = protocol & (MAX_INET_PROTOS - 1); - - return !cmpxchg((const struct inet6_protocol **)&inet6_protos[hash], + return !cmpxchg((const struct inet6_protocol **)&inet6_protos[protocol], NULL, prot) ? 0 : -1; } EXPORT_SYMBOL(inet6_add_protocol); @@ -42,9 +40,9 @@ EXPORT_SYMBOL(inet6_add_protocol); int inet6_del_protocol(const struct inet6_protocol *prot, unsigned char protocol) { - int ret, hash = protocol & (MAX_INET_PROTOS - 1); + int ret; - ret = (cmpxchg((const struct inet6_protocol **)&inet6_protos[hash], + ret = (cmpxchg((const struct inet6_protocol **)&inet6_protos[protocol], prot, NULL) == prot) ? 0 : -1; synchronize_net(); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 43b0042f15f4..b5c1dcb27737 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -165,7 +165,7 @@ static bool ipv6_raw_deliver(struct sk_buff *skb, int nexthdr) saddr = &ipv6_hdr(skb)->saddr; daddr = saddr + 1; - hash = nexthdr & (MAX_INET_PROTOS - 1); + hash = nexthdr & (RAW_HTABLE_SIZE - 1); read_lock(&raw_v6_hashinfo.lock); sk = sk_head(&raw_v6_hashinfo.ht[hash]); @@ -229,7 +229,7 @@ bool raw6_local_deliver(struct sk_buff *skb, int nexthdr) { struct sock *raw_sk; - raw_sk = sk_head(&raw_v6_hashinfo.ht[nexthdr & (MAX_INET_PROTOS - 1)]); + raw_sk = sk_head(&raw_v6_hashinfo.ht[nexthdr & (RAW_HTABLE_SIZE - 1)]); if (raw_sk && !ipv6_raw_deliver(skb, nexthdr)) raw_sk = NULL; -- cgit v1.2.3 From 210d4bc8a3128e3e61ac3bf4657114f8e6450e2a Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 9 May 2012 07:40:40 +1000 Subject: twl4030_charger: Add backup-battery charging This allows a voltage and current (bb_uvolts and bb_uamps) to be specified in the platform_data, and charging of the backup battery will be enabled with those specification. As it is not possible to monitor the backup battery at all there is no new device created to represent it. Signed-off-by: NeilBrown Signed-off-by: Anton Vorontsov --- drivers/power/twl4030_charger.c | 59 +++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/twl.h | 2 ++ 2 files changed, 61 insertions(+) (limited to 'include') diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 3e6e99101106..0511610b235f 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -28,6 +28,7 @@ #define TWL4030_BCIVBUS 0x0c #define TWL4030_BCIMFSTS4 0x10 #define TWL4030_BCICTL1 0x23 +#define TWL4030_BB_CFG 0x12 #define TWL4030_BCIAUTOWEN BIT(5) #define TWL4030_CONFIG_DONE BIT(4) @@ -37,6 +38,17 @@ #define TWL4030_USBFASTMCHG BIT(2) #define TWL4030_STS_VBUS BIT(7) #define TWL4030_STS_USB_ID BIT(2) +#define TWL4030_BBCHEN BIT(4) +#define TWL4030_BBSEL_MASK 0b1100 +#define TWL4030_BBSEL_2V5 0b0000 +#define TWL4030_BBSEL_3V0 0b0100 +#define TWL4030_BBSEL_3V1 0b1000 +#define TWL4030_BBSEL_3V2 0b1100 +#define TWL4030_BBISEL_MASK 0b11 +#define TWL4030_BBISEL_25uA 0b00 +#define TWL4030_BBISEL_150uA 0b01 +#define TWL4030_BBISEL_500uA 0b10 +#define TWL4030_BBISEL_1000uA 0b11 /* BCI interrupts */ #define TWL4030_WOVF BIT(0) /* Watchdog overflow */ @@ -201,6 +213,49 @@ static int twl4030_charger_enable_ac(bool enable) return ret; } +/* + * Enable/Disable charging of Backup Battery. + */ +static int twl4030_charger_enable_backup(int uvolt, int uamp) +{ + int ret; + u8 flags; + + if (uvolt < 2500000 || + uamp < 25) { + /* disable charging of backup battery */ + ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER, + TWL4030_BBCHEN, 0, TWL4030_BB_CFG); + return ret; + } + + flags = TWL4030_BBCHEN; + if (uvolt >= 3200000) + flags |= TWL4030_BBSEL_3V2; + else if (uvolt >= 3100000) + flags |= TWL4030_BBSEL_3V1; + else if (uvolt >= 3000000) + flags |= TWL4030_BBSEL_3V0; + else + flags |= TWL4030_BBSEL_2V5; + + if (uamp >= 1000) + flags |= TWL4030_BBISEL_1000uA; + else if (uamp >= 500) + flags |= TWL4030_BBISEL_500uA; + else if (uamp >= 150) + flags |= TWL4030_BBISEL_150uA; + else + flags |= TWL4030_BBISEL_25uA; + + ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER, + TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK, + flags, + TWL4030_BB_CFG); + + return ret; +} + /* * TWL4030 CHG_PRES (AC charger presence) events */ @@ -424,6 +479,7 @@ static enum power_supply_property twl4030_charger_props[] = { static int __init twl4030_bci_probe(struct platform_device *pdev) { struct twl4030_bci *bci; + struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data; int ret; u32 reg; @@ -503,6 +559,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) twl4030_charger_enable_ac(true); twl4030_charger_enable_usb(bci, true); + twl4030_charger_enable_backup(pdata->bb_uvolt, + pdata->bb_uamp); return 0; @@ -531,6 +589,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) twl4030_charger_enable_ac(false); twl4030_charger_enable_usb(bci, false); + twl4030_charger_enable_backup(0, 0); /* mask interrupts */ twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 3993477103a5..8eec4403b170 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -555,6 +555,8 @@ struct twl4030_clock_init_data { struct twl4030_bci_platform_data { int *battery_tmp_tbl; unsigned int tblsize; + int bb_uvolt; /* voltage to charge backup battery */ + int bb_uamp; /* current for backup battery charging */ }; /* TWL4030_GPIO_MAX (18) GPIOs, with interrupts */ -- cgit v1.2.3 From 41063e9dd11956f2d285e12e4342e1d232ba0ea2 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 19 Jun 2012 21:22:05 -0700 Subject: ipv4: Early TCP socket demux. Input packet processing for local sockets involves two major demuxes. One for the route and one for the socket. But we can optimize this down to one demux for certain kinds of local sockets. Currently we only do this for established TCP sockets, but it could at least in theory be expanded to other kinds of connections. If a TCP socket is established then it's identity is fully specified. This means that whatever input route was used during the three-way handshake must work equally well for the rest of the connection since the keys will not change. Once we move to established state, we cache the receive packet's input route to use later. Like the existing cached route in sk->sk_dst_cache used for output packets, we have to check for route invalidations using dst->obsolete and dst->ops->check(). Early demux occurs outside of a socket locked section, so when a route invalidation occurs we defer the fixup of sk->sk_rx_dst until we are actually inside of established state packet processing and thus have the socket locked. Signed-off-by: David S. Miller --- include/net/inet_hashtables.h | 4 ++-- include/net/protocol.h | 1 + include/net/sock.h | 2 ++ include/net/tcp.h | 1 + net/core/sock.c | 5 +++++ net/ipv4/af_inet.c | 18 +++++++++-------- net/ipv4/ip_input.c | 39 ++++++++++++++++++++++++------------ net/ipv4/tcp_input.c | 16 ++++++++++++++- net/ipv4/tcp_ipv4.c | 46 +++++++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_minisocks.c | 2 ++ 10 files changed, 110 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index 808fc5f76b03..54be0287eb98 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -379,10 +379,10 @@ static inline struct sock *__inet_lookup_skb(struct inet_hashinfo *hashinfo, const __be16 sport, const __be16 dport) { - struct sock *sk; + struct sock *sk = skb_steal_sock(skb); const struct iphdr *iph = ip_hdr(skb); - if (unlikely(sk = skb_steal_sock(skb))) + if (sk) return sk; else return __inet_lookup(dev_net(skb_dst(skb)->dev), hashinfo, diff --git a/include/net/protocol.h b/include/net/protocol.h index a1b1b530c338..967b926cbfb1 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -37,6 +37,7 @@ /* This is used to register protocols. */ struct net_protocol { + int (*early_demux)(struct sk_buff *skb); int (*handler)(struct sk_buff *skb); void (*err_handler)(struct sk_buff *skb, u32 info); int (*gso_send_check)(struct sk_buff *skb); diff --git a/include/net/sock.h b/include/net/sock.h index 4a4521699563..87b424ae750a 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -319,6 +319,7 @@ struct sock { unsigned long sk_flags; struct dst_entry *sk_dst_cache; spinlock_t sk_dst_lock; + struct dst_entry *sk_rx_dst; atomic_t sk_wmem_alloc; atomic_t sk_omem_alloc; int sk_sndbuf; @@ -1426,6 +1427,7 @@ extern struct sk_buff *sock_rmalloc(struct sock *sk, gfp_t priority); extern void sock_wfree(struct sk_buff *skb); extern void sock_rfree(struct sk_buff *skb); +extern void sock_edemux(struct sk_buff *skb); extern int sock_setsockopt(struct socket *sock, int level, int op, char __user *optval, diff --git a/include/net/tcp.h b/include/net/tcp.h index 9332f342259a..6660ffc4963d 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -325,6 +325,7 @@ extern void tcp_v4_err(struct sk_buff *skb, u32); extern void tcp_shutdown (struct sock *sk, int how); +extern int tcp_v4_early_demux(struct sk_buff *skb); extern int tcp_v4_rcv(struct sk_buff *skb); extern struct inet_peer *tcp_v4_get_peer(struct sock *sk); diff --git a/net/core/sock.c b/net/core/sock.c index 9e5b71fda6ec..929bdcc2383b 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1465,6 +1465,11 @@ void sock_rfree(struct sk_buff *skb) } EXPORT_SYMBOL(sock_rfree); +void sock_edemux(struct sk_buff *skb) +{ + sock_put(skb->sk); +} +EXPORT_SYMBOL(sock_edemux); int sock_i_uid(struct sock *sk) { diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 85a3b1763136..07a02f6e9696 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -157,6 +157,7 @@ void inet_sock_destruct(struct sock *sk) kfree(rcu_dereference_protected(inet->inet_opt, 1)); dst_release(rcu_dereference_check(sk->sk_dst_cache, 1)); + dst_release(sk->sk_rx_dst); sk_refcnt_debug_dec(sk); } EXPORT_SYMBOL(inet_sock_destruct); @@ -1518,14 +1519,15 @@ static const struct net_protocol igmp_protocol = { #endif static const struct net_protocol tcp_protocol = { - .handler = tcp_v4_rcv, - .err_handler = tcp_v4_err, - .gso_send_check = tcp_v4_gso_send_check, - .gso_segment = tcp_tso_segment, - .gro_receive = tcp4_gro_receive, - .gro_complete = tcp4_gro_complete, - .no_policy = 1, - .netns_ok = 1, + .early_demux = tcp_v4_early_demux, + .handler = tcp_v4_rcv, + .err_handler = tcp_v4_err, + .gso_send_check = tcp_v4_gso_send_check, + .gso_segment = tcp_tso_segment, + .gro_receive = tcp4_gro_receive, + .gro_complete = tcp4_gro_complete, + .no_policy = 1, + .netns_ok = 1, }; static const struct net_protocol udp_protocol = { diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index c4fe1d271131..93b092c9a394 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -323,19 +323,32 @@ static int ip_rcv_finish(struct sk_buff *skb) * how the packet travels inside Linux networking. */ if (skb_dst(skb) == NULL) { - int err = ip_route_input_noref(skb, iph->daddr, iph->saddr, - iph->tos, skb->dev); - if (unlikely(err)) { - if (err == -EHOSTUNREACH) - IP_INC_STATS_BH(dev_net(skb->dev), - IPSTATS_MIB_INADDRERRORS); - else if (err == -ENETUNREACH) - IP_INC_STATS_BH(dev_net(skb->dev), - IPSTATS_MIB_INNOROUTES); - else if (err == -EXDEV) - NET_INC_STATS_BH(dev_net(skb->dev), - LINUX_MIB_IPRPFILTER); - goto drop; + const struct net_protocol *ipprot; + int protocol = iph->protocol; + int err; + + 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 (err) { + err = ip_route_input_noref(skb, iph->daddr, iph->saddr, + iph->tos, skb->dev); + if (unlikely(err)) { + if (err == -EHOSTUNREACH) + IP_INC_STATS_BH(dev_net(skb->dev), + IPSTATS_MIB_INADDRERRORS); + else if (err == -ENETUNREACH) + IP_INC_STATS_BH(dev_net(skb->dev), + IPSTATS_MIB_INNOROUTES); + else if (err == -EXDEV) + NET_INC_STATS_BH(dev_net(skb->dev), + LINUX_MIB_IPRPFILTER); + goto drop; + } } } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index b224eb8bce8b..8416f8a68e65 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5518,6 +5518,18 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, struct tcp_sock *tp = tcp_sk(sk); int res; + if (sk->sk_rx_dst) { + struct dst_entry *dst = sk->sk_rx_dst; + if (unlikely(dst->obsolete)) { + if (dst->ops->check(dst, 0) == NULL) { + dst_release(dst); + sk->sk_rx_dst = NULL; + } + } + } + if (unlikely(sk->sk_rx_dst == NULL)) + sk->sk_rx_dst = dst_clone(skb_dst(skb)); + /* * Header prediction. * The code loosely follows the one in the famous @@ -5729,8 +5741,10 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb) tcp_set_state(sk, TCP_ESTABLISHED); - if (skb != NULL) + if (skb != NULL) { + sk->sk_rx_dst = dst_clone(skb_dst(skb)); security_inet_conn_established(sk, skb); + } /* Make sure socket is routed, for correct metrics. */ icsk->icsk_af_ops->rebuild_header(sk); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index fda2ca17135e..13857df1dae1 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1671,6 +1671,52 @@ csum_err: } EXPORT_SYMBOL(tcp_v4_do_rcv); +int tcp_v4_early_demux(struct sk_buff *skb) +{ + struct net *net = dev_net(skb->dev); + const struct iphdr *iph; + const struct tcphdr *th; + struct sock *sk; + int err; + + err = -ENOENT; + if (skb->pkt_type != PACKET_HOST) + goto out_err; + + if (!pskb_may_pull(skb, ip_hdrlen(skb) + sizeof(struct tcphdr))) + goto out_err; + + iph = ip_hdr(skb); + th = (struct tcphdr *) ((char *)iph + ip_hdrlen(skb)); + + if (th->doff < sizeof(struct tcphdr) / 4) + goto out_err; + + if (!pskb_may_pull(skb, ip_hdrlen(skb) + th->doff * 4)) + goto out_err; + + sk = __inet_lookup_established(net, &tcp_hashinfo, + iph->saddr, th->source, + iph->daddr, th->dest, + skb->dev->ifindex); + if (sk) { + skb->sk = sk; + skb->destructor = sock_edemux; + if (sk->sk_state != TCP_TIME_WAIT) { + struct dst_entry *dst = sk->sk_rx_dst; + if (dst) + dst = dst_check(dst, 0); + if (dst) { + skb_dst_set_noref(skb, dst); + err = 0; + } + } + } + +out_err: + return err; +} + /* * From tcp_input.c */ diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index cb015317c9f7..72b7c63b1a39 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -445,6 +445,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, struct tcp_sock *oldtp = tcp_sk(sk); struct tcp_cookie_values *oldcvp = oldtp->cookie_values; + newsk->sk_rx_dst = dst_clone(skb_dst(skb)); + /* TCP Cookie Transactions require space for the cookie pair, * as it differs for each connection. There is no need to * copy any s_data_payload stored at the original socket. -- cgit v1.2.3 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') 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: Sat, 9 Jun 2012 02:40:03 -0700 Subject: slab/mempolicy: always use local policy from interrupt context slab_node() could access current->mempolicy from interrupt context. However there's a race condition during exit where the mempolicy is first freed and then the pointer zeroed. Using this from interrupts seems bogus anyways. The interrupt will interrupt a random process and therefore get a random mempolicy. Many times, this will be idle's, which noone can change. Just disable this here and always use local for slab from interrupts. I also cleaned up the callers of slab_node a bit which always passed the same argument. I believe the original mempolicy code did that in fact, so it's likely a regression. v2: send version with correct logic v3: simplify. fix typo. Reported-by: Arun Sharma Cc: penberg@kernel.org Cc: cl@linux.com Signed-off-by: Andi Kleen [tdmackey@twitter.com: Rework control flow based on feedback from cl@linux.com, fix logic, and cleanup current task_struct reference] Acked-by: David Rientjes Acked-by: Christoph Lameter Acked-by: KOSAKI Motohiro Signed-off-by: David Mackey Signed-off-by: Pekka Enberg --- include/linux/mempolicy.h | 2 +- mm/mempolicy.c | 8 +++++++- mm/slab.c | 4 ++-- mm/slub.c | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h index 4aa42732e47f..95b738c7abff 100644 --- a/include/linux/mempolicy.h +++ b/include/linux/mempolicy.h @@ -215,7 +215,7 @@ extern struct zonelist *huge_zonelist(struct vm_area_struct *vma, extern bool init_nodemask_of_mempolicy(nodemask_t *mask); extern bool mempolicy_nodemask_intersects(struct task_struct *tsk, const nodemask_t *mask); -extern unsigned slab_node(struct mempolicy *policy); +extern unsigned slab_node(void); extern enum zone_type policy_zone; diff --git a/mm/mempolicy.c b/mm/mempolicy.c index f15c1b24ca18..cb0b230aa3f2 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -1602,8 +1602,14 @@ static unsigned interleave_nodes(struct mempolicy *policy) * task can change it's policy. The system default policy requires no * such protection. */ -unsigned slab_node(struct mempolicy *policy) +unsigned slab_node(void) { + struct mempolicy *policy; + + if (in_interrupt()) + return numa_node_id(); + + policy = current->mempolicy; if (!policy || policy->flags & MPOL_F_LOCAL) return numa_node_id(); diff --git a/mm/slab.c b/mm/slab.c index fc4a77446700..dd607a8e6706 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3310,7 +3310,7 @@ static void *alternate_node_alloc(struct kmem_cache *cachep, gfp_t flags) if (cpuset_do_slab_mem_spread() && (cachep->flags & SLAB_MEM_SPREAD)) nid_alloc = cpuset_slab_spread_node(); else if (current->mempolicy) - nid_alloc = slab_node(current->mempolicy); + nid_alloc = slab_node(); if (nid_alloc != nid_here) return ____cache_alloc_node(cachep, flags, nid_alloc); return NULL; @@ -3342,7 +3342,7 @@ static void *fallback_alloc(struct kmem_cache *cache, gfp_t flags) retry_cpuset: cpuset_mems_cookie = get_mems_allowed(); - zonelist = node_zonelist(slab_node(current->mempolicy), flags); + zonelist = node_zonelist(slab_node(), flags); retry: /* diff --git a/mm/slub.c b/mm/slub.c index 797271f5afb8..348fed1643f4 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1617,7 +1617,7 @@ static void *get_any_partial(struct kmem_cache *s, gfp_t flags, do { cpuset_mems_cookie = get_mems_allowed(); - zonelist = node_zonelist(slab_node(current->mempolicy), flags); + zonelist = node_zonelist(slab_node(), flags); for_each_zone_zonelist(zone, z, zonelist, high_zoneidx) { struct kmem_cache_node *n; -- cgit v1.2.3 From c5deac3c9b2284a64326e8799dfe7416bc619c02 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 12 Dec 2011 18:43:16 +0100 Subject: fbdev: sh_mobile_lcdc: Implement overlays support Signed-off-by: Laurent Pinchart --- .../sysfs-devices-platform-sh_mobile_lcdc_fb | 44 + drivers/video/sh_mobile_lcdcfb.c | 932 +++++++++++++++++++-- include/video/sh_mobile_lcdc.h | 7 + 3 files changed, 911 insertions(+), 72 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb b/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb new file mode 100644 index 000000000000..2107082426da --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb @@ -0,0 +1,44 @@ +What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_alpha +Date: May 2012 +Contact: Laurent Pinchart +Description: + This file is only available on fb[0-9] devices corresponding + to overlay planes. + + Stores the alpha blending value for the overlay. Values range + from 0 (transparent) to 255 (opaque). The value is ignored if + the mode is not set to Alpha Blending. + +What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_mode +Date: May 2012 +Contact: Laurent Pinchart +Description: + This file is only available on fb[0-9] devices corresponding + to overlay planes. + + Selects the composition mode for the overlay. Possible values + are + + 0 - Alpha Blending + 1 - ROP3 + +What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_position +Date: May 2012 +Contact: Laurent Pinchart +Description: + This file is only available on fb[0-9] devices corresponding + to overlay planes. + + Stores the x,y overlay position on the display in pixels. The + position format is `[0-9]+,[0-9]+'. + +What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_rop3 +Date: May 2012 +Contact: Laurent Pinchart +Description: + This file is only available on fb[0-9] devices corresponding + to overlay planes. + + Stores the raster operation (ROP3) for the overlay. Values + range from 0 to 255. The value is ignored if the mode is not + set to ROP3. diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 799f87171e04..98e81b31fbc4 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -32,12 +33,176 @@ #include "sh_mobile_lcdcfb.h" +/* ---------------------------------------------------------------------------- + * Overlay register definitions + */ + +#define LDBCR 0xb00 +#define LDBCR_UPC(n) (1 << ((n) + 16)) +#define LDBCR_UPF(n) (1 << ((n) + 8)) +#define LDBCR_UPD(n) (1 << ((n) + 0)) +#define LDBnBSIFR(n) (0xb20 + (n) * 0x20 + 0x00) +#define LDBBSIFR_EN (1 << 31) +#define LDBBSIFR_VS (1 << 29) +#define LDBBSIFR_BRSEL (1 << 28) +#define LDBBSIFR_MX (1 << 27) +#define LDBBSIFR_MY (1 << 26) +#define LDBBSIFR_CV3 (3 << 24) +#define LDBBSIFR_CV2 (2 << 24) +#define LDBBSIFR_CV1 (1 << 24) +#define LDBBSIFR_CV0 (0 << 24) +#define LDBBSIFR_CV_MASK (3 << 24) +#define LDBBSIFR_LAY_MASK (0xff << 16) +#define LDBBSIFR_LAY_SHIFT 16 +#define LDBBSIFR_ROP3_MASK (0xff << 16) +#define LDBBSIFR_ROP3_SHIFT 16 +#define LDBBSIFR_AL_PL8 (3 << 14) +#define LDBBSIFR_AL_PL1 (2 << 14) +#define LDBBSIFR_AL_PK (1 << 14) +#define LDBBSIFR_AL_1 (0 << 14) +#define LDBBSIFR_AL_MASK (3 << 14) +#define LDBBSIFR_SWPL (1 << 10) +#define LDBBSIFR_SWPW (1 << 9) +#define LDBBSIFR_SWPB (1 << 8) +#define LDBBSIFR_RY (1 << 7) +#define LDBBSIFR_CHRR_420 (2 << 0) +#define LDBBSIFR_CHRR_422 (1 << 0) +#define LDBBSIFR_CHRR_444 (0 << 0) +#define LDBBSIFR_RPKF_ARGB32 (0x00 << 0) +#define LDBBSIFR_RPKF_RGB16 (0x03 << 0) +#define LDBBSIFR_RPKF_RGB24 (0x0b << 0) +#define LDBBSIFR_RPKF_MASK (0x1f << 0) +#define LDBnBSSZR(n) (0xb20 + (n) * 0x20 + 0x04) +#define LDBBSSZR_BVSS_MASK (0xfff << 16) +#define LDBBSSZR_BVSS_SHIFT 16 +#define LDBBSSZR_BHSS_MASK (0xfff << 0) +#define LDBBSSZR_BHSS_SHIFT 0 +#define LDBnBLOCR(n) (0xb20 + (n) * 0x20 + 0x08) +#define LDBBLOCR_CVLC_MASK (0xfff << 16) +#define LDBBLOCR_CVLC_SHIFT 16 +#define LDBBLOCR_CHLC_MASK (0xfff << 0) +#define LDBBLOCR_CHLC_SHIFT 0 +#define LDBnBSMWR(n) (0xb20 + (n) * 0x20 + 0x0c) +#define LDBBSMWR_BSMWA_MASK (0xffff << 16) +#define LDBBSMWR_BSMWA_SHIFT 16 +#define LDBBSMWR_BSMW_MASK (0xffff << 0) +#define LDBBSMWR_BSMW_SHIFT 0 +#define LDBnBSAYR(n) (0xb20 + (n) * 0x20 + 0x10) +#define LDBBSAYR_FG1A_MASK (0xff << 24) +#define LDBBSAYR_FG1A_SHIFT 24 +#define LDBBSAYR_FG1R_MASK (0xff << 16) +#define LDBBSAYR_FG1R_SHIFT 16 +#define LDBBSAYR_FG1G_MASK (0xff << 8) +#define LDBBSAYR_FG1G_SHIFT 8 +#define LDBBSAYR_FG1B_MASK (0xff << 0) +#define LDBBSAYR_FG1B_SHIFT 0 +#define LDBnBSACR(n) (0xb20 + (n) * 0x20 + 0x14) +#define LDBBSACR_FG2A_MASK (0xff << 24) +#define LDBBSACR_FG2A_SHIFT 24 +#define LDBBSACR_FG2R_MASK (0xff << 16) +#define LDBBSACR_FG2R_SHIFT 16 +#define LDBBSACR_FG2G_MASK (0xff << 8) +#define LDBBSACR_FG2G_SHIFT 8 +#define LDBBSACR_FG2B_MASK (0xff << 0) +#define LDBBSACR_FG2B_SHIFT 0 +#define LDBnBSAAR(n) (0xb20 + (n) * 0x20 + 0x18) +#define LDBBSAAR_AP_MASK (0xff << 24) +#define LDBBSAAR_AP_SHIFT 24 +#define LDBBSAAR_R_MASK (0xff << 16) +#define LDBBSAAR_R_SHIFT 16 +#define LDBBSAAR_GY_MASK (0xff << 8) +#define LDBBSAAR_GY_SHIFT 8 +#define LDBBSAAR_B_MASK (0xff << 0) +#define LDBBSAAR_B_SHIFT 0 +#define LDBnBPPCR(n) (0xb20 + (n) * 0x20 + 0x1c) +#define LDBBPPCR_AP_MASK (0xff << 24) +#define LDBBPPCR_AP_SHIFT 24 +#define LDBBPPCR_R_MASK (0xff << 16) +#define LDBBPPCR_R_SHIFT 16 +#define LDBBPPCR_GY_MASK (0xff << 8) +#define LDBBPPCR_GY_SHIFT 8 +#define LDBBPPCR_B_MASK (0xff << 0) +#define LDBBPPCR_B_SHIFT 0 +#define LDBnBBGCL(n) (0xb10 + (n) * 0x04) +#define LDBBBGCL_BGA_MASK (0xff << 24) +#define LDBBBGCL_BGA_SHIFT 24 +#define LDBBBGCL_BGR_MASK (0xff << 16) +#define LDBBBGCL_BGR_SHIFT 16 +#define LDBBBGCL_BGG_MASK (0xff << 8) +#define LDBBBGCL_BGG_SHIFT 8 +#define LDBBBGCL_BGB_MASK (0xff << 0) +#define LDBBBGCL_BGB_SHIFT 0 + #define SIDE_B_OFFSET 0x1000 #define MIRROR_OFFSET 0x2000 #define MAX_XRES 1920 #define MAX_YRES 1080 +enum sh_mobile_lcdc_overlay_mode { + LCDC_OVERLAY_BLEND, + LCDC_OVERLAY_ROP3, +}; + +/* + * struct sh_mobile_lcdc_overlay - LCDC display overlay + * + * @channel: LCDC channel this overlay belongs to + * @cfg: Overlay configuration + * @info: Frame buffer device + * @index: Overlay index (0-3) + * @base: Overlay registers base address + * @enabled: True if the overlay is enabled + * @mode: Overlay blending mode (alpha blend or ROP3) + * @alpha: Global alpha blending value (0-255, for alpha blending mode) + * @rop3: Raster operation (for ROP3 mode) + * @fb_mem: Frame buffer virtual memory address + * @fb_size: Frame buffer size in bytes + * @dma_handle: Frame buffer DMA address + * @base_addr_y: Overlay base address (RGB or luma component) + * @base_addr_c: Overlay base address (chroma component) + * @pan_offset: Current pan offset in bytes + * @format: Current pixelf format + * @xres: Horizontal visible resolution + * @xres_virtual: Horizontal total resolution + * @yres: Vertical visible resolution + * @yres_virtual: Vertical total resolution + * @pitch: Overlay line pitch + * @pos_x: Horizontal overlay position + * @pos_y: Vertical overlay position + */ +struct sh_mobile_lcdc_overlay { + struct sh_mobile_lcdc_chan *channel; + + const struct sh_mobile_lcdc_overlay_cfg *cfg; + struct fb_info *info; + + unsigned int index; + unsigned long base; + + bool enabled; + enum sh_mobile_lcdc_overlay_mode mode; + unsigned int alpha; + unsigned int rop3; + + void *fb_mem; + unsigned long fb_size; + + dma_addr_t dma_handle; + unsigned long base_addr_y; + unsigned long base_addr_c; + unsigned long pan_offset; + + const struct sh_mobile_lcdc_format_info *format; + unsigned int xres; + unsigned int xres_virtual; + unsigned int yres; + unsigned int yres_virtual; + unsigned int pitch; + int pos_x; + int pos_y; +}; + struct sh_mobile_lcdc_priv { void __iomem *base; int irq; @@ -45,7 +210,10 @@ struct sh_mobile_lcdc_priv { struct device *dev; struct clk *dot_clk; unsigned long lddckr; + struct sh_mobile_lcdc_chan ch[2]; + struct sh_mobile_lcdc_overlay overlays[4]; + struct notifier_block notifier; int started; int forced_fourcc; /* 2 channel LCDC must share fourcc setting */ @@ -141,6 +309,13 @@ static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan, return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]); } +static void lcdc_write_overlay(struct sh_mobile_lcdc_overlay *ovl, + int reg, unsigned long data) +{ + iowrite32(data, ovl->channel->lcdc->base + reg); + iowrite32(data, ovl->channel->lcdc->base + reg + SIDE_B_OFFSET); +} + static void lcdc_write(struct sh_mobile_lcdc_priv *priv, unsigned long reg_offs, unsigned long data) { @@ -685,6 +860,96 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) lcdc_write_chan(ch, LDHAJR, tmp); } +static void sh_mobile_lcdc_overlay_setup(struct sh_mobile_lcdc_overlay *ovl) +{ + u32 format = 0; + + if (!ovl->enabled) { + lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index)); + lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), 0); + lcdc_write(ovl->channel->lcdc, LDBCR, + LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index)); + return; + } + + ovl->base_addr_y = ovl->dma_handle; + ovl->base_addr_c = ovl->base_addr_y + ovl->xres + * ovl->yres_virtual; + + switch (ovl->mode) { + case LCDC_OVERLAY_BLEND: + format = LDBBSIFR_EN | (ovl->alpha << LDBBSIFR_LAY_SHIFT); + break; + + case LCDC_OVERLAY_ROP3: + format = LDBBSIFR_EN | LDBBSIFR_BRSEL + | (ovl->rop3 << LDBBSIFR_ROP3_SHIFT); + break; + } + + switch (ovl->format->fourcc) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV42: + format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW; + break; + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV24: + format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB; + break; + case V4L2_PIX_FMT_BGR32: + default: + format |= LDBBSIFR_SWPL; + break; + } + + switch (ovl->format->fourcc) { + case V4L2_PIX_FMT_RGB565: + format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16; + break; + case V4L2_PIX_FMT_BGR24: + format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24; + break; + case V4L2_PIX_FMT_BGR32: + format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32; + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420; + break; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422; + break; + case V4L2_PIX_FMT_NV24: + case V4L2_PIX_FMT_NV42: + format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444; + break; + } + + lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index)); + + lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), format); + + lcdc_write_overlay(ovl, LDBnBSSZR(ovl->index), + (ovl->yres << LDBBSSZR_BVSS_SHIFT) | + (ovl->xres << LDBBSSZR_BHSS_SHIFT)); + lcdc_write_overlay(ovl, LDBnBLOCR(ovl->index), + (ovl->pos_y << LDBBLOCR_CVLC_SHIFT) | + (ovl->pos_x << LDBBLOCR_CHLC_SHIFT)); + lcdc_write_overlay(ovl, LDBnBSMWR(ovl->index), + ovl->pitch << LDBBSMWR_BSMW_SHIFT); + + lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y); + lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c); + + lcdc_write(ovl->channel->lcdc, LDBCR, + LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index)); +} + /* * __sh_mobile_lcdc_start - Configure and start the LCDC * @priv: LCDC device @@ -892,6 +1157,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) } } + for (k = 0; k < ARRAY_SIZE(priv->overlays); ++k) { + struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[k]; + sh_mobile_lcdc_overlay_setup(ovl); + } + /* Start the LCDC. */ __sh_mobile_lcdc_start(priv); @@ -975,8 +1245,506 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) sh_mobile_lcdc_clk_off(priv); } +static int __sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + if (var->xres > MAX_XRES || var->yres > MAX_YRES) + return -EINVAL; + + /* Make sure the virtual resolution is at least as big as the visible + * resolution. + */ + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; + + if (sh_mobile_format_is_fourcc(var)) { + const struct sh_mobile_lcdc_format_info *format; + + format = sh_mobile_format_info(var->grayscale); + if (format == NULL) + return -EINVAL; + var->bits_per_pixel = format->bpp; + + /* Default to RGB and JPEG color-spaces for RGB and YUV formats + * respectively. + */ + if (!format->yuv) + var->colorspace = V4L2_COLORSPACE_SRGB; + else if (var->colorspace != V4L2_COLORSPACE_REC709) + var->colorspace = V4L2_COLORSPACE_JPEG; + } else { + if (var->bits_per_pixel <= 16) { /* RGB 565 */ + var->bits_per_pixel = 16; + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + } else if (var->bits_per_pixel <= 24) { /* RGB 888 */ + var->bits_per_pixel = 24; + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */ + var->bits_per_pixel = 32; + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + } else + return -EINVAL; + + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + var->transp.msb_right = 0; + } + + /* Make sure we don't exceed our allocated memory. */ + if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 > + info->fix.smem_len) + return -EINVAL; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Frame buffer operations - Overlays + */ + +static ssize_t +overlay_alpha_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + + return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->alpha); +} + +static ssize_t +overlay_alpha_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + unsigned int alpha; + char *endp; + + alpha = simple_strtoul(buf, &endp, 10); + if (isspace(*endp)) + endp++; + + if (endp - buf != count) + return -EINVAL; + + if (alpha > 255) + return -EINVAL; + + if (ovl->alpha != alpha) { + ovl->alpha = alpha; + + if (ovl->mode == LCDC_OVERLAY_BLEND && ovl->enabled) + sh_mobile_lcdc_overlay_setup(ovl); + } + + return count; +} + +static ssize_t +overlay_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + + return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->mode); +} + +static ssize_t +overlay_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + unsigned int mode; + char *endp; + + mode = simple_strtoul(buf, &endp, 10); + if (isspace(*endp)) + endp++; + + if (endp - buf != count) + return -EINVAL; + + if (mode != LCDC_OVERLAY_BLEND && mode != LCDC_OVERLAY_ROP3) + return -EINVAL; + + if (ovl->mode != mode) { + ovl->mode = mode; + + if (ovl->enabled) + sh_mobile_lcdc_overlay_setup(ovl); + } + + return count; +} + +static ssize_t +overlay_position_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + + return scnprintf(buf, PAGE_SIZE, "%d,%d\n", ovl->pos_x, ovl->pos_y); +} + +static ssize_t +overlay_position_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + char *endp; + int pos_x; + int pos_y; + + pos_x = simple_strtol(buf, &endp, 10); + if (*endp != ',') + return -EINVAL; + + pos_y = simple_strtol(endp + 1, &endp, 10); + if (isspace(*endp)) + endp++; + + if (endp - buf != count) + return -EINVAL; + + if (ovl->pos_x != pos_x || ovl->pos_y != pos_y) { + ovl->pos_x = pos_x; + ovl->pos_y = pos_y; + + if (ovl->enabled) + sh_mobile_lcdc_overlay_setup(ovl); + } + + return count; +} + +static ssize_t +overlay_rop3_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + + return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->rop3); +} + +static ssize_t +overlay_rop3_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sh_mobile_lcdc_overlay *ovl = info->par; + unsigned int rop3; + char *endp; + + rop3 = !!simple_strtoul(buf, &endp, 10); + if (isspace(*endp)) + endp++; + + if (endp - buf != count) + return -EINVAL; + + if (rop3 > 255) + return -EINVAL; + + if (ovl->rop3 != rop3) { + ovl->rop3 = rop3; + + if (ovl->mode == LCDC_OVERLAY_ROP3 && ovl->enabled) + sh_mobile_lcdc_overlay_setup(ovl); + } + + return count; +} + +static const struct device_attribute overlay_sysfs_attrs[] = { + __ATTR(ovl_alpha, S_IRUGO|S_IWUSR, + overlay_alpha_show, overlay_alpha_store), + __ATTR(ovl_mode, S_IRUGO|S_IWUSR, + overlay_mode_show, overlay_mode_store), + __ATTR(ovl_position, S_IRUGO|S_IWUSR, + overlay_position_show, overlay_position_store), + __ATTR(ovl_rop3, S_IRUGO|S_IWUSR, + overlay_rop3_show, overlay_rop3_store), +}; + +static const struct fb_fix_screeninfo sh_mobile_lcdc_overlay_fix = { + .id = "SH Mobile LCDC", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .accel = FB_ACCEL_NONE, + .xpanstep = 0, + .ypanstep = 1, + .ywrapstep = 0, + .capabilities = FB_CAP_FOURCC, +}; + +static int sh_mobile_lcdc_overlay_pan(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct sh_mobile_lcdc_overlay *ovl = info->par; + unsigned long base_addr_y; + unsigned long base_addr_c; + unsigned long pan_offset; + unsigned long c_offset; + + if (!ovl->format->yuv) + pan_offset = var->yoffset * ovl->pitch + + var->xoffset * (ovl->format->bpp / 8); + else + pan_offset = var->yoffset * ovl->pitch + var->xoffset; + + if (pan_offset == ovl->pan_offset) + return 0; /* No change, do nothing */ + + /* Set the source address for the next refresh */ + base_addr_y = ovl->dma_handle + pan_offset; + + ovl->base_addr_y = base_addr_y; + ovl->base_addr_c = base_addr_y; + + if (ovl->format->yuv) { + /* Set Y offset */ + c_offset = var->yoffset * ovl->pitch + * (ovl->format->bpp - 8) / 8; + base_addr_c = ovl->dma_handle + + ovl->xres * ovl->yres_virtual + + c_offset; + /* Set X offset */ + if (ovl->format->fourcc == V4L2_PIX_FMT_NV24) + base_addr_c += 2 * var->xoffset; + else + base_addr_c += var->xoffset; + + ovl->base_addr_c = base_addr_c; + } + + lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y); + lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c); + + ovl->pan_offset = pan_offset; + + return 0; +} + +static int sh_mobile_lcdc_overlay_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct sh_mobile_lcdc_overlay *ovl = info->par; + + switch (cmd) { + case FBIO_WAITFORVSYNC: + return sh_mobile_lcdc_wait_for_vsync(ovl->channel); + + default: + return -ENOIOCTLCMD; + } +} + +static int sh_mobile_lcdc_overlay_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + return __sh_mobile_lcdc_check_var(var, info); +} + +static int sh_mobile_lcdc_overlay_set_par(struct fb_info *info) +{ + struct sh_mobile_lcdc_overlay *ovl = info->par; + + ovl->format = + sh_mobile_format_info(sh_mobile_format_fourcc(&info->var)); + + ovl->xres = info->var.xres; + ovl->xres_virtual = info->var.xres_virtual; + ovl->yres = info->var.yres; + ovl->yres_virtual = info->var.yres_virtual; + + if (ovl->format->yuv) + ovl->pitch = info->var.xres; + else + ovl->pitch = info->var.xres * ovl->format->bpp / 8; + + sh_mobile_lcdc_overlay_setup(ovl); + + info->fix.line_length = ovl->pitch; + + if (sh_mobile_format_is_fourcc(&info->var)) { + info->fix.type = FB_TYPE_FOURCC; + info->fix.visual = FB_VISUAL_FOURCC; + } else { + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_TRUECOLOR; + } + + return 0; +} + +/* Overlay blanking. Disable the overlay when blanked. */ +static int sh_mobile_lcdc_overlay_blank(int blank, struct fb_info *info) +{ + struct sh_mobile_lcdc_overlay *ovl = info->par; + + ovl->enabled = !blank; + sh_mobile_lcdc_overlay_setup(ovl); + + /* Prevent the backlight from receiving a blanking event by returning + * a non-zero value. + */ + return 1; +} + +static struct fb_ops sh_mobile_lcdc_overlay_ops = { + .owner = THIS_MODULE, + .fb_read = fb_sys_read, + .fb_write = fb_sys_write, + .fb_fillrect = sys_fillrect, + .fb_copyarea = sys_copyarea, + .fb_imageblit = sys_imageblit, + .fb_blank = sh_mobile_lcdc_overlay_blank, + .fb_pan_display = sh_mobile_lcdc_overlay_pan, + .fb_ioctl = sh_mobile_lcdc_overlay_ioctl, + .fb_check_var = sh_mobile_lcdc_overlay_check_var, + .fb_set_par = sh_mobile_lcdc_overlay_set_par, +}; + +static void +sh_mobile_lcdc_overlay_fb_unregister(struct sh_mobile_lcdc_overlay *ovl) +{ + struct fb_info *info = ovl->info; + + if (info == NULL || info->dev == NULL) + return; + + unregister_framebuffer(ovl->info); +} + +static int __devinit +sh_mobile_lcdc_overlay_fb_register(struct sh_mobile_lcdc_overlay *ovl) +{ + struct sh_mobile_lcdc_priv *lcdc = ovl->channel->lcdc; + struct fb_info *info = ovl->info; + unsigned int i; + int ret; + + if (info == NULL) + return 0; + + ret = register_framebuffer(info); + if (ret < 0) + return ret; + + dev_info(lcdc->dev, "registered %s/overlay %u as %dx%d %dbpp.\n", + dev_name(lcdc->dev), ovl->index, info->var.xres, + info->var.yres, info->var.bits_per_pixel); + + for (i = 0; i < ARRAY_SIZE(overlay_sysfs_attrs); ++i) { + ret = device_create_file(info->dev, &overlay_sysfs_attrs[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +static void +sh_mobile_lcdc_overlay_fb_cleanup(struct sh_mobile_lcdc_overlay *ovl) +{ + struct fb_info *info = ovl->info; + + if (info == NULL || info->device == NULL) + return; + + framebuffer_release(info); +} + +static int __devinit +sh_mobile_lcdc_overlay_fb_init(struct sh_mobile_lcdc_overlay *ovl) +{ + struct sh_mobile_lcdc_priv *priv = ovl->channel->lcdc; + struct fb_var_screeninfo *var; + struct fb_info *info; + + /* Allocate and initialize the frame buffer device. */ + info = framebuffer_alloc(0, priv->dev); + if (info == NULL) { + dev_err(priv->dev, "unable to allocate fb_info\n"); + return -ENOMEM; + } + + ovl->info = info; + + info->flags = FBINFO_FLAG_DEFAULT; + info->fbops = &sh_mobile_lcdc_overlay_ops; + info->device = priv->dev; + info->screen_base = ovl->fb_mem; + info->par = ovl; + + /* Initialize fixed screen information. Restrict pan to 2 lines steps + * for NV12 and NV21. + */ + info->fix = sh_mobile_lcdc_overlay_fix; + snprintf(info->fix.id, sizeof(info->fix.id), + "SH Mobile LCDC Overlay %u", ovl->index); + info->fix.smem_start = ovl->dma_handle; + info->fix.smem_len = ovl->fb_size; + info->fix.line_length = ovl->pitch; + + if (ovl->format->yuv) + info->fix.visual = FB_VISUAL_FOURCC; + else + info->fix.visual = FB_VISUAL_TRUECOLOR; + + if (ovl->format->fourcc == V4L2_PIX_FMT_NV12 || + ovl->format->fourcc == V4L2_PIX_FMT_NV21) + info->fix.ypanstep = 2; + + /* Initialize variable screen information. */ + var = &info->var; + memset(var, 0, sizeof(*var)); + var->xres = ovl->xres; + var->yres = ovl->yres; + var->xres_virtual = ovl->xres_virtual; + var->yres_virtual = ovl->yres_virtual; + var->activate = FB_ACTIVATE_NOW; + + /* Use the legacy API by default for RGB formats, and the FOURCC API + * for YUV formats. + */ + if (!ovl->format->yuv) + var->bits_per_pixel = ovl->format->bpp; + else + var->grayscale = ovl->format->fourcc; + + return sh_mobile_lcdc_overlay_check_var(var, info); +} + /* ----------------------------------------------------------------------------- - * Frame buffer operations + * Frame buffer operations - main frame buffer */ static int sh_mobile_lcdc_setcolreg(u_int regno, @@ -1202,9 +1970,7 @@ static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, unsigned int best_xres = 0; unsigned int best_yres = 0; unsigned int i; - - if (var->xres > MAX_XRES || var->yres > MAX_YRES) - return -EINVAL; + int ret; /* If board code provides us with a list of available modes, make sure * we use one of them. Find the mode closest to the requested one. The @@ -1239,73 +2005,9 @@ static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, var->yres = best_yres; } - /* Make sure the virtual resolution is at least as big as the visible - * resolution. - */ - if (var->xres_virtual < var->xres) - var->xres_virtual = var->xres; - if (var->yres_virtual < var->yres) - var->yres_virtual = var->yres; - - if (sh_mobile_format_is_fourcc(var)) { - const struct sh_mobile_lcdc_format_info *format; - - format = sh_mobile_format_info(var->grayscale); - if (format == NULL) - return -EINVAL; - var->bits_per_pixel = format->bpp; - - /* Default to RGB and JPEG color-spaces for RGB and YUV formats - * respectively. - */ - if (!format->yuv) - var->colorspace = V4L2_COLORSPACE_SRGB; - else if (var->colorspace != V4L2_COLORSPACE_REC709) - var->colorspace = V4L2_COLORSPACE_JPEG; - } else { - if (var->bits_per_pixel <= 16) { /* RGB 565 */ - var->bits_per_pixel = 16; - var->red.offset = 11; - var->red.length = 5; - var->green.offset = 5; - var->green.length = 6; - var->blue.offset = 0; - var->blue.length = 5; - var->transp.offset = 0; - var->transp.length = 0; - } else if (var->bits_per_pixel <= 24) { /* RGB 888 */ - var->bits_per_pixel = 24; - var->red.offset = 16; - var->red.length = 8; - var->green.offset = 8; - var->green.length = 8; - var->blue.offset = 0; - var->blue.length = 8; - var->transp.offset = 0; - var->transp.length = 0; - } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */ - var->bits_per_pixel = 32; - var->red.offset = 16; - var->red.length = 8; - var->green.offset = 8; - var->green.length = 8; - var->blue.offset = 0; - var->blue.length = 8; - var->transp.offset = 24; - var->transp.length = 8; - } else - return -EINVAL; - - var->red.msb_right = 0; - var->green.msb_right = 0; - var->blue.msb_right = 0; - var->transp.msb_right = 0; - } - - /* Make sure we don't exceed our allocated memory. */ - if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 > - info->fix.smem_len) - return -EINVAL; + ret = __sh_mobile_lcdc_check_var(var, info); + if (ret < 0) + return ret; /* only accept the forced_fourcc for dual channel configurations */ if (p->forced_fourcc && @@ -1714,15 +2416,27 @@ static const struct fb_videomode default_720p __devinitconst = { static int sh_mobile_lcdc_remove(struct platform_device *pdev) { struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); - int i; + unsigned int i; fb_unregister_client(&priv->notifier); + for (i = 0; i < ARRAY_SIZE(priv->overlays); i++) + sh_mobile_lcdc_overlay_fb_unregister(&priv->overlays[i]); for (i = 0; i < ARRAY_SIZE(priv->ch); i++) sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]); sh_mobile_lcdc_stop(priv); + for (i = 0; i < ARRAY_SIZE(priv->overlays); i++) { + struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i]; + + sh_mobile_lcdc_overlay_fb_cleanup(ovl); + + if (ovl->fb_mem) + dma_free_coherent(&pdev->dev, ovl->fb_size, + ovl->fb_mem, ovl->dma_handle); + } + for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { struct sh_mobile_lcdc_chan *ch = &priv->ch[i]; @@ -1797,6 +2511,61 @@ static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan * return 0; } +static int __devinit +sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv, + struct sh_mobile_lcdc_overlay *ovl) +{ + const struct sh_mobile_lcdc_format_info *format; + int ret; + + if (ovl->cfg->fourcc == 0) + return 0; + + /* Validate the format. */ + format = sh_mobile_format_info(ovl->cfg->fourcc); + if (format == NULL) { + dev_err(priv->dev, "Invalid FOURCC %08x\n", ovl->cfg->fourcc); + return -EINVAL; + } + + ovl->enabled = false; + ovl->mode = LCDC_OVERLAY_BLEND; + ovl->alpha = 255; + ovl->rop3 = 0; + ovl->pos_x = 0; + ovl->pos_y = 0; + + /* The default Y virtual resolution is twice the panel size to allow for + * double-buffering. + */ + ovl->format = format; + ovl->xres = ovl->cfg->max_xres; + ovl->xres_virtual = ovl->xres; + ovl->yres = ovl->cfg->max_yres; + ovl->yres_virtual = ovl->yres * 2; + + if (!format->yuv) + ovl->pitch = ovl->xres * format->bpp / 8; + else + ovl->pitch = ovl->xres; + + /* Allocate frame buffer memory. */ + ovl->fb_size = ovl->cfg->max_xres * ovl->cfg->max_yres + * format->bpp / 8 * 2; + ovl->fb_mem = dma_alloc_coherent(priv->dev, ovl->fb_size, + &ovl->dma_handle, GFP_KERNEL); + if (!ovl->fb_mem) { + dev_err(priv->dev, "unable to allocate buffer\n"); + return -ENOMEM; + } + + ret = sh_mobile_lcdc_overlay_fb_init(ovl); + if (ret < 0) + return ret; + + return 0; +} + static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, struct sh_mobile_lcdc_chan *ch) @@ -2005,6 +2774,17 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) goto err1; } + for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) { + struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i]; + + ovl->cfg = &pdata->overlays[i]; + ovl->channel = &priv->ch[0]; + + error = sh_mobile_lcdc_overlay_init(priv, ovl); + if (error) + goto err1; + } + error = sh_mobile_lcdc_start(priv); if (error) { dev_err(&pdev->dev, "unable to start hardware\n"); @@ -2019,6 +2799,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) goto err1; } + for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) { + struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i]; + + error = sh_mobile_lcdc_overlay_fb_register(ovl); + if (error) + goto err1; + } + /* Failure ignored */ priv->notifier.notifier_call = sh_mobile_lcdc_notify; fb_register_client(&priv->notifier); diff --git a/include/video/sh_mobile_lcdc.h b/include/video/sh_mobile_lcdc.h index 7571b27a0ba1..ff43ffc1aab2 100644 --- a/include/video/sh_mobile_lcdc.h +++ b/include/video/sh_mobile_lcdc.h @@ -166,6 +166,12 @@ struct sh_mobile_lcdc_bl_info { int (*get_brightness)(void); }; +struct sh_mobile_lcdc_overlay_cfg { + int fourcc; + unsigned int max_xres; + unsigned int max_yres; +}; + struct sh_mobile_lcdc_chan_cfg { int chan; int fourcc; @@ -186,6 +192,7 @@ struct sh_mobile_lcdc_chan_cfg { struct sh_mobile_lcdc_info { int clock_source; struct sh_mobile_lcdc_chan_cfg ch[2]; + struct sh_mobile_lcdc_overlay_cfg overlays[4]; struct sh_mobile_meram_info *meram_dev; }; -- cgit v1.2.3 From b3c185a7614cd95ea9b68d89a8d1ee4227ee9018 Mon Sep 17 00:00:00 2001 From: Paul Mundt 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') 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 From f3761c3950bd2ad813095a240d6a3dcb885d2431 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 19 Jun 2012 19:31:48 +0100 Subject: ALSA: Add missing include of pcm.h to pcm_params.h There's a dependency but no #include. Signed-off-by: Mark Brown Signed-off-by: Takashi Iwai --- include/sound/pcm_params.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index f494f1e3c900..37ae12e0ab06 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -22,6 +22,8 @@ * */ +#include + int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var, int *dir); -- cgit v1.2.3 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') 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 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') 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 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') 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 From 9883ab229d61b884323f9186b1bd4a41373a491b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Jun 2012 20:11:41 +0200 Subject: ASoC: dmaengine-pcm: Rename and deprecate snd_dmaengine_pcm_pointer Currently the sound dmaengine pcm helper functions implement the pcm_pointer callback by trying to count the number of elapsed periods. This is done by advancing the stream position in the dmaengine callback by one period. Unfortunately there is no guarantee that the callback will be called for each elapsed period. It may be possible that under high system load it is only called once for multiple elapsed periods. This patch renames the current implementation and documents its shortcomings and that it should not be used anymore in new drivers. The next patch will introduce a new snd_dmaengine_pcm_pointer which will be implemented based on querying the current stream position from the dma device. Signed-off-by: Lars-Peter Clausen Acked-by Vinod Koul Acked-by: Dong Aisheng --- include/sound/dmaengine_pcm.h | 2 +- sound/soc/ep93xx/ep93xx-pcm.c | 2 +- sound/soc/fsl/imx-pcm-dma.c | 2 +- sound/soc/mxs/mxs-pcm.c | 2 +- sound/soc/soc-dmaengine-pcm.c | 10 +++++----- sound/soc/ux500/ux500_pcm.c | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index a8fcaa6d531f..ea5791583fed 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -38,7 +38,7 @@ void *snd_dmaengine_pcm_get_data(struct snd_pcm_substream *substream); int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream, const struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config); int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd); -snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream); +snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream); int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, dma_filter_fn filter_fn, void *filter_data); diff --git a/sound/soc/ep93xx/ep93xx-pcm.c b/sound/soc/ep93xx/ep93xx-pcm.c index 162dbb74f4cc..4eea98b42bc8 100644 --- a/sound/soc/ep93xx/ep93xx-pcm.c +++ b/sound/soc/ep93xx/ep93xx-pcm.c @@ -136,7 +136,7 @@ static struct snd_pcm_ops ep93xx_pcm_ops = { .hw_params = ep93xx_pcm_hw_params, .hw_free = ep93xx_pcm_hw_free, .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer, + .pointer = snd_dmaengine_pcm_pointer_no_residue, .mmap = ep93xx_pcm_mmap, }; diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index f3c0a5ef35c8..48f9d886f020 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -141,7 +141,7 @@ static struct snd_pcm_ops imx_pcm_ops = { .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_imx_pcm_hw_params, .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer, + .pointer = snd_dmaengine_pcm_pointer_no_residue, .mmap = snd_imx_pcm_mmap, }; diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c index 373dec90579f..f82d766cbf9e 100644 --- a/sound/soc/mxs/mxs-pcm.c +++ b/sound/soc/mxs/mxs-pcm.c @@ -141,7 +141,7 @@ static struct snd_pcm_ops mxs_pcm_ops = { .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_mxs_pcm_hw_params, .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer, + .pointer = snd_dmaengine_pcm_pointer_no_residue, .mmap = snd_mxs_pcm_mmap, }; diff --git a/sound/soc/soc-dmaengine-pcm.c b/sound/soc/soc-dmaengine-pcm.c index 475695234b3d..7c0877e3731c 100644 --- a/sound/soc/soc-dmaengine-pcm.c +++ b/sound/soc/soc-dmaengine-pcm.c @@ -200,18 +200,18 @@ int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd) EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger); /** - * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation + * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation * @substream: PCM substream * - * This function can be used as the PCM pointer callback for dmaengine based PCM - * driver implementations. + * This function is deprecated and should not be used by new drivers, as its + * results may be unreliable. */ -snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) +snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream) { struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); return bytes_to_frames(substream->runtime, prtd->pos); } -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer); +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue); static int dmaengine_pcm_request_channel(struct dmaengine_pcm_runtime_data *prtd, dma_filter_fn filter_fn, void *filter_data) diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c index 97d8e4de29c2..1a04e248453c 100644 --- a/sound/soc/ux500/ux500_pcm.c +++ b/sound/soc/ux500/ux500_pcm.c @@ -261,7 +261,7 @@ static struct snd_pcm_ops ux500_pcm_ops = { .hw_params = ux500_pcm_hw_params, .hw_free = ux500_pcm_hw_free, .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer, + .pointer = snd_dmaengine_pcm_pointer_no_residue, .mmap = ux500_pcm_mmap }; -- cgit v1.2.3 From 3528f27a5d4ac299e2d8cbe7297c1e9edd601ee6 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Jun 2012 20:11:42 +0200 Subject: ASoC: dmaengine-pcm: Add support for querying stream position from DMA driver Currently the sound dmaengine pcm helper functions implement the pcm_pointer callback by trying to count the number of elapsed periods. This is done by advancing the stream position in the dmaengine callback by one period. Unfortunately there is no guarantee that the callback will be called for each elapsed period. It may be possible that under high system load it is only called once for multiple elapsed periods. This patch addresses the issue by implementing support for querying the current stream position directly from the dmaengine driver. Since not all dmaengine drivers support reporting the stream position yet the old period counting implementation is kept for now. Furthermore the new mechanism allows to report the stream position with a sub-period granularity, given that the dmaengine driver supports this. Signed-off-by: Lars-Peter Clausen Acked-by: Vinod Koul Signed-off-by: Mark Brown --- include/sound/dmaengine_pcm.h | 1 + sound/soc/soc-dmaengine-pcm.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index ea5791583fed..b877334bbb0f 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -38,6 +38,7 @@ void *snd_dmaengine_pcm_get_data(struct snd_pcm_substream *substream); int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream, const struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config); int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd); +snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream); snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream); int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, diff --git a/sound/soc/soc-dmaengine-pcm.c b/sound/soc/soc-dmaengine-pcm.c index 7c0877e3731c..2995334d8000 100644 --- a/sound/soc/soc-dmaengine-pcm.c +++ b/sound/soc/soc-dmaengine-pcm.c @@ -30,6 +30,7 @@ struct dmaengine_pcm_runtime_data { struct dma_chan *dma_chan; + dma_cookie_t cookie; unsigned int pos; @@ -153,7 +154,7 @@ static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream) desc->callback = dmaengine_pcm_dma_complete; desc->callback_param = substream; - dmaengine_submit(desc); + prtd->cookie = dmaengine_submit(desc); return 0; } @@ -213,6 +214,32 @@ snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue); +/** + * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation + * @substream: PCM substream + * + * This function can be used as the PCM pointer callback for dmaengine based PCM + * driver implementations. + */ +snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + struct dma_tx_state state; + enum dma_status status; + unsigned int buf_size; + unsigned int pos = 0; + + status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state); + if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) { + buf_size = snd_pcm_lib_buffer_bytes(substream); + if (state.residue > 0 && state.residue <= buf_size) + pos = buf_size - state.residue; + } + + return bytes_to_frames(substream->runtime, pos); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer); + static int dmaengine_pcm_request_channel(struct dmaengine_pcm_runtime_data *prtd, dma_filter_fn filter_fn, void *filter_data) { -- cgit v1.2.3 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') 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 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') 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 From 924d37118f9e18825294b2012a10c6245d6c25e1 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Mon, 18 Jun 2012 19:15:50 -0700 Subject: pstore/ram: Probe as early as possible Registering the platform driver before module_init allows us to log oopses that happen during device probing. This requires changing module_init to postcore_initcall, and switching from platform_driver_probe to platform_driver_register because the platform device is not registered when the platform driver is registered; and because we use driver_register, now can't use create_bundle() (since it will try to register the same driver once again), so we have to switch to platform_device_register_data(). Also, some __init -> __devinit changes were needed. Overall, the registration logic is now much clearer, since we have only one driver registration point, and just an optional dummy device, which is created from the module parameters. Suggested-by: Colin Cross Signed-off-by: Anton Vorontsov Acked-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- fs/pstore/ram.c | 63 +++++++++++++++++++++++----------------------- fs/pstore/ram_core.c | 9 ++++--- include/linux/pstore_ram.h | 6 ++--- 3 files changed, 40 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index c7acf94ff475..0b36e91978e6 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -330,7 +330,7 @@ static int ramoops_init_prz(struct device *dev, struct ramoops_context *cxt, return 0; } -static int __init ramoops_probe(struct platform_device *pdev) +static int __devinit ramoops_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ramoops_platform_data *pdata = pdev->dev.platform_data; @@ -452,6 +452,7 @@ static int __exit ramoops_remove(struct platform_device *pdev) } static struct platform_driver ramoops_driver = { + .probe = ramoops_probe, .remove = __exit_p(ramoops_remove), .driver = { .name = "ramoops", @@ -459,46 +460,46 @@ static struct platform_driver ramoops_driver = { }, }; -static int __init ramoops_init(void) +static void ramoops_register_dummy(void) { - int ret; - ret = platform_driver_probe(&ramoops_driver, ramoops_probe); - if (ret == -ENODEV) { - /* - * If we didn't find a platform device, we use module parameters - * building platform data on the fly. - */ - pr_info("platform device not found, using module parameters\n"); - dummy_data = kzalloc(sizeof(struct ramoops_platform_data), - GFP_KERNEL); - if (!dummy_data) - return -ENOMEM; - dummy_data->mem_size = mem_size; - dummy_data->mem_address = mem_address; - dummy_data->record_size = record_size; - dummy_data->console_size = ramoops_console_size; - dummy_data->dump_oops = dump_oops; - dummy_data->ecc = ramoops_ecc; - dummy = platform_create_bundle(&ramoops_driver, ramoops_probe, - NULL, 0, dummy_data, - sizeof(struct ramoops_platform_data)); - - if (IS_ERR(dummy)) - ret = PTR_ERR(dummy); - else - ret = 0; + if (!mem_size) + return; + + pr_info("using module parameters\n"); + + dummy_data = kzalloc(sizeof(*dummy_data), GFP_KERNEL); + if (!dummy_data) { + pr_info("could not allocate pdata\n"); + return; } - return ret; + dummy_data->mem_size = mem_size; + dummy_data->mem_address = mem_address; + dummy_data->record_size = record_size; + dummy_data->console_size = ramoops_console_size; + dummy_data->dump_oops = dump_oops; + dummy_data->ecc = ramoops_ecc; + + dummy = platform_device_register_data(NULL, "ramoops", -1, + dummy_data, sizeof(struct ramoops_platform_data)); + if (IS_ERR(dummy)) { + pr_info("could not create platform device: %ld\n", + PTR_ERR(dummy)); + } +} + +static int __init ramoops_init(void) +{ + ramoops_register_dummy(); + return platform_driver_register(&ramoops_driver); } +postcore_initcall(ramoops_init); static void __exit ramoops_exit(void) { platform_driver_unregister(&ramoops_driver); kfree(dummy_data); } - -module_init(ramoops_init); module_exit(ramoops_exit); MODULE_LICENSE("GPL"); diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c index 0fd81611525c..26531856daf8 100644 --- a/fs/pstore/ram_core.c +++ b/fs/pstore/ram_core.c @@ -390,7 +390,8 @@ static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size, return 0; } -static int __init persistent_ram_post_init(struct persistent_ram_zone *prz, bool ecc) +static int __devinit persistent_ram_post_init(struct persistent_ram_zone *prz, + bool ecc) { int ret; @@ -436,9 +437,9 @@ void persistent_ram_free(struct persistent_ram_zone *prz) kfree(prz); } -struct persistent_ram_zone * __init persistent_ram_new(phys_addr_t start, - size_t size, - bool ecc) +struct persistent_ram_zone * __devinit persistent_ram_new(phys_addr_t start, + size_t size, + bool ecc) { struct persistent_ram_zone *prz; int ret = -ENOMEM; diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h index 2470bb591434..e681af92c04b 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h @@ -48,9 +48,9 @@ struct persistent_ram_zone { size_t old_log_size; }; -struct persistent_ram_zone * __init persistent_ram_new(phys_addr_t start, - size_t size, - bool ecc); +struct persistent_ram_zone * __devinit persistent_ram_new(phys_addr_t start, + size_t size, + bool ecc); void persistent_ram_free(struct persistent_ram_zone *prz); void persistent_ram_zap(struct persistent_ram_zone *prz); -- cgit v1.2.3 From 66572cfc30a4b764150c83ee5d842a3ce17991c9 Mon Sep 17 00:00:00 2001 From: Victor Goldenshtein Date: Thu, 21 Jun 2012 10:56:46 +0300 Subject: mac80211: add command to get current rssi Get current rssi (in dBm) from the driver/FW. Instead of reporting the signal received in the last rx packet, which might be inaccurate if rx traffic is low and beacon filtering is enabled, get the signal from the driver/FW. Signed-off-by: Victor Goldenshtein Signed-off-by: Johannes Berg --- include/net/mac80211.h | 5 +++++ net/mac80211/cfg.c | 21 +++++++++++++-------- net/mac80211/driver-ops.h | 15 +++++++++++++++ net/mac80211/driver-trace.h | 26 ++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d152f54064fd..f11c2f8b00c9 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2249,6 +2249,9 @@ enum ieee80211_rate_control_changed { * @get_et_strings: Ethtool API to get a set of strings to describe stats * and perhaps other supported types of ethtool data-sets. * + * @get_rssi: Get current signal strength in dBm, the function is optional + * and can sleep. + * */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); @@ -2388,6 +2391,8 @@ struct ieee80211_ops { void (*get_et_strings)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 sset, u8 *data); + int (*get_rssi)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, s8 *rssi_dbm); }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 03aff23c70fd..d0c8f78115cb 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -353,6 +353,7 @@ void sta_set_rate_info_tx(struct sta_info *sta, static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; + struct ieee80211_local *local = sdata->local; struct timespec uptime; sinfo->generation = sdata->local->sta_generation; @@ -388,7 +389,9 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) || (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) { sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG; - sinfo->signal = (s8)sta->last_signal; + if (!local->ops->get_rssi || + drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal)) + sinfo->signal = (s8)sta->last_signal; sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal); } @@ -517,7 +520,7 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy, * network device. */ - rcu_read_lock(); + mutex_lock(&local->sta_mtx); if (sdata->vif.type == NL80211_IFTYPE_STATION) { sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid); @@ -546,7 +549,7 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy, data[i] = (u8)sinfo.signal_avg; i++; } else { - list_for_each_entry_rcu(sta, &local->sta_list, list) { + list_for_each_entry(sta, &local->sta_list, list) { /* Make sure this station belongs to the proper dev */ if (sta->sdata->dev != dev) continue; @@ -603,7 +606,7 @@ do_survey: else data[i++] = -1LL; - rcu_read_unlock(); + mutex_unlock(&local->sta_mtx); if (WARN_ON(i != STA_STATS_LEN)) return; @@ -629,10 +632,11 @@ static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *mac, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; struct sta_info *sta; int ret = -ENOENT; - rcu_read_lock(); + mutex_lock(&local->sta_mtx); sta = sta_info_get_by_idx(sdata, idx); if (sta) { @@ -641,7 +645,7 @@ static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, sta_set_sinfo(sta, sinfo); } - rcu_read_unlock(); + mutex_unlock(&local->sta_mtx); return ret; } @@ -658,10 +662,11 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; struct sta_info *sta; int ret = -ENOENT; - rcu_read_lock(); + mutex_lock(&local->sta_mtx); sta = sta_info_get_bss(sdata, mac); if (sta) { @@ -669,7 +674,7 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, sta_set_sinfo(sta, sinfo); } - rcu_read_unlock(); + mutex_unlock(&local->sta_mtx); return ret; } diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 6d33a0c743ab..933026949df9 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -845,4 +845,19 @@ drv_allow_buffered_frames(struct ieee80211_local *local, more_data); trace_drv_return_void(local); } + +static inline int drv_get_rssi(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta, + s8 *rssi_dbm) +{ + int ret; + + might_sleep(); + + ret = local->ops->get_rssi(&local->hw, &sdata->vif, sta, rssi_dbm); + trace_drv_get_rssi(local, sta, *rssi_dbm, ret); + + return ret; +} #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 6de00b2c268c..a0f7d357884d 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -1218,6 +1218,32 @@ DEFINE_EVENT(release_evt, drv_allow_buffered_frames, TP_ARGS(local, sta, tids, num_frames, reason, more_data) ); +TRACE_EVENT(drv_get_rssi, + TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta, + s8 rssi, int ret), + + TP_ARGS(local, sta, rssi, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + STA_ENTRY + __field(s8, rssi) + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + STA_ASSIGN; + __entry->rssi = rssi; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT STA_PR_FMT " rssi:%d ret:%d", + LOCAL_PR_ARG, STA_PR_ARG, __entry->rssi, __entry->ret + ) +); + /* * Tracing for API calls that drivers call. */ -- cgit v1.2.3 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') 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 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') 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 From 3a9cf8efd7b64f26f1e0f02afb70382f90cc11ca Mon Sep 17 00:00:00 2001 From: Rajeev Kumar Date: Thu, 21 Jun 2012 15:54:51 +0530 Subject: ASoC: Add support for synopsys i2s controller as per ASoC framework. This patch add support for synopsys I2S controller as per the ASoC framework. Signed-off-by: Rajeev Kumar Signed-off-by: Mark Brown --- include/sound/designware_i2s.h | 69 +++++++ sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/dwc/Kconfig | 8 + sound/soc/dwc/Makefile | 3 + sound/soc/dwc/designware_i2s.c | 454 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 536 insertions(+) create mode 100644 include/sound/designware_i2s.h create mode 100644 sound/soc/dwc/Kconfig create mode 100644 sound/soc/dwc/Makefile create mode 100644 sound/soc/dwc/designware_i2s.c (limited to 'include') diff --git a/include/sound/designware_i2s.h b/include/sound/designware_i2s.h new file mode 100644 index 000000000000..26f406e0f673 --- /dev/null +++ b/include/sound/designware_i2s.h @@ -0,0 +1,69 @@ +/* + * Copyright (ST) 2012 Rajeev Kumar (rajeev-dlh.kumar@st.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SOUND_DESIGNWARE_I2S_H +#define __SOUND_DESIGNWARE_I2S_H + +#include +#include + +/* + * struct i2s_clk_config_data - represent i2s clk configuration data + * @chan_nr: number of channel + * @data_width: number of bits per sample (8/16/24/32 bit) + * @sample_rate: sampling frequency (8Khz, 16Khz, 32Khz, 44Khz, 48Khz) + */ +struct i2s_clk_config_data { + int chan_nr; + u32 data_width; + u32 sample_rate; +}; + +struct i2s_platform_data { + #define DWC_I2S_PLAY (1 << 0) + #define DWC_I2S_RECORD (1 << 1) + unsigned int cap; + int channel; + u32 snd_fmts; + u32 snd_rates; + + void *play_dma_data; + void *capture_dma_data; + bool (*filter)(struct dma_chan *chan, void *slave); + int (*i2s_clk_cfg)(struct i2s_clk_config_data *config); +}; + +struct i2s_dma_data { + void *data; + dma_addr_t addr; + u32 max_burst; + enum dma_slave_buswidth addr_width; + bool (*filter)(struct dma_chan *chan, void *slave); +}; + +/* I2S DMA registers */ +#define I2S_RXDMA 0x01C0 +#define I2S_TXDMA 0x01C8 + +#define TWO_CHANNEL_SUPPORT 2 /* up to 2.0 */ +#define FOUR_CHANNEL_SUPPORT 4 /* up to 3.1 */ +#define SIX_CHANNEL_SUPPORT 6 /* up to 5.1 */ +#define EIGHT_CHANNEL_SUPPORT 8 /* up to 7.1 */ + +#endif /* __SOUND_DESIGNWARE_I2S_H */ diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 40b2ad1bb1cd..c5de0a84566f 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -33,6 +33,7 @@ source "sound/soc/atmel/Kconfig" source "sound/soc/au1x/Kconfig" source "sound/soc/blackfin/Kconfig" source "sound/soc/davinci/Kconfig" +source "sound/soc/dwc/Kconfig" source "sound/soc/ep93xx/Kconfig" source "sound/soc/fsl/Kconfig" source "sound/soc/jz4740/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 70990f4017f4..00a555a743b6 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_SND_SOC) += atmel/ obj-$(CONFIG_SND_SOC) += au1x/ obj-$(CONFIG_SND_SOC) += blackfin/ obj-$(CONFIG_SND_SOC) += davinci/ +obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += ep93xx/ obj-$(CONFIG_SND_SOC) += fsl/ obj-$(CONFIG_SND_SOC) += jz4740/ diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig new file mode 100644 index 000000000000..93e9fc33560c --- /dev/null +++ b/sound/soc/dwc/Kconfig @@ -0,0 +1,8 @@ +config SND_DESIGNWARE_I2S + tristate "Synopsys I2S Device Driver" + help + Say Y or M if you want to add support for I2S driver for + Synopsys desigwnware I2S device. The device supports upto + maximum of 8 channels each for play and record. + + diff --git a/sound/soc/dwc/Makefile b/sound/soc/dwc/Makefile new file mode 100644 index 000000000000..319371f690f4 --- /dev/null +++ b/sound/soc/dwc/Makefile @@ -0,0 +1,3 @@ +# SYNOPSYS Platform Support +obj-$(CONFIG_SND_DESIGNWARE_I2S) += designware_i2s.o + diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c new file mode 100644 index 000000000000..e667e2b45e67 --- /dev/null +++ b/sound/soc/dwc/designware_i2s.c @@ -0,0 +1,454 @@ +/* + * ALSA SoC Synopsys I2S Audio Layer + * + * sound/soc/spear/designware_i2s.c + * + * Copyright (C) 2010 ST Microelectronics + * Rajeev Kumar + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* common register for all channel */ +#define IER 0x000 +#define IRER 0x004 +#define ITER 0x008 +#define CER 0x00C +#define CCR 0x010 +#define RXFFR 0x014 +#define TXFFR 0x018 + +/* I2STxRxRegisters for all channels */ +#define LRBR_LTHR(x) (0x40 * x + 0x020) +#define RRBR_RTHR(x) (0x40 * x + 0x024) +#define RER(x) (0x40 * x + 0x028) +#define TER(x) (0x40 * x + 0x02C) +#define RCR(x) (0x40 * x + 0x030) +#define TCR(x) (0x40 * x + 0x034) +#define ISR(x) (0x40 * x + 0x038) +#define IMR(x) (0x40 * x + 0x03C) +#define ROR(x) (0x40 * x + 0x040) +#define TOR(x) (0x40 * x + 0x044) +#define RFCR(x) (0x40 * x + 0x048) +#define TFCR(x) (0x40 * x + 0x04C) +#define RFF(x) (0x40 * x + 0x050) +#define TFF(x) (0x40 * x + 0x054) + +/* I2SCOMPRegisters */ +#define I2S_COMP_PARAM_2 0x01F0 +#define I2S_COMP_PARAM_1 0x01F4 +#define I2S_COMP_VERSION 0x01F8 +#define I2S_COMP_TYPE 0x01FC + +#define MAX_CHANNEL_NUM 8 +#define MIN_CHANNEL_NUM 2 + +struct dw_i2s_dev { + void __iomem *i2s_base; + struct clk *clk; + int active; + unsigned int capability; + struct device *dev; + + /* data related to DMA transfers b/w i2s and DMAC */ + struct i2s_dma_data play_dma_data; + struct i2s_dma_data capture_dma_data; + struct i2s_clk_config_data config; + int (*i2s_clk_cfg)(struct i2s_clk_config_data *config); +}; + +static inline void i2s_write_reg(void *io_base, int reg, u32 val) +{ + writel(val, io_base + reg); +} + +static inline u32 i2s_read_reg(void *io_base, int reg) +{ + return readl(io_base + reg); +} + +static inline void i2s_disable_channels(struct dw_i2s_dev *dev, u32 stream) +{ + u32 i = 0; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = 0; i < 4; i++) + i2s_write_reg(dev->i2s_base, TER(i), 0); + } else { + for (i = 0; i < 4; i++) + i2s_write_reg(dev->i2s_base, RER(i), 0); + } +} + +static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream) +{ + u32 i = 0; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = 0; i < 4; i++) + i2s_write_reg(dev->i2s_base, TOR(i), 0); + } else { + for (i = 0; i < 4; i++) + i2s_write_reg(dev->i2s_base, ROR(i), 0); + } +} + +void i2s_start(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream) +{ + + i2s_write_reg(dev->i2s_base, IER, 1); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + i2s_write_reg(dev->i2s_base, ITER, 1); + else + i2s_write_reg(dev->i2s_base, IRER, 1); + + i2s_write_reg(dev->i2s_base, CER, 1); +} + +static void i2s_stop(struct dw_i2s_dev *dev, + struct snd_pcm_substream *substream) +{ + u32 i = 0, irq; + + i2s_clear_irqs(dev, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + i2s_write_reg(dev->i2s_base, ITER, 0); + + for (i = 0; i < 4; i++) { + irq = i2s_read_reg(dev->i2s_base, IMR(i)); + i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30); + } + } else { + i2s_write_reg(dev->i2s_base, IRER, 0); + + for (i = 0; i < 4; i++) { + irq = i2s_read_reg(dev->i2s_base, IMR(i)); + i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03); + } + } + + if (!dev->active) { + i2s_write_reg(dev->i2s_base, CER, 0); + i2s_write_reg(dev->i2s_base, IER, 0); + } +} + +static int dw_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); + struct i2s_dma_data *dma_data = NULL; + + if (!(dev->capability & DWC_I2S_RECORD) && + (substream->stream == SNDRV_PCM_STREAM_CAPTURE)) + return -EINVAL; + + if (!(dev->capability & DWC_I2S_PLAY) && + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) + return -EINVAL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dma_data = &dev->play_dma_data; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + dma_data = &dev->capture_dma_data; + + snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data); + + return 0; +} + +static int dw_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + struct i2s_clk_config_data *config = &dev->config; + u32 ccr, xfer_resolution, ch_reg, irq; + int ret; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + config->data_width = 16; + ccr = 0x00; + xfer_resolution = 0x02; + break; + + case SNDRV_PCM_FORMAT_S24_LE: + config->data_width = 24; + ccr = 0x08; + xfer_resolution = 0x04; + break; + + case SNDRV_PCM_FORMAT_S32_LE: + config->data_width = 32; + ccr = 0x10; + xfer_resolution = 0x05; + break; + + default: + dev_err(dev->dev, "designware-i2s: unsuppted PCM fmt"); + return -EINVAL; + } + + config->chan_nr = params_channels(params); + + switch (config->chan_nr) { + case EIGHT_CHANNEL_SUPPORT: + ch_reg = 3; + case SIX_CHANNEL_SUPPORT: + ch_reg = 2; + case FOUR_CHANNEL_SUPPORT: + ch_reg = 1; + case TWO_CHANNEL_SUPPORT: + ch_reg = 0; + break; + default: + dev_err(dev->dev, "channel not supported\n"); + } + + i2s_disable_channels(dev, substream->stream); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + i2s_write_reg(dev->i2s_base, TCR(ch_reg), xfer_resolution); + i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02); + irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); + i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30); + i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); + } else { + i2s_write_reg(dev->i2s_base, RCR(ch_reg), xfer_resolution); + i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07); + irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); + i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03); + i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); + } + + i2s_write_reg(dev->i2s_base, CCR, ccr); + + config->sample_rate = params_rate(params); + + if (!dev->i2s_clk_cfg) + return -EINVAL; + + ret = dev->i2s_clk_cfg(config); + if (ret < 0) { + dev_err(dev->dev, "runtime audio clk config fail\n"); + return ret; + } + + return 0; +} + +static void dw_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int dw_i2s_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dev->active++; + i2s_start(dev, substream); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dev->active--; + i2s_stop(dev, substream); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static struct snd_soc_dai_ops dw_i2s_dai_ops = { + .startup = dw_i2s_startup, + .shutdown = dw_i2s_shutdown, + .hw_params = dw_i2s_hw_params, + .trigger = dw_i2s_trigger, +}; + +#ifdef CONFIG_PM + +static int dw_i2s_suspend(struct snd_soc_dai *dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + + clk_disable(dev->clk); + return 0; +} + +static int dw_i2s_resume(struct snd_soc_dai *dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + + clk_enable(dev->clk); + return 0; +} + +#else +#define dw_i2s_suspend NULL +#define dw_i2s_resume NULL +#endif + +static int dw_i2s_probe(struct platform_device *pdev) +{ + const struct i2s_platform_data *pdata = pdev->dev.platform_data; + struct dw_i2s_dev *dev; + struct resource *res; + int ret; + unsigned int cap; + struct snd_soc_dai_driver *dw_i2s_dai; + + if (!pdata) { + dev_err(&pdev->dev, "Invalid platform data\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no i2s resource defined\n"); + return -ENODEV; + } + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "i2s region already claimed\n"); + return -EBUSY; + } + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) { + dev_warn(&pdev->dev, "kzalloc fail\n"); + return -ENOMEM; + } + + dev->i2s_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!dev->i2s_base) { + dev_err(&pdev->dev, "ioremap fail for i2s_region\n"); + return -ENOMEM; + } + + cap = pdata->cap; + dev->capability = cap; + dev->i2s_clk_cfg = pdata->i2s_clk_cfg; + + /* Set DMA slaves info */ + + dev->play_dma_data.data = pdata->play_dma_data; + dev->capture_dma_data.data = pdata->capture_dma_data; + dev->play_dma_data.addr = res->start + I2S_TXDMA; + dev->capture_dma_data.addr = res->start + I2S_RXDMA; + dev->play_dma_data.max_burst = 16; + dev->capture_dma_data.max_burst = 16; + dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + dev->play_dma_data.filter = pdata->filter; + dev->capture_dma_data.filter = pdata->filter; + + dev->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); + + ret = clk_enable(dev->clk); + if (ret < 0) + goto err_clk_put; + + dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL); + if (!dw_i2s_dai) { + dev_err(&pdev->dev, "mem allocation failed for dai driver\n"); + ret = -ENOMEM; + goto err_clk_disable; + } + + if (cap & DWC_I2S_PLAY) { + dev_dbg(&pdev->dev, " SPEAr: play supported\n"); + dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; + dw_i2s_dai->playback.channels_max = pdata->channel; + dw_i2s_dai->playback.formats = pdata->snd_fmts; + dw_i2s_dai->playback.rates = pdata->snd_rates; + } + + if (cap & DWC_I2S_RECORD) { + dev_dbg(&pdev->dev, "SPEAr: record supported\n"); + dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; + dw_i2s_dai->capture.channels_max = pdata->channel; + dw_i2s_dai->capture.formats = pdata->snd_fmts; + dw_i2s_dai->capture.rates = pdata->snd_rates; + } + + dw_i2s_dai->ops = &dw_i2s_dai_ops; + dw_i2s_dai->suspend = dw_i2s_suspend; + dw_i2s_dai->resume = dw_i2s_resume; + + dev->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, dev); + ret = snd_soc_register_dai(&pdev->dev, dw_i2s_dai); + if (ret != 0) { + dev_err(&pdev->dev, "not able to register dai\n"); + goto err_set_drvdata; + } + + return 0; + +err_set_drvdata: + dev_set_drvdata(&pdev->dev, NULL); +err_clk_disable: + clk_disable(dev->clk); +err_clk_put: + clk_put(dev->clk); + return ret; +} + +static int dw_i2s_remove(struct platform_device *pdev) +{ + struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_dai(&pdev->dev); + dev_set_drvdata(&pdev->dev, NULL); + + clk_put(dev->clk); + + return 0; +} + +static struct platform_driver dw_i2s_driver = { + .probe = dw_i2s_probe, + .remove = dw_i2s_remove, + .driver = { + .name = "designware-i2s", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(dw_i2s_driver); + +MODULE_AUTHOR("Rajeev Kumar "); +MODULE_DESCRIPTION("DESIGNWARE I2S SoC Interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:designware_i2s"); -- cgit v1.2.3 From 241b446f30de171b627524c107ce03e5ecee0124 Mon Sep 17 00:00:00 2001 From: Rajeev Kumar Date: Thu, 21 Jun 2012 15:54:52 +0530 Subject: ASoC: Add support for SPEAr ASoC pcm layer. This patch add support for the SPEAr ASoC pcm layer in ASoC framework. The pcm layer uses common snd_dmaengine framework. Signed-off-by: Rajeev Kumar Signed-off-by: Mark Brown --- include/sound/spear_dma.h | 35 ++++++++ sound/soc/spear/spear_pcm.c | 214 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+) create mode 100644 include/sound/spear_dma.h create mode 100644 sound/soc/spear/spear_pcm.c (limited to 'include') diff --git a/include/sound/spear_dma.h b/include/sound/spear_dma.h new file mode 100644 index 000000000000..1b365bfdfb37 --- /dev/null +++ b/include/sound/spear_dma.h @@ -0,0 +1,35 @@ +/* +* linux/spear_dma.h +* +* Copyright (ST) 2012 Rajeev Kumar (rajeev-dlh.kumar@st.com) +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +*/ + +#ifndef SPEAR_DMA_H +#define SPEAR_DMA_H + +#include + +struct spear_dma_data { + void *data; + dma_addr_t addr; + u32 max_burst; + enum dma_slave_buswidth addr_width; + bool (*filter)(struct dma_chan *chan, void *slave); +}; + +#endif /* SPEAR_DMA_H */ diff --git a/sound/soc/spear/spear_pcm.c b/sound/soc/spear/spear_pcm.c new file mode 100644 index 000000000000..97c2cac8e92c --- /dev/null +++ b/sound/soc/spear/spear_pcm.c @@ -0,0 +1,214 @@ +/* + * ALSA PCM interface for ST SPEAr Processors + * + * sound/soc/spear/spear_pcm.c + * + * Copyright (C) 2012 ST Microelectronics + * Rajeev Kumar + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct snd_pcm_hardware spear_pcm_hardware = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .buffer_bytes_max = 16 * 1024, /* max buffer size */ + .period_bytes_min = 2 * 1024, /* 1 msec data minimum period size */ + .period_bytes_max = 2 * 1024, /* maximum period size */ + .periods_min = 1, /* min # periods */ + .periods_max = 8, /* max # of periods */ + .fifo_size = 0, /* fifo size in bytes */ +}; + +static int spear_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +static int spear_pcm_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_set_runtime_buffer(substream, NULL); + + return 0; +} + +static int spear_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + struct spear_dma_data *dma_data = (struct spear_dma_data *) + snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + int ret; + + ret = snd_soc_set_runtime_hwparams(substream, &spear_pcm_hardware); + if (ret) + return ret; + + ret = snd_dmaengine_pcm_open(substream, dma_data->filter, dma_data); + if (ret) + return ret; + + snd_dmaengine_pcm_set_data(substream, dma_data); + + return 0; +} + +static int spear_pcm_close(struct snd_pcm_substream *substream) +{ + + snd_dmaengine_pcm_close(substream); + + return 0; +} + +static int spear_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, runtime->dma_addr, + runtime->dma_bytes); +} + +static struct snd_pcm_ops spear_pcm_ops = { + .open = spear_pcm_open, + .close = spear_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = spear_pcm_hw_params, + .hw_free = spear_pcm_hw_free, + .trigger = snd_dmaengine_pcm_trigger, + .pointer = snd_dmaengine_pcm_pointer, + .mmap = spear_pcm_mmap, +}; + +static int +spear_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream, + size_t size) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + if (!buf->area) + return -ENOMEM; + + dev_info(buf->dev.dev, + " preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", + (void *)buf->area, (void *)buf->addr, size); + + buf->bytes = size; + return 0; +} + +static void spear_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf && !buf->area) + continue; + + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} + +static u64 spear_pcm_dmamask = DMA_BIT_MASK(32); + +static int spear_pcm_new(struct snd_card *card, + struct snd_soc_dai *dai, struct snd_pcm *pcm) +{ + int ret; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &spear_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + if (dai->driver->playback.channels_min) { + ret = spear_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK, + spear_pcm_hardware.buffer_bytes_max); + if (ret) + return ret; + } + + if (dai->driver->capture.channels_min) { + ret = spear_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE, + spear_pcm_hardware.buffer_bytes_max); + if (ret) + return ret; + } + + return 0; +} + +struct snd_soc_platform_driver spear_soc_platform = { + .ops = &spear_pcm_ops, + .pcm_new = spear_pcm_new, + .pcm_free = spear_pcm_free, +}; + +static int __devinit spear_soc_platform_probe(struct platform_device *pdev) +{ + return snd_soc_register_platform(&pdev->dev, &spear_soc_platform); +} + +static int __devexit spear_soc_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + + return 0; +} + +static struct platform_driver spear_pcm_driver = { + .driver = { + .name = "spear-pcm-audio", + .owner = THIS_MODULE, + }, + + .probe = spear_soc_platform_probe, + .remove = __devexit_p(spear_soc_platform_remove), +}; + +module_platform_driver(spear_pcm_driver); + +MODULE_AUTHOR("Rajeev Kumar "); +MODULE_DESCRIPTION("SPEAr PCM DMA module"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:spear-pcm-audio"); -- cgit v1.2.3 From d59315ca8c0de00df9b363f94a2641a30961ca1c Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 21 Jun 2012 12:49:23 -0700 Subject: libceph: drop ceph_con_get/put helpers and nref member These are no longer used. Every ceph_connection instance is embedded in another structure, and refcounts manipulated via the get/put ops. Signed-off-by: Sage Weil --- include/linux/ceph/messenger.h | 1 - net/ceph/messenger.c | 28 +--------------------------- 2 files changed, 1 insertion(+), 28 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index a334dbd1b324..cc6f9bdcf466 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -135,7 +135,6 @@ struct ceph_msg_pos { */ struct ceph_connection { void *private; - atomic_t nref; const struct ceph_connection_operations *ops; diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index fc0cee7c9aa2..ab690e2e1206 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -500,30 +500,6 @@ bool ceph_con_opened(struct ceph_connection *con) return con->connect_seq > 0; } -/* - * generic get/put - */ -struct ceph_connection *ceph_con_get(struct ceph_connection *con) -{ - int nref = __atomic_add_unless(&con->nref, 1, 0); - - dout("con_get %p nref = %d -> %d\n", con, nref, nref + 1); - - return nref ? con : NULL; -} - -void ceph_con_put(struct ceph_connection *con) -{ - int nref = atomic_dec_return(&con->nref); - - BUG_ON(nref < 0); - if (nref == 0) { - BUG_ON(con->sock); - kfree(con); - } - dout("con_put %p nref = %d -> %d\n", con, nref + 1, nref); -} - /* * initialize a new connection. */ @@ -535,7 +511,6 @@ void ceph_con_init(struct ceph_connection *con, void *private, memset(con, 0, sizeof(*con)); con->private = private; con->ops = ops; - atomic_set(&con->nref, 1); con->msgr = msgr; con_sock_state_init(con); @@ -1951,8 +1926,7 @@ static int try_write(struct ceph_connection *con) { int ret = 1; - dout("try_write start %p state %lu nref %d\n", con, con->state, - atomic_read(&con->nref)); + dout("try_write start %p state %lu\n", con, con->state); more: dout("try_write out_kvec_bytes %d\n", con->out_kvec_bytes); -- cgit v1.2.3 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') 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 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') 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 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') 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 From 7586eceb0abc0ea1c2b023e3e5d4dfd4ff40930a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 20 Jun 2012 05:02:19 +0000 Subject: ipv4: tcp: dont cache output dst for syncookies Don't cache output dst for syncookies, as this adds pressure on IP route cache and rcu subsystem for no gain. Signed-off-by: Eric Dumazet Cc: Hans Schillstrom Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/net/flow.h | 1 + include/net/inet_connection_sock.h | 3 ++- net/dccp/ipv4.c | 2 +- net/ipv4/inet_connection_sock.c | 8 ++++++-- net/ipv4/route.c | 5 ++++- net/ipv4/tcp_ipv4.c | 12 +++++++----- 6 files changed, 21 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/flow.h b/include/net/flow.h index 6c469dbdb917..bd524f598561 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -22,6 +22,7 @@ struct flowi_common { #define FLOWI_FLAG_ANYSRC 0x01 #define FLOWI_FLAG_PRECOW_METRICS 0x02 #define FLOWI_FLAG_CAN_SLEEP 0x04 +#define FLOWI_FLAG_RT_NOCACHE 0x08 __u32 flowic_secid; }; diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index e1b7734c456f..af3c743a40e4 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -251,7 +251,8 @@ extern int inet_csk_get_port(struct sock *sk, unsigned short snum); extern struct dst_entry* inet_csk_route_req(struct sock *sk, struct flowi4 *fl4, - const struct request_sock *req); + const struct request_sock *req, + bool nocache); extern struct dst_entry* inet_csk_route_child_sock(struct sock *sk, struct sock *newsk, const struct request_sock *req); diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 07f5579ca756..3eb76b5f221a 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -504,7 +504,7 @@ static int dccp_v4_send_response(struct sock *sk, struct request_sock *req, struct dst_entry *dst; struct flowi4 fl4; - dst = inet_csk_route_req(sk, &fl4, req); + dst = inet_csk_route_req(sk, &fl4, req, false); if (dst == NULL) goto out; diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index f9ee7417f6a0..034ddbe42adf 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -368,17 +368,21 @@ EXPORT_SYMBOL(inet_csk_reset_keepalive_timer); struct dst_entry *inet_csk_route_req(struct sock *sk, struct flowi4 *fl4, - const struct request_sock *req) + const struct request_sock *req, + bool nocache) { struct rtable *rt; const struct inet_request_sock *ireq = inet_rsk(req); struct ip_options_rcu *opt = inet_rsk(req)->opt; struct net *net = sock_net(sk); + int flags = inet_sk_flowi_flags(sk) & ~FLOWI_FLAG_PRECOW_METRICS; + if (nocache) + flags |= FLOWI_FLAG_RT_NOCACHE; flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, sk->sk_protocol, - inet_sk_flowi_flags(sk) & ~FLOWI_FLAG_PRECOW_METRICS, + flags, (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr, ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport); security_req_classify_flow(req, flowi4_to_flowi(fl4)); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index a91f6d33804c..8d62d85e68dc 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1156,7 +1156,7 @@ restart: candp = NULL; now = jiffies; - if (!rt_caching(dev_net(rt->dst.dev))) { + if (!rt_caching(dev_net(rt->dst.dev)) || (rt->dst.flags & DST_NOCACHE)) { /* * If we're not caching, just tell the caller we * were successful and don't touch the route. The @@ -2582,6 +2582,9 @@ static struct rtable *__mkroute_output(const struct fib_result *res, rt_set_nexthop(rth, fl4, res, fi, type, 0); + if (fl4->flowi4_flags & FLOWI_FLAG_RT_NOCACHE) + rth->dst.flags |= DST_NOCACHE; + return rth; } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 21e22a00481a..b52934f5334e 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -825,7 +825,8 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, struct request_sock *req, struct request_values *rvp, - u16 queue_mapping) + u16 queue_mapping, + bool nocache) { const struct inet_request_sock *ireq = inet_rsk(req); struct flowi4 fl4; @@ -833,7 +834,7 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, struct sk_buff * skb; /* First, grab a route. */ - if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL) + if (!dst && (dst = inet_csk_route_req(sk, &fl4, req, nocache)) == NULL) return -1; skb = tcp_make_synack(sk, dst, req, rvp); @@ -855,7 +856,7 @@ static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req, struct request_values *rvp) { TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); - return tcp_v4_send_synack(sk, NULL, req, rvp, 0); + return tcp_v4_send_synack(sk, NULL, req, rvp, 0, false); } /* @@ -1388,7 +1389,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) */ if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle && - (dst = inet_csk_route_req(sk, &fl4, req)) != NULL && + (dst = inet_csk_route_req(sk, &fl4, req, want_cookie)) != NULL && fl4.daddr == saddr && (peer = rt_get_peer((struct rtable *)dst, fl4.daddr)) != NULL) { inet_peer_refcheck(peer); @@ -1424,7 +1425,8 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) if (tcp_v4_send_synack(sk, dst, req, (struct request_values *)&tmp_ext, - skb_get_queue_mapping(skb)) || + skb_get_queue_mapping(skb), + want_cookie) || want_cookie) goto drop_and_free; -- cgit v1.2.3 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') 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 From ace36d85809f6005b559802f194d44c6aa61af88 Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Thu, 21 Jun 2012 15:54:53 +0530 Subject: ASoC: SPEAr spdif_in: Add spdif IN support This patch implements the spdif IN driver for ST peripheral Signed-off-by: Vipin Kumar Signed-off-by: Rajeev Kumar Signed-off-by: Mark Brown --- include/sound/spear_spdif.h | 29 ++++ sound/soc/spear/spdif_in.c | 297 ++++++++++++++++++++++++++++++++++++++++ sound/soc/spear/spdif_in_regs.h | 60 ++++++++ 3 files changed, 386 insertions(+) create mode 100644 include/sound/spear_spdif.h create mode 100644 sound/soc/spear/spdif_in.c create mode 100644 sound/soc/spear/spdif_in_regs.h (limited to 'include') diff --git a/include/sound/spear_spdif.h b/include/sound/spear_spdif.h new file mode 100644 index 000000000000..a12f39695610 --- /dev/null +++ b/include/sound/spear_spdif.h @@ -0,0 +1,29 @@ +/* + * Copyright (ST) 2012 Vipin Kumar (vipin.kumar@st.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SOUND_SPDIF_H +#define __SOUND_SPDIF_H + +struct spear_spdif_platform_data { + /* DMA params */ + void *dma_params; + bool (*filter)(struct dma_chan *chan, void *slave); + void (*reset_perip)(void); +}; + +#endif /* SOUND_SPDIF_H */ diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c new file mode 100644 index 000000000000..c7c4b20395bb --- /dev/null +++ b/sound/soc/spear/spdif_in.c @@ -0,0 +1,297 @@ +/* + * ALSA SoC SPDIF In Audio Layer for spear processors + * + * Copyright (C) 2012 ST Microelectronics + * Vipin Kumar + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spdif_in_regs.h" + +struct spdif_in_params { + u32 format; +}; + +struct spdif_in_dev { + struct clk *clk; + struct spear_dma_data dma_params; + struct spdif_in_params saved_params; + void *io_base; + struct device *dev; + void (*reset_perip)(void); + int irq; +}; + +static void spdif_in_configure(struct spdif_in_dev *host) +{ + u32 ctrl = SPDIF_IN_PRTYEN | SPDIF_IN_STATEN | SPDIF_IN_USREN | + SPDIF_IN_VALEN | SPDIF_IN_BLKEN; + ctrl |= SPDIF_MODE_16BIT | SPDIF_FIFO_THRES_16; + + writel(ctrl, host->io_base + SPDIF_IN_CTRL); + writel(0xF, host->io_base + SPDIF_IN_IRQ_MASK); +} + +static int spdif_in_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(cpu_dai); + + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return -EINVAL; + + snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&host->dma_params); + return 0; +} + +static void spdif_in_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); + + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return; + + writel(0x0, host->io_base + SPDIF_IN_IRQ_MASK); + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static void spdif_in_format(struct spdif_in_dev *host, u32 format) +{ + u32 ctrl = readl(host->io_base + SPDIF_IN_CTRL); + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + ctrl |= SPDIF_XTRACT_16BIT; + break; + + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + ctrl &= ~SPDIF_XTRACT_16BIT; + break; + } + + writel(ctrl, host->io_base + SPDIF_IN_CTRL); +} + +static int spdif_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); + u32 format; + + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return -EINVAL; + + format = params_format(params); + host->saved_params.format = format; + + return 0; +} + +static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); + u32 ctrl; + int ret = 0; + + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return -EINVAL; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + clk_enable(host->clk); + spdif_in_configure(host); + spdif_in_format(host, host->saved_params.format); + + ctrl = readl(host->io_base + SPDIF_IN_CTRL); + ctrl |= SPDIF_IN_SAMPLE | SPDIF_IN_ENB; + writel(ctrl, host->io_base + SPDIF_IN_CTRL); + writel(0xF, host->io_base + SPDIF_IN_IRQ_MASK); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ctrl = readl(host->io_base + SPDIF_IN_CTRL); + ctrl &= ~(SPDIF_IN_SAMPLE | SPDIF_IN_ENB); + writel(ctrl, host->io_base + SPDIF_IN_CTRL); + writel(0x0, host->io_base + SPDIF_IN_IRQ_MASK); + + if (host->reset_perip) + host->reset_perip(); + clk_disable(host->clk); + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} + +static struct snd_soc_dai_ops spdif_in_dai_ops = { + .startup = spdif_in_startup, + .shutdown = spdif_in_shutdown, + .trigger = spdif_in_trigger, + .hw_params = spdif_in_hw_params, +}; + +struct snd_soc_dai_driver spdif_in_dai = { + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000), + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, + }, + .ops = &spdif_in_dai_ops, +}; + +static irqreturn_t spdif_in_irq(int irq, void *arg) +{ + struct spdif_in_dev *host = (struct spdif_in_dev *)arg; + + u32 irq_status = readl(host->io_base + SPDIF_IN_IRQ); + + if (!irq_status) + return IRQ_NONE; + + if (irq_status & SPDIF_IRQ_FIFOWRITE) + dev_err(host->dev, "spdif in: fifo write error"); + if (irq_status & SPDIF_IRQ_EMPTYFIFOREAD) + dev_err(host->dev, "spdif in: empty fifo read error"); + if (irq_status & SPDIF_IRQ_FIFOFULL) + dev_err(host->dev, "spdif in: fifo full error"); + if (irq_status & SPDIF_IRQ_OUTOFRANGE) + dev_err(host->dev, "spdif in: out of range error"); + + writel(0, host->io_base + SPDIF_IN_IRQ); + + return IRQ_HANDLED; +} + +static int spdif_in_probe(struct platform_device *pdev) +{ + struct spdif_in_dev *host; + struct spear_spdif_platform_data *pdata; + struct resource *res, *res_fifo; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + res_fifo = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res_fifo) + return -EINVAL; + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name)) { + dev_warn(&pdev->dev, "Failed to get memory resourse\n"); + return -ENOENT; + } + + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); + if (!host) { + dev_warn(&pdev->dev, "kzalloc fail\n"); + return -ENOMEM; + } + + host->io_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!host->io_base) { + dev_warn(&pdev->dev, "ioremap failed\n"); + return -ENOMEM; + } + + host->irq = platform_get_irq(pdev, 0); + if (host->irq < 0) + return -EINVAL; + + host->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); + + pdata = dev_get_platdata(&pdev->dev); + + if (!pdata) + return -EINVAL; + + host->dma_params.data = pdata->dma_params; + host->dma_params.addr = res_fifo->start; + host->dma_params.max_burst = 16; + host->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_params.filter = pdata->filter; + host->reset_perip = pdata->reset_perip; + + host->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, host); + + ret = devm_request_irq(&pdev->dev, host->irq, spdif_in_irq, 0, + "spdif-in", host); + if (ret) { + clk_put(host->clk); + dev_warn(&pdev->dev, "request_irq failed\n"); + return ret; + } + + ret = snd_soc_register_dai(&pdev->dev, &spdif_in_dai); + if (ret != 0) { + clk_put(host->clk); + return ret; + } + + return 0; +} + +static int spdif_in_remove(struct platform_device *pdev) +{ + struct spdif_in_dev *host = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_dai(&pdev->dev); + dev_set_drvdata(&pdev->dev, NULL); + + clk_put(host->clk); + + return 0; +} + + +static struct platform_driver spdif_in_driver = { + .probe = spdif_in_probe, + .remove = spdif_in_remove, + .driver = { + .name = "spdif-in", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(spdif_in_driver); + +MODULE_AUTHOR("Vipin Kumar "); +MODULE_DESCRIPTION("SPEAr SPDIF IN SoC Interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:spdif_in"); diff --git a/sound/soc/spear/spdif_in_regs.h b/sound/soc/spear/spdif_in_regs.h new file mode 100644 index 000000000000..37af7bc66b7f --- /dev/null +++ b/sound/soc/spear/spdif_in_regs.h @@ -0,0 +1,60 @@ +/* + * SPEAr SPDIF IN controller header file + * + * Copyright (ST) 2011 Vipin Kumar (vipin.kumar@st.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SPDIF_IN_REGS_H +#define SPDIF_IN_REGS_H + +#define SPDIF_IN_CTRL 0x00 + #define SPDIF_IN_PRTYEN (1 << 20) + #define SPDIF_IN_STATEN (1 << 19) + #define SPDIF_IN_USREN (1 << 18) + #define SPDIF_IN_VALEN (1 << 17) + #define SPDIF_IN_BLKEN (1 << 16) + + #define SPDIF_MODE_24BIT (8 << 12) + #define SPDIF_MODE_23BIT (7 << 12) + #define SPDIF_MODE_22BIT (6 << 12) + #define SPDIF_MODE_21BIT (5 << 12) + #define SPDIF_MODE_20BIT (4 << 12) + #define SPDIF_MODE_19BIT (3 << 12) + #define SPDIF_MODE_18BIT (2 << 12) + #define SPDIF_MODE_17BIT (1 << 12) + #define SPDIF_MODE_16BIT (0 << 12) + #define SPDIF_MODE_MASK (0x0F << 12) + + #define SPDIF_IN_VALID (1 << 11) + #define SPDIF_IN_SAMPLE (1 << 10) + #define SPDIF_DATA_SWAP (1 << 9) + #define SPDIF_IN_ENB (1 << 8) + #define SPDIF_DATA_REVERT (1 << 7) + #define SPDIF_XTRACT_16BIT (1 << 6) + #define SPDIF_FIFO_THRES_16 (16 << 0) + +#define SPDIF_IN_IRQ_MASK 0x04 +#define SPDIF_IN_IRQ 0x08 + #define SPDIF_IRQ_FIFOWRITE (1 << 0) + #define SPDIF_IRQ_EMPTYFIFOREAD (1 << 1) + #define SPDIF_IRQ_FIFOFULL (1 << 2) + #define SPDIF_IRQ_OUTOFRANGE (1 << 3) + +#define SPDIF_IN_STA 0x0C + #define SPDIF_IN_LOCK (0x1 << 0) + +#endif /* SPDIF_IN_REGS_H */ -- cgit v1.2.3 From 229e3fdc1ba49b747e9434b55b3f6bd68a4db251 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 22 Jun 2012 11:40:55 +0100 Subject: ASoC: core: Add DOUBLE_R variants of the _RANGE controls The code handles this fine already, we just need new macros in the header for drivers to create the controls. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index e4348d25fca3..e063380f63a2 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -42,6 +42,10 @@ ((unsigned long)&(struct soc_mixer_control) \ {.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \ .max = xmax, .platform_max = xmax, .invert = xinvert}) +#define SOC_DOUBLE_R_RANGE_VALUE(xlreg, xrreg, xshift, xmin, xmax, xinvert) \ + ((unsigned long)&(struct soc_mixer_control) \ + {.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \ + .min = xmin, .max = xmax, .platform_max = xmax, .invert = xinvert}) #define SOC_SINGLE(xname, reg, shift, max, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ @@ -96,6 +100,13 @@ .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \ .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ xmax, xinvert) } +#define SOC_DOUBLE_R_RANGE(xname, reg_left, reg_right, xshift, xmin, \ + xmax, xinvert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .info = snd_soc_info_volsw_range, \ + .get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \ + .private_value = SOC_DOUBLE_R_RANGE_VALUE(reg_left, reg_right, \ + xshift, xmin, xmax, xinvert) } #define SOC_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ @@ -114,6 +125,16 @@ .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \ .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ xmax, xinvert) } +#define SOC_DOUBLE_R_RANGE_TLV(xname, reg_left, reg_right, xshift, xmin, \ + xmax, xinvert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw_range, \ + .get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \ + .private_value = SOC_DOUBLE_R_RANGE_VALUE(reg_left, reg_right, \ + xshift, xmin, xmax, xinvert) } #define SOC_DOUBLE_R_SX_TLV(xname, xreg, xrreg, xshift, xmin, xmax, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ -- cgit v1.2.3 From c46a019a7941ff92291cda1cc2774bf720552ad9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 19 Jun 2012 16:28:40 +0100 Subject: mfd: arizona: Register definitions Several forthcoming Wolfson devices are based on a common platform known as Arizona allowing a great deal of reuse of driver code. This patch adds register definitions for these devices. Signed-off-by: Mark Brown --- include/linux/mfd/arizona/registers.h | 6222 +++++++++++++++++++++++++++++++++ 1 file changed, 6222 insertions(+) create mode 100644 include/linux/mfd/arizona/registers.h (limited to 'include') diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h new file mode 100644 index 000000000000..989c47d681e0 --- /dev/null +++ b/include/linux/mfd/arizona/registers.h @@ -0,0 +1,6222 @@ +/* + * ARIZONA register definitions + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _ARIZONA_REGISTERS_H +#define _ARIZONA_REGISTERS_H + +/* + * Register values. + */ +#define ARIZONA_SOFTWARE_RESET 0x00 +#define ARIZONA_DEVICE_REVISION 0x01 +#define ARIZONA_CTRL_IF_SPI_CFG_1 0x08 +#define ARIZONA_CTRL_IF_I2C1_CFG_1 0x09 +#define ARIZONA_CTRL_IF_STATUS_1 0x0D +#define ARIZONA_WRITE_SEQUENCER_CTRL_0 0x16 +#define ARIZONA_WRITE_SEQUENCER_CTRL_1 0x17 +#define ARIZONA_WRITE_SEQUENCER_CTRL_2 0x18 +#define ARIZONA_WRITE_SEQUENCER_PROM 0x1A +#define ARIZONA_TONE_GENERATOR_1 0x20 +#define ARIZONA_TONE_GENERATOR_2 0x21 +#define ARIZONA_TONE_GENERATOR_3 0x22 +#define ARIZONA_TONE_GENERATOR_4 0x23 +#define ARIZONA_TONE_GENERATOR_5 0x24 +#define ARIZONA_PWM_DRIVE_1 0x30 +#define ARIZONA_PWM_DRIVE_2 0x31 +#define ARIZONA_PWM_DRIVE_3 0x32 +#define ARIZONA_WAKE_CONTROL 0x40 +#define ARIZONA_SEQUENCE_CONTROL 0x41 +#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_1 0x61 +#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2 0x62 +#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3 0x63 +#define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4 0x64 +#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_1 0x68 +#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_2 0x69 +#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_3 0x6A +#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_4 0x6B +#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_5 0x6C +#define ARIZONA_ALWAYS_ON_TRIGGERS_SEQUENCE_SELECT_6 0x6D +#define ARIZONA_COMFORT_NOISE_GENERATOR 0x70 +#define ARIZONA_HAPTICS_CONTROL_1 0x90 +#define ARIZONA_HAPTICS_CONTROL_2 0x91 +#define ARIZONA_HAPTICS_PHASE_1_INTENSITY 0x92 +#define ARIZONA_HAPTICS_PHASE_1_DURATION 0x93 +#define ARIZONA_HAPTICS_PHASE_2_INTENSITY 0x94 +#define ARIZONA_HAPTICS_PHASE_2_DURATION 0x95 +#define ARIZONA_HAPTICS_PHASE_3_INTENSITY 0x96 +#define ARIZONA_HAPTICS_PHASE_3_DURATION 0x97 +#define ARIZONA_HAPTICS_STATUS 0x98 +#define ARIZONA_CLOCK_32K_1 0x100 +#define ARIZONA_SYSTEM_CLOCK_1 0x101 +#define ARIZONA_SAMPLE_RATE_1 0x102 +#define ARIZONA_SAMPLE_RATE_2 0x103 +#define ARIZONA_SAMPLE_RATE_3 0x104 +#define ARIZONA_SAMPLE_RATE_1_STATUS 0x10A +#define ARIZONA_SAMPLE_RATE_2_STATUS 0x10B +#define ARIZONA_SAMPLE_RATE_3_STATUS 0x10C +#define ARIZONA_ASYNC_CLOCK_1 0x112 +#define ARIZONA_ASYNC_SAMPLE_RATE_1 0x113 +#define ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS 0x11B +#define ARIZONA_OUTPUT_SYSTEM_CLOCK 0x149 +#define ARIZONA_OUTPUT_ASYNC_CLOCK 0x14A +#define ARIZONA_RATE_ESTIMATOR_1 0x152 +#define ARIZONA_RATE_ESTIMATOR_2 0x153 +#define ARIZONA_RATE_ESTIMATOR_3 0x154 +#define ARIZONA_RATE_ESTIMATOR_4 0x155 +#define ARIZONA_RATE_ESTIMATOR_5 0x156 +#define ARIZONA_FLL1_CONTROL_1 0x171 +#define ARIZONA_FLL1_CONTROL_2 0x172 +#define ARIZONA_FLL1_CONTROL_3 0x173 +#define ARIZONA_FLL1_CONTROL_4 0x174 +#define ARIZONA_FLL1_CONTROL_5 0x175 +#define ARIZONA_FLL1_CONTROL_6 0x176 +#define ARIZONA_FLL1_LOOP_FILTER_TEST_1 0x177 +#define ARIZONA_FLL1_SYNCHRONISER_1 0x181 +#define ARIZONA_FLL1_SYNCHRONISER_2 0x182 +#define ARIZONA_FLL1_SYNCHRONISER_3 0x183 +#define ARIZONA_FLL1_SYNCHRONISER_4 0x184 +#define ARIZONA_FLL1_SYNCHRONISER_5 0x185 +#define ARIZONA_FLL1_SYNCHRONISER_6 0x186 +#define ARIZONA_FLL1_SPREAD_SPECTRUM 0x189 +#define ARIZONA_FLL1_GPIO_CLOCK 0x18A +#define ARIZONA_FLL2_CONTROL_1 0x191 +#define ARIZONA_FLL2_CONTROL_2 0x192 +#define ARIZONA_FLL2_CONTROL_3 0x193 +#define ARIZONA_FLL2_CONTROL_4 0x194 +#define ARIZONA_FLL2_CONTROL_5 0x195 +#define ARIZONA_FLL2_CONTROL_6 0x196 +#define ARIZONA_FLL2_LOOP_FILTER_TEST_1 0x197 +#define ARIZONA_FLL2_SYNCHRONISER_1 0x1A1 +#define ARIZONA_FLL2_SYNCHRONISER_2 0x1A2 +#define ARIZONA_FLL2_SYNCHRONISER_3 0x1A3 +#define ARIZONA_FLL2_SYNCHRONISER_4 0x1A4 +#define ARIZONA_FLL2_SYNCHRONISER_5 0x1A5 +#define ARIZONA_FLL2_SYNCHRONISER_6 0x1A6 +#define ARIZONA_FLL2_SPREAD_SPECTRUM 0x1A9 +#define ARIZONA_FLL2_GPIO_CLOCK 0x1AA +#define ARIZONA_MIC_CHARGE_PUMP_1 0x200 +#define ARIZONA_LDO1_CONTROL_1 0x210 +#define ARIZONA_LDO2_CONTROL_1 0x213 +#define ARIZONA_MIC_BIAS_CTRL_1 0x218 +#define ARIZONA_MIC_BIAS_CTRL_2 0x219 +#define ARIZONA_MIC_BIAS_CTRL_3 0x21A +#define ARIZONA_ACCESSORY_DETECT_MODE_1 0x293 +#define ARIZONA_HEADPHONE_DETECT_1 0x29B +#define ARIZONA_HEADPHONE_DETECT_2 0x29C +#define ARIZONA_MIC_DETECT_1 0x2A3 +#define ARIZONA_MIC_DETECT_2 0x2A4 +#define ARIZONA_MIC_DETECT_3 0x2A5 +#define ARIZONA_MIC_NOISE_MIX_CONTROL_1 0x2C3 +#define ARIZONA_ISOLATION_CONTROL 0x2CB +#define ARIZONA_JACK_DETECT_ANALOGUE 0x2D3 +#define ARIZONA_INPUT_ENABLES 0x300 +#define ARIZONA_INPUT_RATE 0x308 +#define ARIZONA_INPUT_VOLUME_RAMP 0x309 +#define ARIZONA_IN1L_CONTROL 0x310 +#define ARIZONA_ADC_DIGITAL_VOLUME_1L 0x311 +#define ARIZONA_DMIC1L_CONTROL 0x312 +#define ARIZONA_IN1R_CONTROL 0x314 +#define ARIZONA_ADC_DIGITAL_VOLUME_1R 0x315 +#define ARIZONA_DMIC1R_CONTROL 0x316 +#define ARIZONA_IN2L_CONTROL 0x318 +#define ARIZONA_ADC_DIGITAL_VOLUME_2L 0x319 +#define ARIZONA_DMIC2L_CONTROL 0x31A +#define ARIZONA_IN2R_CONTROL 0x31C +#define ARIZONA_ADC_DIGITAL_VOLUME_2R 0x31D +#define ARIZONA_DMIC2R_CONTROL 0x31E +#define ARIZONA_IN3L_CONTROL 0x320 +#define ARIZONA_ADC_DIGITAL_VOLUME_3L 0x321 +#define ARIZONA_DMIC3L_CONTROL 0x322 +#define ARIZONA_IN3R_CONTROL 0x324 +#define ARIZONA_ADC_DIGITAL_VOLUME_3R 0x325 +#define ARIZONA_DMIC3R_CONTROL 0x326 +#define ARIZONA_OUTPUT_ENABLES_1 0x400 +#define ARIZONA_OUTPUT_STATUS_1 0x401 +#define ARIZONA_OUTPUT_RATE_1 0x408 +#define ARIZONA_OUTPUT_VOLUME_RAMP 0x409 +#define ARIZONA_OUTPUT_PATH_CONFIG_1L 0x410 +#define ARIZONA_DAC_DIGITAL_VOLUME_1L 0x411 +#define ARIZONA_DAC_VOLUME_LIMIT_1L 0x412 +#define ARIZONA_NOISE_GATE_SELECT_1L 0x413 +#define ARIZONA_OUTPUT_PATH_CONFIG_1R 0x414 +#define ARIZONA_DAC_DIGITAL_VOLUME_1R 0x415 +#define ARIZONA_DAC_VOLUME_LIMIT_1R 0x416 +#define ARIZONA_NOISE_GATE_SELECT_1R 0x417 +#define ARIZONA_OUTPUT_PATH_CONFIG_2L 0x418 +#define ARIZONA_DAC_DIGITAL_VOLUME_2L 0x419 +#define ARIZONA_DAC_VOLUME_LIMIT_2L 0x41A +#define ARIZONA_NOISE_GATE_SELECT_2L 0x41B +#define ARIZONA_OUTPUT_PATH_CONFIG_2R 0x41C +#define ARIZONA_DAC_DIGITAL_VOLUME_2R 0x41D +#define ARIZONA_DAC_VOLUME_LIMIT_2R 0x41E +#define ARIZONA_NOISE_GATE_SELECT_2R 0x41F +#define ARIZONA_OUTPUT_PATH_CONFIG_3L 0x420 +#define ARIZONA_DAC_DIGITAL_VOLUME_3L 0x421 +#define ARIZONA_DAC_VOLUME_LIMIT_3L 0x422 +#define ARIZONA_NOISE_GATE_SELECT_3L 0x423 +#define ARIZONA_OUTPUT_PATH_CONFIG_3R 0x424 +#define ARIZONA_DAC_DIGITAL_VOLUME_3R 0x425 +#define ARIZONA_DAC_VOLUME_LIMIT_3R 0x426 +#define ARIZONA_OUTPUT_PATH_CONFIG_4L 0x428 +#define ARIZONA_DAC_DIGITAL_VOLUME_4L 0x429 +#define ARIZONA_OUT_VOLUME_4L 0x42A +#define ARIZONA_NOISE_GATE_SELECT_4L 0x42B +#define ARIZONA_OUTPUT_PATH_CONFIG_4R 0x42C +#define ARIZONA_DAC_DIGITAL_VOLUME_4R 0x42D +#define ARIZONA_OUT_VOLUME_4R 0x42E +#define ARIZONA_NOISE_GATE_SELECT_4R 0x42F +#define ARIZONA_OUTPUT_PATH_CONFIG_5L 0x430 +#define ARIZONA_DAC_DIGITAL_VOLUME_5L 0x431 +#define ARIZONA_DAC_VOLUME_LIMIT_5L 0x432 +#define ARIZONA_NOISE_GATE_SELECT_5L 0x433 +#define ARIZONA_OUTPUT_PATH_CONFIG_5R 0x434 +#define ARIZONA_DAC_DIGITAL_VOLUME_5R 0x435 +#define ARIZONA_DAC_VOLUME_LIMIT_5R 0x436 +#define ARIZONA_NOISE_GATE_SELECT_5R 0x437 +#define ARIZONA_DAC_AEC_CONTROL_1 0x450 +#define ARIZONA_NOISE_GATE_CONTROL 0x458 +#define ARIZONA_PDM_SPK1_CTRL_1 0x490 +#define ARIZONA_PDM_SPK1_CTRL_2 0x491 +#define ARIZONA_DAC_COMP_1 0x4DC +#define ARIZONA_DAC_COMP_2 0x4DD +#define ARIZONA_DAC_COMP_3 0x4DE +#define ARIZONA_DAC_COMP_4 0x4DF +#define ARIZONA_AIF1_BCLK_CTRL 0x500 +#define ARIZONA_AIF1_TX_PIN_CTRL 0x501 +#define ARIZONA_AIF1_RX_PIN_CTRL 0x502 +#define ARIZONA_AIF1_RATE_CTRL 0x503 +#define ARIZONA_AIF1_FORMAT 0x504 +#define ARIZONA_AIF1_TX_BCLK_RATE 0x505 +#define ARIZONA_AIF1_RX_BCLK_RATE 0x506 +#define ARIZONA_AIF1_FRAME_CTRL_1 0x507 +#define ARIZONA_AIF1_FRAME_CTRL_2 0x508 +#define ARIZONA_AIF1_FRAME_CTRL_3 0x509 +#define ARIZONA_AIF1_FRAME_CTRL_4 0x50A +#define ARIZONA_AIF1_FRAME_CTRL_5 0x50B +#define ARIZONA_AIF1_FRAME_CTRL_6 0x50C +#define ARIZONA_AIF1_FRAME_CTRL_7 0x50D +#define ARIZONA_AIF1_FRAME_CTRL_8 0x50E +#define ARIZONA_AIF1_FRAME_CTRL_9 0x50F +#define ARIZONA_AIF1_FRAME_CTRL_10 0x510 +#define ARIZONA_AIF1_FRAME_CTRL_11 0x511 +#define ARIZONA_AIF1_FRAME_CTRL_12 0x512 +#define ARIZONA_AIF1_FRAME_CTRL_13 0x513 +#define ARIZONA_AIF1_FRAME_CTRL_14 0x514 +#define ARIZONA_AIF1_FRAME_CTRL_15 0x515 +#define ARIZONA_AIF1_FRAME_CTRL_16 0x516 +#define ARIZONA_AIF1_FRAME_CTRL_17 0x517 +#define ARIZONA_AIF1_FRAME_CTRL_18 0x518 +#define ARIZONA_AIF1_TX_ENABLES 0x519 +#define ARIZONA_AIF1_RX_ENABLES 0x51A +#define ARIZONA_AIF1_FORCE_WRITE 0x51B +#define ARIZONA_AIF2_BCLK_CTRL 0x540 +#define ARIZONA_AIF2_TX_PIN_CTRL 0x541 +#define ARIZONA_AIF2_RX_PIN_CTRL 0x542 +#define ARIZONA_AIF2_RATE_CTRL 0x543 +#define ARIZONA_AIF2_FORMAT 0x544 +#define ARIZONA_AIF2_TX_BCLK_RATE 0x545 +#define ARIZONA_AIF2_RX_BCLK_RATE 0x546 +#define ARIZONA_AIF2_FRAME_CTRL_1 0x547 +#define ARIZONA_AIF2_FRAME_CTRL_2 0x548 +#define ARIZONA_AIF2_FRAME_CTRL_3 0x549 +#define ARIZONA_AIF2_FRAME_CTRL_4 0x54A +#define ARIZONA_AIF2_FRAME_CTRL_11 0x551 +#define ARIZONA_AIF2_FRAME_CTRL_12 0x552 +#define ARIZONA_AIF2_TX_ENABLES 0x559 +#define ARIZONA_AIF2_RX_ENABLES 0x55A +#define ARIZONA_AIF2_FORCE_WRITE 0x55B +#define ARIZONA_AIF3_BCLK_CTRL 0x580 +#define ARIZONA_AIF3_TX_PIN_CTRL 0x581 +#define ARIZONA_AIF3_RX_PIN_CTRL 0x582 +#define ARIZONA_AIF3_RATE_CTRL 0x583 +#define ARIZONA_AIF3_FORMAT 0x584 +#define ARIZONA_AIF3_TX_BCLK_RATE 0x585 +#define ARIZONA_AIF3_RX_BCLK_RATE 0x586 +#define ARIZONA_AIF3_FRAME_CTRL_1 0x587 +#define ARIZONA_AIF3_FRAME_CTRL_2 0x588 +#define ARIZONA_AIF3_FRAME_CTRL_3 0x589 +#define ARIZONA_AIF3_FRAME_CTRL_4 0x58A +#define ARIZONA_AIF3_FRAME_CTRL_11 0x591 +#define ARIZONA_AIF3_FRAME_CTRL_12 0x592 +#define ARIZONA_AIF3_TX_ENABLES 0x599 +#define ARIZONA_AIF3_RX_ENABLES 0x59A +#define ARIZONA_AIF3_FORCE_WRITE 0x59B +#define ARIZONA_SLIMBUS_FRAMER_REF_GEAR 0x5E3 +#define ARIZONA_SLIMBUS_RATES_1 0x5E5 +#define ARIZONA_SLIMBUS_RATES_2 0x5E6 +#define ARIZONA_SLIMBUS_RATES_3 0x5E7 +#define ARIZONA_SLIMBUS_RATES_4 0x5E8 +#define ARIZONA_SLIMBUS_RATES_5 0x5E9 +#define ARIZONA_SLIMBUS_RATES_6 0x5EA +#define ARIZONA_SLIMBUS_RATES_7 0x5EB +#define ARIZONA_SLIMBUS_RATES_8 0x5EC +#define ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE 0x5F5 +#define ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE 0x5F6 +#define ARIZONA_SLIMBUS_RX_PORT_STATUS 0x5F7 +#define ARIZONA_SLIMBUS_TX_PORT_STATUS 0x5F8 +#define ARIZONA_PWM1MIX_INPUT_1_SOURCE 0x640 +#define ARIZONA_PWM1MIX_INPUT_1_VOLUME 0x641 +#define ARIZONA_PWM1MIX_INPUT_2_SOURCE 0x642 +#define ARIZONA_PWM1MIX_INPUT_2_VOLUME 0x643 +#define ARIZONA_PWM1MIX_INPUT_3_SOURCE 0x644 +#define ARIZONA_PWM1MIX_INPUT_3_VOLUME 0x645 +#define ARIZONA_PWM1MIX_INPUT_4_SOURCE 0x646 +#define ARIZONA_PWM1MIX_INPUT_4_VOLUME 0x647 +#define ARIZONA_PWM2MIX_INPUT_1_SOURCE 0x648 +#define ARIZONA_PWM2MIX_INPUT_1_VOLUME 0x649 +#define ARIZONA_PWM2MIX_INPUT_2_SOURCE 0x64A +#define ARIZONA_PWM2MIX_INPUT_2_VOLUME 0x64B +#define ARIZONA_PWM2MIX_INPUT_3_SOURCE 0x64C +#define ARIZONA_PWM2MIX_INPUT_3_VOLUME 0x64D +#define ARIZONA_PWM2MIX_INPUT_4_SOURCE 0x64E +#define ARIZONA_PWM2MIX_INPUT_4_VOLUME 0x64F +#define ARIZONA_MICMIX_INPUT_1_SOURCE 0x660 +#define ARIZONA_MICMIX_INPUT_1_VOLUME 0x661 +#define ARIZONA_MICMIX_INPUT_2_SOURCE 0x662 +#define ARIZONA_MICMIX_INPUT_2_VOLUME 0x663 +#define ARIZONA_MICMIX_INPUT_3_SOURCE 0x664 +#define ARIZONA_MICMIX_INPUT_3_VOLUME 0x665 +#define ARIZONA_MICMIX_INPUT_4_SOURCE 0x666 +#define ARIZONA_MICMIX_INPUT_4_VOLUME 0x667 +#define ARIZONA_NOISEMIX_INPUT_1_SOURCE 0x668 +#define ARIZONA_NOISEMIX_INPUT_1_VOLUME 0x669 +#define ARIZONA_NOISEMIX_INPUT_2_SOURCE 0x66A +#define ARIZONA_NOISEMIX_INPUT_2_VOLUME 0x66B +#define ARIZONA_NOISEMIX_INPUT_3_SOURCE 0x66C +#define ARIZONA_NOISEMIX_INPUT_3_VOLUME 0x66D +#define ARIZONA_NOISEMIX_INPUT_4_SOURCE 0x66E +#define ARIZONA_NOISEMIX_INPUT_4_VOLUME 0x66F +#define ARIZONA_OUT1LMIX_INPUT_1_SOURCE 0x680 +#define ARIZONA_OUT1LMIX_INPUT_1_VOLUME 0x681 +#define ARIZONA_OUT1LMIX_INPUT_2_SOURCE 0x682 +#define ARIZONA_OUT1LMIX_INPUT_2_VOLUME 0x683 +#define ARIZONA_OUT1LMIX_INPUT_3_SOURCE 0x684 +#define ARIZONA_OUT1LMIX_INPUT_3_VOLUME 0x685 +#define ARIZONA_OUT1LMIX_INPUT_4_SOURCE 0x686 +#define ARIZONA_OUT1LMIX_INPUT_4_VOLUME 0x687 +#define ARIZONA_OUT1RMIX_INPUT_1_SOURCE 0x688 +#define ARIZONA_OUT1RMIX_INPUT_1_VOLUME 0x689 +#define ARIZONA_OUT1RMIX_INPUT_2_SOURCE 0x68A +#define ARIZONA_OUT1RMIX_INPUT_2_VOLUME 0x68B +#define ARIZONA_OUT1RMIX_INPUT_3_SOURCE 0x68C +#define ARIZONA_OUT1RMIX_INPUT_3_VOLUME 0x68D +#define ARIZONA_OUT1RMIX_INPUT_4_SOURCE 0x68E +#define ARIZONA_OUT1RMIX_INPUT_4_VOLUME 0x68F +#define ARIZONA_OUT2LMIX_INPUT_1_SOURCE 0x690 +#define ARIZONA_OUT2LMIX_INPUT_1_VOLUME 0x691 +#define ARIZONA_OUT2LMIX_INPUT_2_SOURCE 0x692 +#define ARIZONA_OUT2LMIX_INPUT_2_VOLUME 0x693 +#define ARIZONA_OUT2LMIX_INPUT_3_SOURCE 0x694 +#define ARIZONA_OUT2LMIX_INPUT_3_VOLUME 0x695 +#define ARIZONA_OUT2LMIX_INPUT_4_SOURCE 0x696 +#define ARIZONA_OUT2LMIX_INPUT_4_VOLUME 0x697 +#define ARIZONA_OUT2RMIX_INPUT_1_SOURCE 0x698 +#define ARIZONA_OUT2RMIX_INPUT_1_VOLUME 0x699 +#define ARIZONA_OUT2RMIX_INPUT_2_SOURCE 0x69A +#define ARIZONA_OUT2RMIX_INPUT_2_VOLUME 0x69B +#define ARIZONA_OUT2RMIX_INPUT_3_SOURCE 0x69C +#define ARIZONA_OUT2RMIX_INPUT_3_VOLUME 0x69D +#define ARIZONA_OUT2RMIX_INPUT_4_SOURCE 0x69E +#define ARIZONA_OUT2RMIX_INPUT_4_VOLUME 0x69F +#define ARIZONA_OUT3LMIX_INPUT_1_SOURCE 0x6A0 +#define ARIZONA_OUT3LMIX_INPUT_1_VOLUME 0x6A1 +#define ARIZONA_OUT3LMIX_INPUT_2_SOURCE 0x6A2 +#define ARIZONA_OUT3LMIX_INPUT_2_VOLUME 0x6A3 +#define ARIZONA_OUT3LMIX_INPUT_3_SOURCE 0x6A4 +#define ARIZONA_OUT3LMIX_INPUT_3_VOLUME 0x6A5 +#define ARIZONA_OUT3LMIX_INPUT_4_SOURCE 0x6A6 +#define ARIZONA_OUT3LMIX_INPUT_4_VOLUME 0x6A7 +#define ARIZONA_OUT4LMIX_INPUT_1_SOURCE 0x6B0 +#define ARIZONA_OUT4LMIX_INPUT_1_VOLUME 0x6B1 +#define ARIZONA_OUT4LMIX_INPUT_2_SOURCE 0x6B2 +#define ARIZONA_OUT4LMIX_INPUT_2_VOLUME 0x6B3 +#define ARIZONA_OUT4LMIX_INPUT_3_SOURCE 0x6B4 +#define ARIZONA_OUT4LMIX_INPUT_3_VOLUME 0x6B5 +#define ARIZONA_OUT4LMIX_INPUT_4_SOURCE 0x6B6 +#define ARIZONA_OUT4LMIX_INPUT_4_VOLUME 0x6B7 +#define ARIZONA_OUT4RMIX_INPUT_1_SOURCE 0x6B8 +#define ARIZONA_OUT4RMIX_INPUT_1_VOLUME 0x6B9 +#define ARIZONA_OUT4RMIX_INPUT_2_SOURCE 0x6BA +#define ARIZONA_OUT4RMIX_INPUT_2_VOLUME 0x6BB +#define ARIZONA_OUT4RMIX_INPUT_3_SOURCE 0x6BC +#define ARIZONA_OUT4RMIX_INPUT_3_VOLUME 0x6BD +#define ARIZONA_OUT4RMIX_INPUT_4_SOURCE 0x6BE +#define ARIZONA_OUT4RMIX_INPUT_4_VOLUME 0x6BF +#define ARIZONA_OUT5LMIX_INPUT_1_SOURCE 0x6C0 +#define ARIZONA_OUT5LMIX_INPUT_1_VOLUME 0x6C1 +#define ARIZONA_OUT5LMIX_INPUT_2_SOURCE 0x6C2 +#define ARIZONA_OUT5LMIX_INPUT_2_VOLUME 0x6C3 +#define ARIZONA_OUT5LMIX_INPUT_3_SOURCE 0x6C4 +#define ARIZONA_OUT5LMIX_INPUT_3_VOLUME 0x6C5 +#define ARIZONA_OUT5LMIX_INPUT_4_SOURCE 0x6C6 +#define ARIZONA_OUT5LMIX_INPUT_4_VOLUME 0x6C7 +#define ARIZONA_OUT5RMIX_INPUT_1_SOURCE 0x6C8 +#define ARIZONA_OUT5RMIX_INPUT_1_VOLUME 0x6C9 +#define ARIZONA_OUT5RMIX_INPUT_2_SOURCE 0x6CA +#define ARIZONA_OUT5RMIX_INPUT_2_VOLUME 0x6CB +#define ARIZONA_OUT5RMIX_INPUT_3_SOURCE 0x6CC +#define ARIZONA_OUT5RMIX_INPUT_3_VOLUME 0x6CD +#define ARIZONA_OUT5RMIX_INPUT_4_SOURCE 0x6CE +#define ARIZONA_OUT5RMIX_INPUT_4_VOLUME 0x6CF +#define ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE 0x700 +#define ARIZONA_AIF1TX1MIX_INPUT_1_VOLUME 0x701 +#define ARIZONA_AIF1TX1MIX_INPUT_2_SOURCE 0x702 +#define ARIZONA_AIF1TX1MIX_INPUT_2_VOLUME 0x703 +#define ARIZONA_AIF1TX1MIX_INPUT_3_SOURCE 0x704 +#define ARIZONA_AIF1TX1MIX_INPUT_3_VOLUME 0x705 +#define ARIZONA_AIF1TX1MIX_INPUT_4_SOURCE 0x706 +#define ARIZONA_AIF1TX1MIX_INPUT_4_VOLUME 0x707 +#define ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE 0x708 +#define ARIZONA_AIF1TX2MIX_INPUT_1_VOLUME 0x709 +#define ARIZONA_AIF1TX2MIX_INPUT_2_SOURCE 0x70A +#define ARIZONA_AIF1TX2MIX_INPUT_2_VOLUME 0x70B +#define ARIZONA_AIF1TX2MIX_INPUT_3_SOURCE 0x70C +#define ARIZONA_AIF1TX2MIX_INPUT_3_VOLUME 0x70D +#define ARIZONA_AIF1TX2MIX_INPUT_4_SOURCE 0x70E +#define ARIZONA_AIF1TX2MIX_INPUT_4_VOLUME 0x70F +#define ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE 0x710 +#define ARIZONA_AIF1TX3MIX_INPUT_1_VOLUME 0x711 +#define ARIZONA_AIF1TX3MIX_INPUT_2_SOURCE 0x712 +#define ARIZONA_AIF1TX3MIX_INPUT_2_VOLUME 0x713 +#define ARIZONA_AIF1TX3MIX_INPUT_3_SOURCE 0x714 +#define ARIZONA_AIF1TX3MIX_INPUT_3_VOLUME 0x715 +#define ARIZONA_AIF1TX3MIX_INPUT_4_SOURCE 0x716 +#define ARIZONA_AIF1TX3MIX_INPUT_4_VOLUME 0x717 +#define ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE 0x718 +#define ARIZONA_AIF1TX4MIX_INPUT_1_VOLUME 0x719 +#define ARIZONA_AIF1TX4MIX_INPUT_2_SOURCE 0x71A +#define ARIZONA_AIF1TX4MIX_INPUT_2_VOLUME 0x71B +#define ARIZONA_AIF1TX4MIX_INPUT_3_SOURCE 0x71C +#define ARIZONA_AIF1TX4MIX_INPUT_3_VOLUME 0x71D +#define ARIZONA_AIF1TX4MIX_INPUT_4_SOURCE 0x71E +#define ARIZONA_AIF1TX4MIX_INPUT_4_VOLUME 0x71F +#define ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE 0x720 +#define ARIZONA_AIF1TX5MIX_INPUT_1_VOLUME 0x721 +#define ARIZONA_AIF1TX5MIX_INPUT_2_SOURCE 0x722 +#define ARIZONA_AIF1TX5MIX_INPUT_2_VOLUME 0x723 +#define ARIZONA_AIF1TX5MIX_INPUT_3_SOURCE 0x724 +#define ARIZONA_AIF1TX5MIX_INPUT_3_VOLUME 0x725 +#define ARIZONA_AIF1TX5MIX_INPUT_4_SOURCE 0x726 +#define ARIZONA_AIF1TX5MIX_INPUT_4_VOLUME 0x727 +#define ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE 0x728 +#define ARIZONA_AIF1TX6MIX_INPUT_1_VOLUME 0x729 +#define ARIZONA_AIF1TX6MIX_INPUT_2_SOURCE 0x72A +#define ARIZONA_AIF1TX6MIX_INPUT_2_VOLUME 0x72B +#define ARIZONA_AIF1TX6MIX_INPUT_3_SOURCE 0x72C +#define ARIZONA_AIF1TX6MIX_INPUT_3_VOLUME 0x72D +#define ARIZONA_AIF1TX6MIX_INPUT_4_SOURCE 0x72E +#define ARIZONA_AIF1TX6MIX_INPUT_4_VOLUME 0x72F +#define ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE 0x730 +#define ARIZONA_AIF1TX7MIX_INPUT_1_VOLUME 0x731 +#define ARIZONA_AIF1TX7MIX_INPUT_2_SOURCE 0x732 +#define ARIZONA_AIF1TX7MIX_INPUT_2_VOLUME 0x733 +#define ARIZONA_AIF1TX7MIX_INPUT_3_SOURCE 0x734 +#define ARIZONA_AIF1TX7MIX_INPUT_3_VOLUME 0x735 +#define ARIZONA_AIF1TX7MIX_INPUT_4_SOURCE 0x736 +#define ARIZONA_AIF1TX7MIX_INPUT_4_VOLUME 0x737 +#define ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE 0x738 +#define ARIZONA_AIF1TX8MIX_INPUT_1_VOLUME 0x739 +#define ARIZONA_AIF1TX8MIX_INPUT_2_SOURCE 0x73A +#define ARIZONA_AIF1TX8MIX_INPUT_2_VOLUME 0x73B +#define ARIZONA_AIF1TX8MIX_INPUT_3_SOURCE 0x73C +#define ARIZONA_AIF1TX8MIX_INPUT_3_VOLUME 0x73D +#define ARIZONA_AIF1TX8MIX_INPUT_4_SOURCE 0x73E +#define ARIZONA_AIF1TX8MIX_INPUT_4_VOLUME 0x73F +#define ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE 0x740 +#define ARIZONA_AIF2TX1MIX_INPUT_1_VOLUME 0x741 +#define ARIZONA_AIF2TX1MIX_INPUT_2_SOURCE 0x742 +#define ARIZONA_AIF2TX1MIX_INPUT_2_VOLUME 0x743 +#define ARIZONA_AIF2TX1MIX_INPUT_3_SOURCE 0x744 +#define ARIZONA_AIF2TX1MIX_INPUT_3_VOLUME 0x745 +#define ARIZONA_AIF2TX1MIX_INPUT_4_SOURCE 0x746 +#define ARIZONA_AIF2TX1MIX_INPUT_4_VOLUME 0x747 +#define ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE 0x748 +#define ARIZONA_AIF2TX2MIX_INPUT_1_VOLUME 0x749 +#define ARIZONA_AIF2TX2MIX_INPUT_2_SOURCE 0x74A +#define ARIZONA_AIF2TX2MIX_INPUT_2_VOLUME 0x74B +#define ARIZONA_AIF2TX2MIX_INPUT_3_SOURCE 0x74C +#define ARIZONA_AIF2TX2MIX_INPUT_3_VOLUME 0x74D +#define ARIZONA_AIF2TX2MIX_INPUT_4_SOURCE 0x74E +#define ARIZONA_AIF2TX2MIX_INPUT_4_VOLUME 0x74F +#define ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE 0x780 +#define ARIZONA_AIF3TX1MIX_INPUT_1_VOLUME 0x781 +#define ARIZONA_AIF3TX1MIX_INPUT_2_SOURCE 0x782 +#define ARIZONA_AIF3TX1MIX_INPUT_2_VOLUME 0x783 +#define ARIZONA_AIF3TX1MIX_INPUT_3_SOURCE 0x784 +#define ARIZONA_AIF3TX1MIX_INPUT_3_VOLUME 0x785 +#define ARIZONA_AIF3TX1MIX_INPUT_4_SOURCE 0x786 +#define ARIZONA_AIF3TX1MIX_INPUT_4_VOLUME 0x787 +#define ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE 0x788 +#define ARIZONA_AIF3TX2MIX_INPUT_1_VOLUME 0x789 +#define ARIZONA_AIF3TX2MIX_INPUT_2_SOURCE 0x78A +#define ARIZONA_AIF3TX2MIX_INPUT_2_VOLUME 0x78B +#define ARIZONA_AIF3TX2MIX_INPUT_3_SOURCE 0x78C +#define ARIZONA_AIF3TX2MIX_INPUT_3_VOLUME 0x78D +#define ARIZONA_AIF3TX2MIX_INPUT_4_SOURCE 0x78E +#define ARIZONA_AIF3TX2MIX_INPUT_4_VOLUME 0x78F +#define ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE 0x7C0 +#define ARIZONA_SLIMTX1MIX_INPUT_1_VOLUME 0x7C1 +#define ARIZONA_SLIMTX1MIX_INPUT_2_SOURCE 0x7C2 +#define ARIZONA_SLIMTX1MIX_INPUT_2_VOLUME 0x7C3 +#define ARIZONA_SLIMTX1MIX_INPUT_3_SOURCE 0x7C4 +#define ARIZONA_SLIMTX1MIX_INPUT_3_VOLUME 0x7C5 +#define ARIZONA_SLIMTX1MIX_INPUT_4_SOURCE 0x7C6 +#define ARIZONA_SLIMTX1MIX_INPUT_4_VOLUME 0x7C7 +#define ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE 0x7C8 +#define ARIZONA_SLIMTX2MIX_INPUT_1_VOLUME 0x7C9 +#define ARIZONA_SLIMTX2MIX_INPUT_2_SOURCE 0x7CA +#define ARIZONA_SLIMTX2MIX_INPUT_2_VOLUME 0x7CB +#define ARIZONA_SLIMTX2MIX_INPUT_3_SOURCE 0x7CC +#define ARIZONA_SLIMTX2MIX_INPUT_3_VOLUME 0x7CD +#define ARIZONA_SLIMTX2MIX_INPUT_4_SOURCE 0x7CE +#define ARIZONA_SLIMTX2MIX_INPUT_4_VOLUME 0x7CF +#define ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE 0x7D0 +#define ARIZONA_SLIMTX3MIX_INPUT_1_VOLUME 0x7D1 +#define ARIZONA_SLIMTX3MIX_INPUT_2_SOURCE 0x7D2 +#define ARIZONA_SLIMTX3MIX_INPUT_2_VOLUME 0x7D3 +#define ARIZONA_SLIMTX3MIX_INPUT_3_SOURCE 0x7D4 +#define ARIZONA_SLIMTX3MIX_INPUT_3_VOLUME 0x7D5 +#define ARIZONA_SLIMTX3MIX_INPUT_4_SOURCE 0x7D6 +#define ARIZONA_SLIMTX3MIX_INPUT_4_VOLUME 0x7D7 +#define ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE 0x7D8 +#define ARIZONA_SLIMTX4MIX_INPUT_1_VOLUME 0x7D9 +#define ARIZONA_SLIMTX4MIX_INPUT_2_SOURCE 0x7DA +#define ARIZONA_SLIMTX4MIX_INPUT_2_VOLUME 0x7DB +#define ARIZONA_SLIMTX4MIX_INPUT_3_SOURCE 0x7DC +#define ARIZONA_SLIMTX4MIX_INPUT_3_VOLUME 0x7DD +#define ARIZONA_SLIMTX4MIX_INPUT_4_SOURCE 0x7DE +#define ARIZONA_SLIMTX4MIX_INPUT_4_VOLUME 0x7DF +#define ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE 0x7E0 +#define ARIZONA_SLIMTX5MIX_INPUT_1_VOLUME 0x7E1 +#define ARIZONA_SLIMTX5MIX_INPUT_2_SOURCE 0x7E2 +#define ARIZONA_SLIMTX5MIX_INPUT_2_VOLUME 0x7E3 +#define ARIZONA_SLIMTX5MIX_INPUT_3_SOURCE 0x7E4 +#define ARIZONA_SLIMTX5MIX_INPUT_3_VOLUME 0x7E5 +#define ARIZONA_SLIMTX5MIX_INPUT_4_SOURCE 0x7E6 +#define ARIZONA_SLIMTX5MIX_INPUT_4_VOLUME 0x7E7 +#define ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE 0x7E8 +#define ARIZONA_SLIMTX6MIX_INPUT_1_VOLUME 0x7E9 +#define ARIZONA_SLIMTX6MIX_INPUT_2_SOURCE 0x7EA +#define ARIZONA_SLIMTX6MIX_INPUT_2_VOLUME 0x7EB +#define ARIZONA_SLIMTX6MIX_INPUT_3_SOURCE 0x7EC +#define ARIZONA_SLIMTX6MIX_INPUT_3_VOLUME 0x7ED +#define ARIZONA_SLIMTX6MIX_INPUT_4_SOURCE 0x7EE +#define ARIZONA_SLIMTX6MIX_INPUT_4_VOLUME 0x7EF +#define ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE 0x7F0 +#define ARIZONA_SLIMTX7MIX_INPUT_1_VOLUME 0x7F1 +#define ARIZONA_SLIMTX7MIX_INPUT_2_SOURCE 0x7F2 +#define ARIZONA_SLIMTX7MIX_INPUT_2_VOLUME 0x7F3 +#define ARIZONA_SLIMTX7MIX_INPUT_3_SOURCE 0x7F4 +#define ARIZONA_SLIMTX7MIX_INPUT_3_VOLUME 0x7F5 +#define ARIZONA_SLIMTX7MIX_INPUT_4_SOURCE 0x7F6 +#define ARIZONA_SLIMTX7MIX_INPUT_4_VOLUME 0x7F7 +#define ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE 0x7F8 +#define ARIZONA_SLIMTX8MIX_INPUT_1_VOLUME 0x7F9 +#define ARIZONA_SLIMTX8MIX_INPUT_2_SOURCE 0x7FA +#define ARIZONA_SLIMTX8MIX_INPUT_2_VOLUME 0x7FB +#define ARIZONA_SLIMTX8MIX_INPUT_3_SOURCE 0x7FC +#define ARIZONA_SLIMTX8MIX_INPUT_3_VOLUME 0x7FD +#define ARIZONA_SLIMTX8MIX_INPUT_4_SOURCE 0x7FE +#define ARIZONA_SLIMTX8MIX_INPUT_4_VOLUME 0x7FF +#define ARIZONA_EQ1MIX_INPUT_1_SOURCE 0x880 +#define ARIZONA_EQ1MIX_INPUT_1_VOLUME 0x881 +#define ARIZONA_EQ1MIX_INPUT_2_SOURCE 0x882 +#define ARIZONA_EQ1MIX_INPUT_2_VOLUME 0x883 +#define ARIZONA_EQ1MIX_INPUT_3_SOURCE 0x884 +#define ARIZONA_EQ1MIX_INPUT_3_VOLUME 0x885 +#define ARIZONA_EQ1MIX_INPUT_4_SOURCE 0x886 +#define ARIZONA_EQ1MIX_INPUT_4_VOLUME 0x887 +#define ARIZONA_EQ2MIX_INPUT_1_SOURCE 0x888 +#define ARIZONA_EQ2MIX_INPUT_1_VOLUME 0x889 +#define ARIZONA_EQ2MIX_INPUT_2_SOURCE 0x88A +#define ARIZONA_EQ2MIX_INPUT_2_VOLUME 0x88B +#define ARIZONA_EQ2MIX_INPUT_3_SOURCE 0x88C +#define ARIZONA_EQ2MIX_INPUT_3_VOLUME 0x88D +#define ARIZONA_EQ2MIX_INPUT_4_SOURCE 0x88E +#define ARIZONA_EQ2MIX_INPUT_4_VOLUME 0x88F +#define ARIZONA_EQ3MIX_INPUT_1_SOURCE 0x890 +#define ARIZONA_EQ3MIX_INPUT_1_VOLUME 0x891 +#define ARIZONA_EQ3MIX_INPUT_2_SOURCE 0x892 +#define ARIZONA_EQ3MIX_INPUT_2_VOLUME 0x893 +#define ARIZONA_EQ3MIX_INPUT_3_SOURCE 0x894 +#define ARIZONA_EQ3MIX_INPUT_3_VOLUME 0x895 +#define ARIZONA_EQ3MIX_INPUT_4_SOURCE 0x896 +#define ARIZONA_EQ3MIX_INPUT_4_VOLUME 0x897 +#define ARIZONA_EQ4MIX_INPUT_1_SOURCE 0x898 +#define ARIZONA_EQ4MIX_INPUT_1_VOLUME 0x899 +#define ARIZONA_EQ4MIX_INPUT_2_SOURCE 0x89A +#define ARIZONA_EQ4MIX_INPUT_2_VOLUME 0x89B +#define ARIZONA_EQ4MIX_INPUT_3_SOURCE 0x89C +#define ARIZONA_EQ4MIX_INPUT_3_VOLUME 0x89D +#define ARIZONA_EQ4MIX_INPUT_4_SOURCE 0x89E +#define ARIZONA_EQ4MIX_INPUT_4_VOLUME 0x89F +#define ARIZONA_DRC1LMIX_INPUT_1_SOURCE 0x8C0 +#define ARIZONA_DRC1LMIX_INPUT_1_VOLUME 0x8C1 +#define ARIZONA_DRC1LMIX_INPUT_2_SOURCE 0x8C2 +#define ARIZONA_DRC1LMIX_INPUT_2_VOLUME 0x8C3 +#define ARIZONA_DRC1LMIX_INPUT_3_SOURCE 0x8C4 +#define ARIZONA_DRC1LMIX_INPUT_3_VOLUME 0x8C5 +#define ARIZONA_DRC1LMIX_INPUT_4_SOURCE 0x8C6 +#define ARIZONA_DRC1LMIX_INPUT_4_VOLUME 0x8C7 +#define ARIZONA_DRC1RMIX_INPUT_1_SOURCE 0x8C8 +#define ARIZONA_DRC1RMIX_INPUT_1_VOLUME 0x8C9 +#define ARIZONA_DRC1RMIX_INPUT_2_SOURCE 0x8CA +#define ARIZONA_DRC1RMIX_INPUT_2_VOLUME 0x8CB +#define ARIZONA_DRC1RMIX_INPUT_3_SOURCE 0x8CC +#define ARIZONA_DRC1RMIX_INPUT_3_VOLUME 0x8CD +#define ARIZONA_DRC1RMIX_INPUT_4_SOURCE 0x8CE +#define ARIZONA_DRC1RMIX_INPUT_4_VOLUME 0x8CF +#define ARIZONA_DRC2LMIX_INPUT_1_SOURCE 0x8D0 +#define ARIZONA_DRC2LMIX_INPUT_1_VOLUME 0x8D1 +#define ARIZONA_DRC2LMIX_INPUT_2_SOURCE 0x8D2 +#define ARIZONA_DRC2LMIX_INPUT_2_VOLUME 0x8D3 +#define ARIZONA_DRC2LMIX_INPUT_3_SOURCE 0x8D4 +#define ARIZONA_DRC2LMIX_INPUT_3_VOLUME 0x8D5 +#define ARIZONA_DRC2LMIX_INPUT_4_SOURCE 0x8D6 +#define ARIZONA_DRC2LMIX_INPUT_4_VOLUME 0x8D7 +#define ARIZONA_DRC2RMIX_INPUT_1_SOURCE 0x8D8 +#define ARIZONA_DRC2RMIX_INPUT_1_VOLUME 0x8D9 +#define ARIZONA_DRC2RMIX_INPUT_2_SOURCE 0x8DA +#define ARIZONA_DRC2RMIX_INPUT_2_VOLUME 0x8DB +#define ARIZONA_DRC2RMIX_INPUT_3_SOURCE 0x8DC +#define ARIZONA_DRC2RMIX_INPUT_3_VOLUME 0x8DD +#define ARIZONA_DRC2RMIX_INPUT_4_SOURCE 0x8DE +#define ARIZONA_DRC2RMIX_INPUT_4_VOLUME 0x8DF +#define ARIZONA_HPLP1MIX_INPUT_1_SOURCE 0x900 +#define ARIZONA_HPLP1MIX_INPUT_1_VOLUME 0x901 +#define ARIZONA_HPLP1MIX_INPUT_2_SOURCE 0x902 +#define ARIZONA_HPLP1MIX_INPUT_2_VOLUME 0x903 +#define ARIZONA_HPLP1MIX_INPUT_3_SOURCE 0x904 +#define ARIZONA_HPLP1MIX_INPUT_3_VOLUME 0x905 +#define ARIZONA_HPLP1MIX_INPUT_4_SOURCE 0x906 +#define ARIZONA_HPLP1MIX_INPUT_4_VOLUME 0x907 +#define ARIZONA_HPLP2MIX_INPUT_1_SOURCE 0x908 +#define ARIZONA_HPLP2MIX_INPUT_1_VOLUME 0x909 +#define ARIZONA_HPLP2MIX_INPUT_2_SOURCE 0x90A +#define ARIZONA_HPLP2MIX_INPUT_2_VOLUME 0x90B +#define ARIZONA_HPLP2MIX_INPUT_3_SOURCE 0x90C +#define ARIZONA_HPLP2MIX_INPUT_3_VOLUME 0x90D +#define ARIZONA_HPLP2MIX_INPUT_4_SOURCE 0x90E +#define ARIZONA_HPLP2MIX_INPUT_4_VOLUME 0x90F +#define ARIZONA_HPLP3MIX_INPUT_1_SOURCE 0x910 +#define ARIZONA_HPLP3MIX_INPUT_1_VOLUME 0x911 +#define ARIZONA_HPLP3MIX_INPUT_2_SOURCE 0x912 +#define ARIZONA_HPLP3MIX_INPUT_2_VOLUME 0x913 +#define ARIZONA_HPLP3MIX_INPUT_3_SOURCE 0x914 +#define ARIZONA_HPLP3MIX_INPUT_3_VOLUME 0x915 +#define ARIZONA_HPLP3MIX_INPUT_4_SOURCE 0x916 +#define ARIZONA_HPLP3MIX_INPUT_4_VOLUME 0x917 +#define ARIZONA_HPLP4MIX_INPUT_1_SOURCE 0x918 +#define ARIZONA_HPLP4MIX_INPUT_1_VOLUME 0x919 +#define ARIZONA_HPLP4MIX_INPUT_2_SOURCE 0x91A +#define ARIZONA_HPLP4MIX_INPUT_2_VOLUME 0x91B +#define ARIZONA_HPLP4MIX_INPUT_3_SOURCE 0x91C +#define ARIZONA_HPLP4MIX_INPUT_3_VOLUME 0x91D +#define ARIZONA_HPLP4MIX_INPUT_4_SOURCE 0x91E +#define ARIZONA_HPLP4MIX_INPUT_4_VOLUME 0x91F +#define ARIZONA_DSP1LMIX_INPUT_1_SOURCE 0x940 +#define ARIZONA_DSP1LMIX_INPUT_1_VOLUME 0x941 +#define ARIZONA_DSP1LMIX_INPUT_2_SOURCE 0x942 +#define ARIZONA_DSP1LMIX_INPUT_2_VOLUME 0x943 +#define ARIZONA_DSP1LMIX_INPUT_3_SOURCE 0x944 +#define ARIZONA_DSP1LMIX_INPUT_3_VOLUME 0x945 +#define ARIZONA_DSP1LMIX_INPUT_4_SOURCE 0x946 +#define ARIZONA_DSP1LMIX_INPUT_4_VOLUME 0x947 +#define ARIZONA_DSP1RMIX_INPUT_1_SOURCE 0x948 +#define ARIZONA_DSP1RMIX_INPUT_1_VOLUME 0x949 +#define ARIZONA_DSP1RMIX_INPUT_2_SOURCE 0x94A +#define ARIZONA_DSP1RMIX_INPUT_2_VOLUME 0x94B +#define ARIZONA_DSP1RMIX_INPUT_3_SOURCE 0x94C +#define ARIZONA_DSP1RMIX_INPUT_3_VOLUME 0x94D +#define ARIZONA_DSP1RMIX_INPUT_4_SOURCE 0x94E +#define ARIZONA_DSP1RMIX_INPUT_4_VOLUME 0x94F +#define ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE 0x950 +#define ARIZONA_DSP1AUX2MIX_INPUT_1_SOURCE 0x958 +#define ARIZONA_DSP1AUX3MIX_INPUT_1_SOURCE 0x960 +#define ARIZONA_DSP1AUX4MIX_INPUT_1_SOURCE 0x968 +#define ARIZONA_DSP1AUX5MIX_INPUT_1_SOURCE 0x970 +#define ARIZONA_DSP1AUX6MIX_INPUT_1_SOURCE 0x978 +#define ARIZONA_ASRC1LMIX_INPUT_1_SOURCE 0xA80 +#define ARIZONA_ASRC1RMIX_INPUT_1_SOURCE 0xA88 +#define ARIZONA_ASRC2LMIX_INPUT_1_SOURCE 0xA90 +#define ARIZONA_ASRC2RMIX_INPUT_1_SOURCE 0xA98 +#define ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE 0xB00 +#define ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE 0xB08 +#define ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE 0xB20 +#define ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE 0xB28 +#define ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE 0xB40 +#define ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE 0xB48 +#define ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE 0xB60 +#define ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE 0xB68 +#define ARIZONA_GPIO1_CTRL 0xC00 +#define ARIZONA_GPIO2_CTRL 0xC01 +#define ARIZONA_GPIO3_CTRL 0xC02 +#define ARIZONA_GPIO4_CTRL 0xC03 +#define ARIZONA_GPIO5_CTRL 0xC04 +#define ARIZONA_IRQ_CTRL_1 0xC0F +#define ARIZONA_GPIO_DEBOUNCE_CONFIG 0xC10 +#define ARIZONA_MISC_PAD_CTRL_1 0xC20 +#define ARIZONA_MISC_PAD_CTRL_2 0xC21 +#define ARIZONA_MISC_PAD_CTRL_3 0xC22 +#define ARIZONA_MISC_PAD_CTRL_4 0xC23 +#define ARIZONA_MISC_PAD_CTRL_5 0xC24 +#define ARIZONA_MISC_PAD_CTRL_6 0xC25 +#define ARIZONA_INTERRUPT_STATUS_1 0xD00 +#define ARIZONA_INTERRUPT_STATUS_2 0xD01 +#define ARIZONA_INTERRUPT_STATUS_3 0xD02 +#define ARIZONA_INTERRUPT_STATUS_4 0xD03 +#define ARIZONA_INTERRUPT_STATUS_5 0xD04 +#define ARIZONA_INTERRUPT_STATUS_1_MASK 0xD08 +#define ARIZONA_INTERRUPT_STATUS_2_MASK 0xD09 +#define ARIZONA_INTERRUPT_STATUS_3_MASK 0xD0A +#define ARIZONA_INTERRUPT_STATUS_4_MASK 0xD0B +#define ARIZONA_INTERRUPT_STATUS_5_MASK 0xD0C +#define ARIZONA_INTERRUPT_CONTROL 0xD0F +#define ARIZONA_IRQ2_STATUS_1 0xD10 +#define ARIZONA_IRQ2_STATUS_2 0xD11 +#define ARIZONA_IRQ2_STATUS_3 0xD12 +#define ARIZONA_IRQ2_STATUS_4 0xD13 +#define ARIZONA_IRQ2_STATUS_5 0xD14 +#define ARIZONA_IRQ2_STATUS_1_MASK 0xD18 +#define ARIZONA_IRQ2_STATUS_2_MASK 0xD19 +#define ARIZONA_IRQ2_STATUS_3_MASK 0xD1A +#define ARIZONA_IRQ2_STATUS_4_MASK 0xD1B +#define ARIZONA_IRQ2_STATUS_5_MASK 0xD1C +#define ARIZONA_IRQ2_CONTROL 0xD1F +#define ARIZONA_INTERRUPT_RAW_STATUS_2 0xD20 +#define ARIZONA_INTERRUPT_RAW_STATUS_3 0xD21 +#define ARIZONA_INTERRUPT_RAW_STATUS_4 0xD22 +#define ARIZONA_INTERRUPT_RAW_STATUS_5 0xD23 +#define ARIZONA_INTERRUPT_RAW_STATUS_6 0xD24 +#define ARIZONA_INTERRUPT_RAW_STATUS_7 0xD25 +#define ARIZONA_INTERRUPT_RAW_STATUS_8 0xD26 +#define ARIZONA_IRQ_PIN_STATUS 0xD40 +#define ARIZONA_ADSP2_IRQ0 0xD41 +#define ARIZONA_AOD_WKUP_AND_TRIG 0xD50 +#define ARIZONA_AOD_IRQ1 0xD51 +#define ARIZONA_AOD_IRQ2 0xD52 +#define ARIZONA_AOD_IRQ_MASK_IRQ1 0xD53 +#define ARIZONA_AOD_IRQ_MASK_IRQ2 0xD54 +#define ARIZONA_AOD_IRQ_RAW_STATUS 0xD55 +#define ARIZONA_JACK_DETECT_DEBOUNCE 0xD56 +#define ARIZONA_FX_CTRL1 0xE00 +#define ARIZONA_FX_CTRL2 0xE01 +#define ARIZONA_EQ1_1 0xE10 +#define ARIZONA_EQ1_2 0xE11 +#define ARIZONA_EQ1_3 0xE12 +#define ARIZONA_EQ1_4 0xE13 +#define ARIZONA_EQ1_5 0xE14 +#define ARIZONA_EQ1_6 0xE15 +#define ARIZONA_EQ1_7 0xE16 +#define ARIZONA_EQ1_8 0xE17 +#define ARIZONA_EQ1_9 0xE18 +#define ARIZONA_EQ1_10 0xE19 +#define ARIZONA_EQ1_11 0xE1A +#define ARIZONA_EQ1_12 0xE1B +#define ARIZONA_EQ1_13 0xE1C +#define ARIZONA_EQ1_14 0xE1D +#define ARIZONA_EQ1_15 0xE1E +#define ARIZONA_EQ1_16 0xE1F +#define ARIZONA_EQ1_17 0xE20 +#define ARIZONA_EQ1_18 0xE21 +#define ARIZONA_EQ1_19 0xE22 +#define ARIZONA_EQ1_20 0xE23 +#define ARIZONA_EQ1_21 0xE24 +#define ARIZONA_EQ2_1 0xE26 +#define ARIZONA_EQ2_2 0xE27 +#define ARIZONA_EQ2_3 0xE28 +#define ARIZONA_EQ2_4 0xE29 +#define ARIZONA_EQ2_5 0xE2A +#define ARIZONA_EQ2_6 0xE2B +#define ARIZONA_EQ2_7 0xE2C +#define ARIZONA_EQ2_8 0xE2D +#define ARIZONA_EQ2_9 0xE2E +#define ARIZONA_EQ2_10 0xE2F +#define ARIZONA_EQ2_11 0xE30 +#define ARIZONA_EQ2_12 0xE31 +#define ARIZONA_EQ2_13 0xE32 +#define ARIZONA_EQ2_14 0xE33 +#define ARIZONA_EQ2_15 0xE34 +#define ARIZONA_EQ2_16 0xE35 +#define ARIZONA_EQ2_17 0xE36 +#define ARIZONA_EQ2_18 0xE37 +#define ARIZONA_EQ2_19 0xE38 +#define ARIZONA_EQ2_20 0xE39 +#define ARIZONA_EQ2_21 0xE3A +#define ARIZONA_EQ3_1 0xE3C +#define ARIZONA_EQ3_2 0xE3D +#define ARIZONA_EQ3_3 0xE3E +#define ARIZONA_EQ3_4 0xE3F +#define ARIZONA_EQ3_5 0xE40 +#define ARIZONA_EQ3_6 0xE41 +#define ARIZONA_EQ3_7 0xE42 +#define ARIZONA_EQ3_8 0xE43 +#define ARIZONA_EQ3_9 0xE44 +#define ARIZONA_EQ3_10 0xE45 +#define ARIZONA_EQ3_11 0xE46 +#define ARIZONA_EQ3_12 0xE47 +#define ARIZONA_EQ3_13 0xE48 +#define ARIZONA_EQ3_14 0xE49 +#define ARIZONA_EQ3_15 0xE4A +#define ARIZONA_EQ3_16 0xE4B +#define ARIZONA_EQ3_17 0xE4C +#define ARIZONA_EQ3_18 0xE4D +#define ARIZONA_EQ3_19 0xE4E +#define ARIZONA_EQ3_20 0xE4F +#define ARIZONA_EQ3_21 0xE50 +#define ARIZONA_EQ4_1 0xE52 +#define ARIZONA_EQ4_2 0xE53 +#define ARIZONA_EQ4_3 0xE54 +#define ARIZONA_EQ4_4 0xE55 +#define ARIZONA_EQ4_5 0xE56 +#define ARIZONA_EQ4_6 0xE57 +#define ARIZONA_EQ4_7 0xE58 +#define ARIZONA_EQ4_8 0xE59 +#define ARIZONA_EQ4_9 0xE5A +#define ARIZONA_EQ4_10 0xE5B +#define ARIZONA_EQ4_11 0xE5C +#define ARIZONA_EQ4_12 0xE5D +#define ARIZONA_EQ4_13 0xE5E +#define ARIZONA_EQ4_14 0xE5F +#define ARIZONA_EQ4_15 0xE60 +#define ARIZONA_EQ4_16 0xE61 +#define ARIZONA_EQ4_17 0xE62 +#define ARIZONA_EQ4_18 0xE63 +#define ARIZONA_EQ4_19 0xE64 +#define ARIZONA_EQ4_20 0xE65 +#define ARIZONA_EQ4_21 0xE66 +#define ARIZONA_DRC1_CTRL1 0xE80 +#define ARIZONA_DRC1_CTRL2 0xE81 +#define ARIZONA_DRC1_CTRL3 0xE82 +#define ARIZONA_DRC1_CTRL4 0xE83 +#define ARIZONA_DRC1_CTRL5 0xE84 +#define ARIZONA_DRC2_CTRL1 0xE89 +#define ARIZONA_DRC2_CTRL2 0xE8A +#define ARIZONA_DRC2_CTRL3 0xE8B +#define ARIZONA_DRC2_CTRL4 0xE8C +#define ARIZONA_DRC2_CTRL5 0xE8D +#define ARIZONA_HPLPF1_1 0xEC0 +#define ARIZONA_HPLPF1_2 0xEC1 +#define ARIZONA_HPLPF2_1 0xEC4 +#define ARIZONA_HPLPF2_2 0xEC5 +#define ARIZONA_HPLPF3_1 0xEC8 +#define ARIZONA_HPLPF3_2 0xEC9 +#define ARIZONA_HPLPF4_1 0xECC +#define ARIZONA_HPLPF4_2 0xECD +#define ARIZONA_ASRC_ENABLE 0xEE0 +#define ARIZONA_ASRC_RATE1 0xEE2 +#define ARIZONA_ASRC_RATE2 0xEE3 +#define ARIZONA_ISRC_1_CTRL_1 0xEF0 +#define ARIZONA_ISRC_1_CTRL_2 0xEF1 +#define ARIZONA_ISRC_1_CTRL_3 0xEF2 +#define ARIZONA_ISRC_2_CTRL_1 0xEF3 +#define ARIZONA_ISRC_2_CTRL_2 0xEF4 +#define ARIZONA_ISRC_2_CTRL_3 0xEF5 +#define ARIZONA_ISRC_3_CTRL_1 0xEF6 +#define ARIZONA_ISRC_3_CTRL_2 0xEF7 +#define ARIZONA_ISRC_3_CTRL_3 0xEF8 +#define ARIZONA_DSP1_CONTROL_1 0x1100 +#define ARIZONA_DSP1_CLOCKING_1 0x1101 +#define ARIZONA_DSP1_STATUS_1 0x1104 +#define ARIZONA_DSP1_STATUS_2 0x1105 + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - software reset + */ +#define ARIZONA_SW_RST_DEV_ID1_MASK 0xFFFF /* SW_RST_DEV_ID1 - [15:0] */ +#define ARIZONA_SW_RST_DEV_ID1_SHIFT 0 /* SW_RST_DEV_ID1 - [15:0] */ +#define ARIZONA_SW_RST_DEV_ID1_WIDTH 16 /* SW_RST_DEV_ID1 - [15:0] */ + +/* + * R1 (0x01) - Device Revision + */ +#define ARIZONA_DEVICE_REVISION_MASK 0x00FF /* DEVICE_REVISION - [7:0] */ +#define ARIZONA_DEVICE_REVISION_SHIFT 0 /* DEVICE_REVISION - [7:0] */ +#define ARIZONA_DEVICE_REVISION_WIDTH 8 /* DEVICE_REVISION - [7:0] */ + +/* + * R8 (0x08) - Ctrl IF SPI CFG 1 + */ +#define ARIZONA_SPI_CFG 0x0010 /* SPI_CFG */ +#define ARIZONA_SPI_CFG_MASK 0x0010 /* SPI_CFG */ +#define ARIZONA_SPI_CFG_SHIFT 4 /* SPI_CFG */ +#define ARIZONA_SPI_CFG_WIDTH 1 /* SPI_CFG */ +#define ARIZONA_SPI_4WIRE 0x0008 /* SPI_4WIRE */ +#define ARIZONA_SPI_4WIRE_MASK 0x0008 /* SPI_4WIRE */ +#define ARIZONA_SPI_4WIRE_SHIFT 3 /* SPI_4WIRE */ +#define ARIZONA_SPI_4WIRE_WIDTH 1 /* SPI_4WIRE */ +#define ARIZONA_SPI_AUTO_INC_MASK 0x0003 /* SPI_AUTO_INC - [1:0] */ +#define ARIZONA_SPI_AUTO_INC_SHIFT 0 /* SPI_AUTO_INC - [1:0] */ +#define ARIZONA_SPI_AUTO_INC_WIDTH 2 /* SPI_AUTO_INC - [1:0] */ + +/* + * R9 (0x09) - Ctrl IF I2C1 CFG 1 + */ +#define ARIZONA_I2C1_AUTO_INC_MASK 0x0003 /* I2C1_AUTO_INC - [1:0] */ +#define ARIZONA_I2C1_AUTO_INC_SHIFT 0 /* I2C1_AUTO_INC - [1:0] */ +#define ARIZONA_I2C1_AUTO_INC_WIDTH 2 /* I2C1_AUTO_INC - [1:0] */ + +/* + * R13 (0x0D) - Ctrl IF Status 1 + */ +#define ARIZONA_I2C1_BUSY 0x0020 /* I2C1_BUSY */ +#define ARIZONA_I2C1_BUSY_MASK 0x0020 /* I2C1_BUSY */ +#define ARIZONA_I2C1_BUSY_SHIFT 5 /* I2C1_BUSY */ +#define ARIZONA_I2C1_BUSY_WIDTH 1 /* I2C1_BUSY */ +#define ARIZONA_SPI_BUSY 0x0010 /* SPI_BUSY */ +#define ARIZONA_SPI_BUSY_MASK 0x0010 /* SPI_BUSY */ +#define ARIZONA_SPI_BUSY_SHIFT 4 /* SPI_BUSY */ +#define ARIZONA_SPI_BUSY_WIDTH 1 /* SPI_BUSY */ + +/* + * R22 (0x16) - Write Sequencer Ctrl 0 + */ +#define ARIZONA_WSEQ_ABORT 0x0800 /* WSEQ_ABORT */ +#define ARIZONA_WSEQ_ABORT_MASK 0x0800 /* WSEQ_ABORT */ +#define ARIZONA_WSEQ_ABORT_SHIFT 11 /* WSEQ_ABORT */ +#define ARIZONA_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */ +#define ARIZONA_WSEQ_START 0x0400 /* WSEQ_START */ +#define ARIZONA_WSEQ_START_MASK 0x0400 /* WSEQ_START */ +#define ARIZONA_WSEQ_START_SHIFT 10 /* WSEQ_START */ +#define ARIZONA_WSEQ_START_WIDTH 1 /* WSEQ_START */ +#define ARIZONA_WSEQ_ENA 0x0200 /* WSEQ_ENA */ +#define ARIZONA_WSEQ_ENA_MASK 0x0200 /* WSEQ_ENA */ +#define ARIZONA_WSEQ_ENA_SHIFT 9 /* WSEQ_ENA */ +#define ARIZONA_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */ +#define ARIZONA_WSEQ_START_INDEX_MASK 0x01FF /* WSEQ_START_INDEX - [8:0] */ +#define ARIZONA_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [8:0] */ +#define ARIZONA_WSEQ_START_INDEX_WIDTH 9 /* WSEQ_START_INDEX - [8:0] */ + +/* + * R23 (0x17) - Write Sequencer Ctrl 1 + */ +#define ARIZONA_WSEQ_BUSY 0x0200 /* WSEQ_BUSY */ +#define ARIZONA_WSEQ_BUSY_MASK 0x0200 /* WSEQ_BUSY */ +#define ARIZONA_WSEQ_BUSY_SHIFT 9 /* WSEQ_BUSY */ +#define ARIZONA_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */ +#define ARIZONA_WSEQ_CURRENT_INDEX_MASK 0x01FF /* WSEQ_CURRENT_INDEX - [8:0] */ +#define ARIZONA_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [8:0] */ +#define ARIZONA_WSEQ_CURRENT_INDEX_WIDTH 9 /* WSEQ_CURRENT_INDEX - [8:0] */ + +/* + * R24 (0x18) - Write Sequencer Ctrl 2 + */ +#define ARIZONA_LOAD_DEFAULTS 0x0002 /* LOAD_DEFAULTS */ +#define ARIZONA_LOAD_DEFAULTS_MASK 0x0002 /* LOAD_DEFAULTS */ +#define ARIZONA_LOAD_DEFAULTS_SHIFT 1 /* LOAD_DEFAULTS */ +#define ARIZONA_LOAD_DEFAULTS_WIDTH 1 /* LOAD_DEFAULTS */ +#define ARIZONA_WSEQ_LOAD_MEM 0x0001 /* WSEQ_LOAD_MEM */ +#define ARIZONA_WSEQ_LOAD_MEM_MASK 0x0001 /* WSEQ_LOAD_MEM */ +#define ARIZONA_WSEQ_LOAD_MEM_SHIFT 0 /* WSEQ_LOAD_MEM */ +#define ARIZONA_WSEQ_LOAD_MEM_WIDTH 1 /* WSEQ_LOAD_MEM */ + +/* + * R26 (0x1A) - Write Sequencer PROM + */ +#define ARIZONA_WSEQ_OTP_WRITE 0x0001 /* WSEQ_OTP_WRITE */ +#define ARIZONA_WSEQ_OTP_WRITE_MASK 0x0001 /* WSEQ_OTP_WRITE */ +#define ARIZONA_WSEQ_OTP_WRITE_SHIFT 0 /* WSEQ_OTP_WRITE */ +#define ARIZONA_WSEQ_OTP_WRITE_WIDTH 1 /* WSEQ_OTP_WRITE */ + +/* + * R32 (0x20) - Tone Generator 1 + */ +#define ARIZONA_TONE_RATE_MASK 0x7800 /* TONE_RATE - [14:11] */ +#define ARIZONA_TONE_RATE_SHIFT 11 /* TONE_RATE - [14:11] */ +#define ARIZONA_TONE_RATE_WIDTH 4 /* TONE_RATE - [14:11] */ +#define ARIZONA_TONE_OFFSET_MASK 0x0300 /* TONE_OFFSET - [9:8] */ +#define ARIZONA_TONE_OFFSET_SHIFT 8 /* TONE_OFFSET - [9:8] */ +#define ARIZONA_TONE_OFFSET_WIDTH 2 /* TONE_OFFSET - [9:8] */ +#define ARIZONA_TONE2_OVD 0x0020 /* TONE2_OVD */ +#define ARIZONA_TONE2_OVD_MASK 0x0020 /* TONE2_OVD */ +#define ARIZONA_TONE2_OVD_SHIFT 5 /* TONE2_OVD */ +#define ARIZONA_TONE2_OVD_WIDTH 1 /* TONE2_OVD */ +#define ARIZONA_TONE1_OVD 0x0010 /* TONE1_OVD */ +#define ARIZONA_TONE1_OVD_MASK 0x0010 /* TONE1_OVD */ +#define ARIZONA_TONE1_OVD_SHIFT 4 /* TONE1_OVD */ +#define ARIZONA_TONE1_OVD_WIDTH 1 /* TONE1_OVD */ +#define ARIZONA_TONE2_ENA 0x0002 /* TONE2_ENA */ +#define ARIZONA_TONE2_ENA_MASK 0x0002 /* TONE2_ENA */ +#define ARIZONA_TONE2_ENA_SHIFT 1 /* TONE2_ENA */ +#define ARIZONA_TONE2_ENA_WIDTH 1 /* TONE2_ENA */ +#define ARIZONA_TONE1_ENA 0x0001 /* TONE1_ENA */ +#define ARIZONA_TONE1_ENA_MASK 0x0001 /* TONE1_ENA */ +#define ARIZONA_TONE1_ENA_SHIFT 0 /* TONE1_ENA */ +#define ARIZONA_TONE1_ENA_WIDTH 1 /* TONE1_ENA */ + +/* + * R33 (0x21) - Tone Generator 2 + */ +#define ARIZONA_TONE1_LVL_0_MASK 0xFFFF /* TONE1_LVL - [15:0] */ +#define ARIZONA_TONE1_LVL_0_SHIFT 0 /* TONE1_LVL - [15:0] */ +#define ARIZONA_TONE1_LVL_0_WIDTH 16 /* TONE1_LVL - [15:0] */ + +/* + * R34 (0x22) - Tone Generator 3 + */ +#define ARIZONA_TONE1_LVL_MASK 0x00FF /* TONE1_LVL - [7:0] */ +#define ARIZONA_TONE1_LVL_SHIFT 0 /* TONE1_LVL - [7:0] */ +#define ARIZONA_TONE1_LVL_WIDTH 8 /* TONE1_LVL - [7:0] */ + +/* + * R35 (0x23) - Tone Generator 4 + */ +#define ARIZONA_TONE2_LVL_0_MASK 0xFFFF /* TONE2_LVL - [15:0] */ +#define ARIZONA_TONE2_LVL_0_SHIFT 0 /* TONE2_LVL - [15:0] */ +#define ARIZONA_TONE2_LVL_0_WIDTH 16 /* TONE2_LVL - [15:0] */ + +/* + * R36 (0x24) - Tone Generator 5 + */ +#define ARIZONA_TONE2_LVL_MASK 0x00FF /* TONE2_LVL - [7:0] */ +#define ARIZONA_TONE2_LVL_SHIFT 0 /* TONE2_LVL - [7:0] */ +#define ARIZONA_TONE2_LVL_WIDTH 8 /* TONE2_LVL - [7:0] */ + +/* + * R48 (0x30) - PWM Drive 1 + */ +#define ARIZONA_PWM_RATE_MASK 0x7800 /* PWM_RATE - [14:11] */ +#define ARIZONA_PWM_RATE_SHIFT 11 /* PWM_RATE - [14:11] */ +#define ARIZONA_PWM_RATE_WIDTH 4 /* PWM_RATE - [14:11] */ +#define ARIZONA_PWM_CLK_SEL_MASK 0x0700 /* PWM_CLK_SEL - [10:8] */ +#define ARIZONA_PWM_CLK_SEL_SHIFT 8 /* PWM_CLK_SEL - [10:8] */ +#define ARIZONA_PWM_CLK_SEL_WIDTH 3 /* PWM_CLK_SEL - [10:8] */ +#define ARIZONA_PWM2_OVD 0x0020 /* PWM2_OVD */ +#define ARIZONA_PWM2_OVD_MASK 0x0020 /* PWM2_OVD */ +#define ARIZONA_PWM2_OVD_SHIFT 5 /* PWM2_OVD */ +#define ARIZONA_PWM2_OVD_WIDTH 1 /* PWM2_OVD */ +#define ARIZONA_PWM1_OVD 0x0010 /* PWM1_OVD */ +#define ARIZONA_PWM1_OVD_MASK 0x0010 /* PWM1_OVD */ +#define ARIZONA_PWM1_OVD_SHIFT 4 /* PWM1_OVD */ +#define ARIZONA_PWM1_OVD_WIDTH 1 /* PWM1_OVD */ +#define ARIZONA_PWM2_ENA 0x0002 /* PWM2_ENA */ +#define ARIZONA_PWM2_ENA_MASK 0x0002 /* PWM2_ENA */ +#define ARIZONA_PWM2_ENA_SHIFT 1 /* PWM2_ENA */ +#define ARIZONA_PWM2_ENA_WIDTH 1 /* PWM2_ENA */ +#define ARIZONA_PWM1_ENA 0x0001 /* PWM1_ENA */ +#define ARIZONA_PWM1_ENA_MASK 0x0001 /* PWM1_ENA */ +#define ARIZONA_PWM1_ENA_SHIFT 0 /* PWM1_ENA */ +#define ARIZONA_PWM1_ENA_WIDTH 1 /* PWM1_ENA */ + +/* + * R49 (0x31) - PWM Drive 2 + */ +#define ARIZONA_PWM1_LVL_MASK 0x03FF /* PWM1_LVL - [9:0] */ +#define ARIZONA_PWM1_LVL_SHIFT 0 /* PWM1_LVL - [9:0] */ +#define ARIZONA_PWM1_LVL_WIDTH 10 /* PWM1_LVL - [9:0] */ + +/* + * R50 (0x32) - PWM Drive 3 + */ +#define ARIZONA_PWM2_LVL_MASK 0x03FF /* PWM2_LVL - [9:0] */ +#define ARIZONA_PWM2_LVL_SHIFT 0 /* PWM2_LVL - [9:0] */ +#define ARIZONA_PWM2_LVL_WIDTH 10 /* PWM2_LVL - [9:0] */ + +/* + * R64 (0x40) - Wake control + */ +#define ARIZONA_WKUP_GP5_FALL 0x0020 /* WKUP_GP5_FALL */ +#define ARIZONA_WKUP_GP5_FALL_MASK 0x0020 /* WKUP_GP5_FALL */ +#define ARIZONA_WKUP_GP5_FALL_SHIFT 5 /* WKUP_GP5_FALL */ +#define ARIZONA_WKUP_GP5_FALL_WIDTH 1 /* WKUP_GP5_FALL */ +#define ARIZONA_WKUP_GP5_RISE 0x0010 /* WKUP_GP5_RISE */ +#define ARIZONA_WKUP_GP5_RISE_MASK 0x0010 /* WKUP_GP5_RISE */ +#define ARIZONA_WKUP_GP5_RISE_SHIFT 4 /* WKUP_GP5_RISE */ +#define ARIZONA_WKUP_GP5_RISE_WIDTH 1 /* WKUP_GP5_RISE */ +#define ARIZONA_WKUP_JD1_FALL 0x0008 /* WKUP_JD1_FALL */ +#define ARIZONA_WKUP_JD1_FALL_MASK 0x0008 /* WKUP_JD1_FALL */ +#define ARIZONA_WKUP_JD1_FALL_SHIFT 3 /* WKUP_JD1_FALL */ +#define ARIZONA_WKUP_JD1_FALL_WIDTH 1 /* WKUP_JD1_FALL */ +#define ARIZONA_WKUP_JD1_RISE 0x0004 /* WKUP_JD1_RISE */ +#define ARIZONA_WKUP_JD1_RISE_MASK 0x0004 /* WKUP_JD1_RISE */ +#define ARIZONA_WKUP_JD1_RISE_SHIFT 2 /* WKUP_JD1_RISE */ +#define ARIZONA_WKUP_JD1_RISE_WIDTH 1 /* WKUP_JD1_RISE */ +#define ARIZONA_WKUP_JD2_FALL 0x0002 /* WKUP_JD2_FALL */ +#define ARIZONA_WKUP_JD2_FALL_MASK 0x0002 /* WKUP_JD2_FALL */ +#define ARIZONA_WKUP_JD2_FALL_SHIFT 1 /* WKUP_JD2_FALL */ +#define ARIZONA_WKUP_JD2_FALL_WIDTH 1 /* WKUP_JD2_FALL */ +#define ARIZONA_WKUP_JD2_RISE 0x0001 /* WKUP_JD2_RISE */ +#define ARIZONA_WKUP_JD2_RISE_MASK 0x0001 /* WKUP_JD2_RISE */ +#define ARIZONA_WKUP_JD2_RISE_SHIFT 0 /* WKUP_JD2_RISE */ +#define ARIZONA_WKUP_JD2_RISE_WIDTH 1 /* WKUP_JD2_RISE */ + +/* + * R65 (0x41) - Sequence control + */ +#define ARIZONA_WSEQ_ENA_GP5_FALL 0x0020 /* WSEQ_ENA_GP5_FALL */ +#define ARIZONA_WSEQ_ENA_GP5_FALL_MASK 0x0020 /* WSEQ_ENA_GP5_FALL */ +#define ARIZONA_WSEQ_ENA_GP5_FALL_SHIFT 5 /* WSEQ_ENA_GP5_FALL */ +#define ARIZONA_WSEQ_ENA_GP5_FALL_WIDTH 1 /* WSEQ_ENA_GP5_FALL */ +#define ARIZONA_WSEQ_ENA_GP5_RISE 0x0010 /* WSEQ_ENA_GP5_RISE */ +#define ARIZONA_WSEQ_ENA_GP5_RISE_MASK 0x0010 /* WSEQ_ENA_GP5_RISE */ +#define ARIZONA_WSEQ_ENA_GP5_RISE_SHIFT 4 /* WSEQ_ENA_GP5_RISE */ +#define ARIZONA_WSEQ_ENA_GP5_RISE_WIDTH 1 /* WSEQ_ENA_GP5_RISE */ +#define ARIZONA_WSEQ_ENA_JD1_FALL 0x0008 /* WSEQ_ENA_JD1_FALL */ +#define ARIZONA_WSEQ_ENA_JD1_FALL_MASK 0x0008 /* WSEQ_ENA_JD1_FALL */ +#define ARIZONA_WSEQ_ENA_JD1_FALL_SHIFT 3 /* WSEQ_ENA_JD1_FALL */ +#define ARIZONA_WSEQ_ENA_JD1_FALL_WIDTH 1 /* WSEQ_ENA_JD1_FALL */ +#define ARIZONA_WSEQ_ENA_JD1_RISE 0x0004 /* WSEQ_ENA_JD1_RISE */ +#define ARIZONA_WSEQ_ENA_JD1_RISE_MASK 0x0004 /* WSEQ_ENA_JD1_RISE */ +#define ARIZONA_WSEQ_ENA_JD1_RISE_SHIFT 2 /* WSEQ_ENA_JD1_RISE */ +#define ARIZONA_WSEQ_ENA_JD1_RISE_WIDTH 1 /* WSEQ_ENA_JD1_RISE */ +#define ARIZONA_WSEQ_ENA_JD2_FALL 0x0002 /* WSEQ_ENA_JD2_FALL */ +#define ARIZONA_WSEQ_ENA_JD2_FALL_MASK 0x0002 /* WSEQ_ENA_JD2_FALL */ +#define ARIZONA_WSEQ_ENA_JD2_FALL_SHIFT 1 /* WSEQ_ENA_JD2_FALL */ +#define ARIZONA_WSEQ_ENA_JD2_FALL_WIDTH 1 /* WSEQ_ENA_JD2_FALL */ +#define ARIZONA_WSEQ_ENA_JD2_RISE 0x0001 /* WSEQ_ENA_JD2_RISE */ +#define ARIZONA_WSEQ_ENA_JD2_RISE_MASK 0x0001 /* WSEQ_ENA_JD2_RISE */ +#define ARIZONA_WSEQ_ENA_JD2_RISE_SHIFT 0 /* WSEQ_ENA_JD2_RISE */ +#define ARIZONA_WSEQ_ENA_JD2_RISE_WIDTH 1 /* WSEQ_ENA_JD2_RISE */ + +/* + * R97 (0x61) - Sample Rate Sequence Select 1 + */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR_MASK 0x01FF /* WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR_SHIFT 0 /* WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR_WIDTH 9 /* WSEQ_SAMPLE_RATE_DETECT_A_SEQ_ADDR - [8:0] */ + +/* + * R98 (0x62) - Sample Rate Sequence Select 2 + */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR_MASK 0x01FF /* WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR_SHIFT 0 /* WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR_WIDTH 9 /* WSEQ_SAMPLE_RATE_DETECT_B_SEQ_ADDR - [8:0] */ + +/* + * R99 (0x63) - Sample Rate Sequence Select 3 + */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR_MASK 0x01FF /* WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR_SHIFT 0 /* WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR_WIDTH 9 /* WSEQ_SAMPLE_RATE_DETECT_C_SEQ_ADDR - [8:0] */ + +/* + * R100 (0x64) - Sample Rate Sequence Select 4 + */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR_MASK 0x01FF /* WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR_SHIFT 0 /* WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR_WIDTH 9 /* WSEQ_SAMPLE_RATE_DETECT_D_SEQ_ADDR - [8:0] */ + +/* + * R104 (0x68) - Always On Triggers Sequence Select 1 + */ +#define ARIZONA_WSEQ_GP5_RISE_SEQ_ADDR_MASK 0x01FF /* WSEQ_GP5_RISE_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_GP5_RISE_SEQ_ADDR_SHIFT 0 /* WSEQ_GP5_RISE_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_GP5_RISE_SEQ_ADDR_WIDTH 9 /* WSEQ_GP5_RISE_SEQ_ADDR - [8:0] */ + +/* + * R105 (0x69) - Always On Triggers Sequence Select 2 + */ +#define ARIZONA_WSEQ_GP5_FALL_SEQ_ADDR_MASK 0x01FF /* WSEQ_GP5_FALL_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_GP5_FALL_SEQ_ADDR_SHIFT 0 /* WSEQ_GP5_FALL_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_GP5_FALL_SEQ_ADDR_WIDTH 9 /* WSEQ_GP5_FALL_SEQ_ADDR - [8:0] */ + +/* + * R106 (0x6A) - Always On Triggers Sequence Select 3 + */ +#define ARIZONA_WSEQ_JD1_RISE_SEQ_ADDR_MASK 0x01FF /* WSEQ_JD1_RISE_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD1_RISE_SEQ_ADDR_SHIFT 0 /* WSEQ_JD1_RISE_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD1_RISE_SEQ_ADDR_WIDTH 9 /* WSEQ_JD1_RISE_SEQ_ADDR - [8:0] */ + +/* + * R107 (0x6B) - Always On Triggers Sequence Select 4 + */ +#define ARIZONA_WSEQ_JD1_FALL_SEQ_ADDR_MASK 0x01FF /* WSEQ_JD1_FALL_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD1_FALL_SEQ_ADDR_SHIFT 0 /* WSEQ_JD1_FALL_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD1_FALL_SEQ_ADDR_WIDTH 9 /* WSEQ_JD1_FALL_SEQ_ADDR - [8:0] */ + +/* + * R108 (0x6C) - Always On Triggers Sequence Select 5 + */ +#define ARIZONA_WSEQ_JD2_RISE_SEQ_ADDR_MASK 0x01FF /* WSEQ_JD2_RISE_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD2_RISE_SEQ_ADDR_SHIFT 0 /* WSEQ_JD2_RISE_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD2_RISE_SEQ_ADDR_WIDTH 9 /* WSEQ_JD2_RISE_SEQ_ADDR - [8:0] */ + +/* + * R109 (0x6D) - Always On Triggers Sequence Select 6 + */ +#define ARIZONA_WSEQ_JD2_FALL_SEQ_ADDR_MASK 0x01FF /* WSEQ_JD2_FALL_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD2_FALL_SEQ_ADDR_SHIFT 0 /* WSEQ_JD2_FALL_SEQ_ADDR - [8:0] */ +#define ARIZONA_WSEQ_JD2_FALL_SEQ_ADDR_WIDTH 9 /* WSEQ_JD2_FALL_SEQ_ADDR - [8:0] */ + +/* + * R112 (0x70) - Comfort Noise Generator + */ +#define ARIZONA_NOISE_GEN_RATE_MASK 0x7800 /* NOISE_GEN_RATE - [14:11] */ +#define ARIZONA_NOISE_GEN_RATE_SHIFT 11 /* NOISE_GEN_RATE - [14:11] */ +#define ARIZONA_NOISE_GEN_RATE_WIDTH 4 /* NOISE_GEN_RATE - [14:11] */ +#define ARIZONA_NOISE_GEN_ENA 0x0020 /* NOISE_GEN_ENA */ +#define ARIZONA_NOISE_GEN_ENA_MASK 0x0020 /* NOISE_GEN_ENA */ +#define ARIZONA_NOISE_GEN_ENA_SHIFT 5 /* NOISE_GEN_ENA */ +#define ARIZONA_NOISE_GEN_ENA_WIDTH 1 /* NOISE_GEN_ENA */ +#define ARIZONA_NOISE_GEN_GAIN_MASK 0x001F /* NOISE_GEN_GAIN - [4:0] */ +#define ARIZONA_NOISE_GEN_GAIN_SHIFT 0 /* NOISE_GEN_GAIN - [4:0] */ +#define ARIZONA_NOISE_GEN_GAIN_WIDTH 5 /* NOISE_GEN_GAIN - [4:0] */ + +/* + * R144 (0x90) - Haptics Control 1 + */ +#define ARIZONA_HAP_RATE_MASK 0x7800 /* HAP_RATE - [14:11] */ +#define ARIZONA_HAP_RATE_SHIFT 11 /* HAP_RATE - [14:11] */ +#define ARIZONA_HAP_RATE_WIDTH 4 /* HAP_RATE - [14:11] */ +#define ARIZONA_ONESHOT_TRIG 0x0010 /* ONESHOT_TRIG */ +#define ARIZONA_ONESHOT_TRIG_MASK 0x0010 /* ONESHOT_TRIG */ +#define ARIZONA_ONESHOT_TRIG_SHIFT 4 /* ONESHOT_TRIG */ +#define ARIZONA_ONESHOT_TRIG_WIDTH 1 /* ONESHOT_TRIG */ +#define ARIZONA_HAP_CTRL_MASK 0x000C /* HAP_CTRL - [3:2] */ +#define ARIZONA_HAP_CTRL_SHIFT 2 /* HAP_CTRL - [3:2] */ +#define ARIZONA_HAP_CTRL_WIDTH 2 /* HAP_CTRL - [3:2] */ +#define ARIZONA_HAP_ACT 0x0002 /* HAP_ACT */ +#define ARIZONA_HAP_ACT_MASK 0x0002 /* HAP_ACT */ +#define ARIZONA_HAP_ACT_SHIFT 1 /* HAP_ACT */ +#define ARIZONA_HAP_ACT_WIDTH 1 /* HAP_ACT */ + +/* + * R145 (0x91) - Haptics Control 2 + */ +#define ARIZONA_LRA_FREQ_MASK 0x7FFF /* LRA_FREQ - [14:0] */ +#define ARIZONA_LRA_FREQ_SHIFT 0 /* LRA_FREQ - [14:0] */ +#define ARIZONA_LRA_FREQ_WIDTH 15 /* LRA_FREQ - [14:0] */ + +/* + * R146 (0x92) - Haptics phase 1 intensity + */ +#define ARIZONA_PHASE1_INTENSITY_MASK 0x00FF /* PHASE1_INTENSITY - [7:0] */ +#define ARIZONA_PHASE1_INTENSITY_SHIFT 0 /* PHASE1_INTENSITY - [7:0] */ +#define ARIZONA_PHASE1_INTENSITY_WIDTH 8 /* PHASE1_INTENSITY - [7:0] */ + +/* + * R147 (0x93) - Haptics phase 1 duration + */ +#define ARIZONA_PHASE1_DURATION_MASK 0x01FF /* PHASE1_DURATION - [8:0] */ +#define ARIZONA_PHASE1_DURATION_SHIFT 0 /* PHASE1_DURATION - [8:0] */ +#define ARIZONA_PHASE1_DURATION_WIDTH 9 /* PHASE1_DURATION - [8:0] */ + +/* + * R148 (0x94) - Haptics phase 2 intensity + */ +#define ARIZONA_PHASE2_INTENSITY_MASK 0x00FF /* PHASE2_INTENSITY - [7:0] */ +#define ARIZONA_PHASE2_INTENSITY_SHIFT 0 /* PHASE2_INTENSITY - [7:0] */ +#define ARIZONA_PHASE2_INTENSITY_WIDTH 8 /* PHASE2_INTENSITY - [7:0] */ + +/* + * R149 (0x95) - Haptics phase 2 duration + */ +#define ARIZONA_PHASE2_DURATION_MASK 0x07FF /* PHASE2_DURATION - [10:0] */ +#define ARIZONA_PHASE2_DURATION_SHIFT 0 /* PHASE2_DURATION - [10:0] */ +#define ARIZONA_PHASE2_DURATION_WIDTH 11 /* PHASE2_DURATION - [10:0] */ + +/* + * R150 (0x96) - Haptics phase 3 intensity + */ +#define ARIZONA_PHASE3_INTENSITY_MASK 0x00FF /* PHASE3_INTENSITY - [7:0] */ +#define ARIZONA_PHASE3_INTENSITY_SHIFT 0 /* PHASE3_INTENSITY - [7:0] */ +#define ARIZONA_PHASE3_INTENSITY_WIDTH 8 /* PHASE3_INTENSITY - [7:0] */ + +/* + * R151 (0x97) - Haptics phase 3 duration + */ +#define ARIZONA_PHASE3_DURATION_MASK 0x01FF /* PHASE3_DURATION - [8:0] */ +#define ARIZONA_PHASE3_DURATION_SHIFT 0 /* PHASE3_DURATION - [8:0] */ +#define ARIZONA_PHASE3_DURATION_WIDTH 9 /* PHASE3_DURATION - [8:0] */ + +/* + * R152 (0x98) - Haptics Status + */ +#define ARIZONA_ONESHOT_STS 0x0001 /* ONESHOT_STS */ +#define ARIZONA_ONESHOT_STS_MASK 0x0001 /* ONESHOT_STS */ +#define ARIZONA_ONESHOT_STS_SHIFT 0 /* ONESHOT_STS */ +#define ARIZONA_ONESHOT_STS_WIDTH 1 /* ONESHOT_STS */ + +/* + * R256 (0x100) - Clock 32k 1 + */ +#define ARIZONA_CLK_32K_ENA 0x0040 /* CLK_32K_ENA */ +#define ARIZONA_CLK_32K_ENA_MASK 0x0040 /* CLK_32K_ENA */ +#define ARIZONA_CLK_32K_ENA_SHIFT 6 /* CLK_32K_ENA */ +#define ARIZONA_CLK_32K_ENA_WIDTH 1 /* CLK_32K_ENA */ +#define ARIZONA_CLK_32K_SRC_MASK 0x0003 /* CLK_32K_SRC - [1:0] */ +#define ARIZONA_CLK_32K_SRC_SHIFT 0 /* CLK_32K_SRC - [1:0] */ +#define ARIZONA_CLK_32K_SRC_WIDTH 2 /* CLK_32K_SRC - [1:0] */ + +/* + * R257 (0x101) - System Clock 1 + */ +#define ARIZONA_SYSCLK_FRAC 0x8000 /* SYSCLK_FRAC */ +#define ARIZONA_SYSCLK_FRAC_MASK 0x8000 /* SYSCLK_FRAC */ +#define ARIZONA_SYSCLK_FRAC_SHIFT 15 /* SYSCLK_FRAC */ +#define ARIZONA_SYSCLK_FRAC_WIDTH 1 /* SYSCLK_FRAC */ +#define ARIZONA_SYSCLK_FREQ_MASK 0x0700 /* SYSCLK_FREQ - [10:8] */ +#define ARIZONA_SYSCLK_FREQ_SHIFT 8 /* SYSCLK_FREQ - [10:8] */ +#define ARIZONA_SYSCLK_FREQ_WIDTH 3 /* SYSCLK_FREQ - [10:8] */ +#define ARIZONA_SYSCLK_ENA 0x0040 /* SYSCLK_ENA */ +#define ARIZONA_SYSCLK_ENA_MASK 0x0040 /* SYSCLK_ENA */ +#define ARIZONA_SYSCLK_ENA_SHIFT 6 /* SYSCLK_ENA */ +#define ARIZONA_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */ +#define ARIZONA_SYSCLK_SRC_MASK 0x000F /* SYSCLK_SRC - [3:0] */ +#define ARIZONA_SYSCLK_SRC_SHIFT 0 /* SYSCLK_SRC - [3:0] */ +#define ARIZONA_SYSCLK_SRC_WIDTH 4 /* SYSCLK_SRC - [3:0] */ + +/* + * R258 (0x102) - Sample rate 1 + */ +#define ARIZONA_SAMPLE_RATE_1_MASK 0x001F /* SAMPLE_RATE_1 - [4:0] */ +#define ARIZONA_SAMPLE_RATE_1_SHIFT 0 /* SAMPLE_RATE_1 - [4:0] */ +#define ARIZONA_SAMPLE_RATE_1_WIDTH 5 /* SAMPLE_RATE_1 - [4:0] */ + +/* + * R259 (0x103) - Sample rate 2 + */ +#define ARIZONA_SAMPLE_RATE_2_MASK 0x001F /* SAMPLE_RATE_2 - [4:0] */ +#define ARIZONA_SAMPLE_RATE_2_SHIFT 0 /* SAMPLE_RATE_2 - [4:0] */ +#define ARIZONA_SAMPLE_RATE_2_WIDTH 5 /* SAMPLE_RATE_2 - [4:0] */ + +/* + * R260 (0x104) - Sample rate 3 + */ +#define ARIZONA_SAMPLE_RATE_3_MASK 0x001F /* SAMPLE_RATE_3 - [4:0] */ +#define ARIZONA_SAMPLE_RATE_3_SHIFT 0 /* SAMPLE_RATE_3 - [4:0] */ +#define ARIZONA_SAMPLE_RATE_3_WIDTH 5 /* SAMPLE_RATE_3 - [4:0] */ + +/* + * R266 (0x10A) - Sample rate 1 status + */ +#define ARIZONA_SAMPLE_RATE_1_STS_MASK 0x001F /* SAMPLE_RATE_1_STS - [4:0] */ +#define ARIZONA_SAMPLE_RATE_1_STS_SHIFT 0 /* SAMPLE_RATE_1_STS - [4:0] */ +#define ARIZONA_SAMPLE_RATE_1_STS_WIDTH 5 /* SAMPLE_RATE_1_STS - [4:0] */ + +/* + * R267 (0x10B) - Sample rate 2 status + */ +#define ARIZONA_SAMPLE_RATE_2_STS_MASK 0x001F /* SAMPLE_RATE_2_STS - [4:0] */ +#define ARIZONA_SAMPLE_RATE_2_STS_SHIFT 0 /* SAMPLE_RATE_2_STS - [4:0] */ +#define ARIZONA_SAMPLE_RATE_2_STS_WIDTH 5 /* SAMPLE_RATE_2_STS - [4:0] */ + +/* + * R268 (0x10C) - Sample rate 3 status + */ +#define ARIZONA_SAMPLE_RATE_3_STS_MASK 0x001F /* SAMPLE_RATE_3_STS - [4:0] */ +#define ARIZONA_SAMPLE_RATE_3_STS_SHIFT 0 /* SAMPLE_RATE_3_STS - [4:0] */ +#define ARIZONA_SAMPLE_RATE_3_STS_WIDTH 5 /* SAMPLE_RATE_3_STS - [4:0] */ + +/* + * R274 (0x112) - Async clock 1 + */ +#define ARIZONA_ASYNC_CLK_FREQ_MASK 0x0700 /* ASYNC_CLK_FREQ - [10:8] */ +#define ARIZONA_ASYNC_CLK_FREQ_SHIFT 8 /* ASYNC_CLK_FREQ - [10:8] */ +#define ARIZONA_ASYNC_CLK_FREQ_WIDTH 3 /* ASYNC_CLK_FREQ - [10:8] */ +#define ARIZONA_ASYNC_CLK_ENA 0x0040 /* ASYNC_CLK_ENA */ +#define ARIZONA_ASYNC_CLK_ENA_MASK 0x0040 /* ASYNC_CLK_ENA */ +#define ARIZONA_ASYNC_CLK_ENA_SHIFT 6 /* ASYNC_CLK_ENA */ +#define ARIZONA_ASYNC_CLK_ENA_WIDTH 1 /* ASYNC_CLK_ENA */ +#define ARIZONA_ASYNC_CLK_SRC_MASK 0x000F /* ASYNC_CLK_SRC - [3:0] */ +#define ARIZONA_ASYNC_CLK_SRC_SHIFT 0 /* ASYNC_CLK_SRC - [3:0] */ +#define ARIZONA_ASYNC_CLK_SRC_WIDTH 4 /* ASYNC_CLK_SRC - [3:0] */ + +/* + * R275 (0x113) - Async sample rate 1 + */ +#define ARIZONA_ASYNC_SAMPLE_RATE_MASK 0x001F /* ASYNC_SAMPLE_RATE - [4:0] */ +#define ARIZONA_ASYNC_SAMPLE_RATE_SHIFT 0 /* ASYNC_SAMPLE_RATE - [4:0] */ +#define ARIZONA_ASYNC_SAMPLE_RATE_WIDTH 5 /* ASYNC_SAMPLE_RATE - [4:0] */ + +/* + * R283 (0x11B) - Async sample rate 1 status + */ +#define ARIZONA_ASYNC_SAMPLE_RATE_STS_MASK 0x001F /* ASYNC_SAMPLE_RATE_STS - [4:0] */ +#define ARIZONA_ASYNC_SAMPLE_RATE_STS_SHIFT 0 /* ASYNC_SAMPLE_RATE_STS - [4:0] */ +#define ARIZONA_ASYNC_SAMPLE_RATE_STS_WIDTH 5 /* ASYNC_SAMPLE_RATE_STS - [4:0] */ + +/* + * R329 (0x149) - Output system clock + */ +#define ARIZONA_OPCLK_ENA 0x8000 /* OPCLK_ENA */ +#define ARIZONA_OPCLK_ENA_MASK 0x8000 /* OPCLK_ENA */ +#define ARIZONA_OPCLK_ENA_SHIFT 15 /* OPCLK_ENA */ +#define ARIZONA_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */ +#define ARIZONA_OPCLK_DIV_MASK 0x00F8 /* OPCLK_DIV - [7:3] */ +#define ARIZONA_OPCLK_DIV_SHIFT 3 /* OPCLK_DIV - [7:3] */ +#define ARIZONA_OPCLK_DIV_WIDTH 5 /* OPCLK_DIV - [7:3] */ +#define ARIZONA_OPCLK_SEL_MASK 0x0007 /* OPCLK_SEL - [2:0] */ +#define ARIZONA_OPCLK_SEL_SHIFT 0 /* OPCLK_SEL - [2:0] */ +#define ARIZONA_OPCLK_SEL_WIDTH 3 /* OPCLK_SEL - [2:0] */ + +/* + * R330 (0x14A) - Output async clock + */ +#define ARIZONA_OPCLK_ASYNC_ENA 0x8000 /* OPCLK_ASYNC_ENA */ +#define ARIZONA_OPCLK_ASYNC_ENA_MASK 0x8000 /* OPCLK_ASYNC_ENA */ +#define ARIZONA_OPCLK_ASYNC_ENA_SHIFT 15 /* OPCLK_ASYNC_ENA */ +#define ARIZONA_OPCLK_ASYNC_ENA_WIDTH 1 /* OPCLK_ASYNC_ENA */ +#define ARIZONA_OPCLK_ASYNC_DIV_MASK 0x00F8 /* OPCLK_ASYNC_DIV - [7:3] */ +#define ARIZONA_OPCLK_ASYNC_DIV_SHIFT 3 /* OPCLK_ASYNC_DIV - [7:3] */ +#define ARIZONA_OPCLK_ASYNC_DIV_WIDTH 5 /* OPCLK_ASYNC_DIV - [7:3] */ +#define ARIZONA_OPCLK_ASYNC_SEL_MASK 0x0007 /* OPCLK_ASYNC_SEL - [2:0] */ +#define ARIZONA_OPCLK_ASYNC_SEL_SHIFT 0 /* OPCLK_ASYNC_SEL - [2:0] */ +#define ARIZONA_OPCLK_ASYNC_SEL_WIDTH 3 /* OPCLK_ASYNC_SEL - [2:0] */ + +/* + * R338 (0x152) - Rate Estimator 1 + */ +#define ARIZONA_TRIG_ON_STARTUP 0x0010 /* TRIG_ON_STARTUP */ +#define ARIZONA_TRIG_ON_STARTUP_MASK 0x0010 /* TRIG_ON_STARTUP */ +#define ARIZONA_TRIG_ON_STARTUP_SHIFT 4 /* TRIG_ON_STARTUP */ +#define ARIZONA_TRIG_ON_STARTUP_WIDTH 1 /* TRIG_ON_STARTUP */ +#define ARIZONA_LRCLK_SRC_MASK 0x000E /* LRCLK_SRC - [3:1] */ +#define ARIZONA_LRCLK_SRC_SHIFT 1 /* LRCLK_SRC - [3:1] */ +#define ARIZONA_LRCLK_SRC_WIDTH 3 /* LRCLK_SRC - [3:1] */ +#define ARIZONA_RATE_EST_ENA 0x0001 /* RATE_EST_ENA */ +#define ARIZONA_RATE_EST_ENA_MASK 0x0001 /* RATE_EST_ENA */ +#define ARIZONA_RATE_EST_ENA_SHIFT 0 /* RATE_EST_ENA */ +#define ARIZONA_RATE_EST_ENA_WIDTH 1 /* RATE_EST_ENA */ + +/* + * R339 (0x153) - Rate Estimator 2 + */ +#define ARIZONA_SAMPLE_RATE_DETECT_A_MASK 0x001F /* SAMPLE_RATE_DETECT_A - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_A_SHIFT 0 /* SAMPLE_RATE_DETECT_A - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_A_WIDTH 5 /* SAMPLE_RATE_DETECT_A - [4:0] */ + +/* + * R340 (0x154) - Rate Estimator 3 + */ +#define ARIZONA_SAMPLE_RATE_DETECT_B_MASK 0x001F /* SAMPLE_RATE_DETECT_B - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_B_SHIFT 0 /* SAMPLE_RATE_DETECT_B - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_B_WIDTH 5 /* SAMPLE_RATE_DETECT_B - [4:0] */ + +/* + * R341 (0x155) - Rate Estimator 4 + */ +#define ARIZONA_SAMPLE_RATE_DETECT_C_MASK 0x001F /* SAMPLE_RATE_DETECT_C - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_C_SHIFT 0 /* SAMPLE_RATE_DETECT_C - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_C_WIDTH 5 /* SAMPLE_RATE_DETECT_C - [4:0] */ + +/* + * R342 (0x156) - Rate Estimator 5 + */ +#define ARIZONA_SAMPLE_RATE_DETECT_D_MASK 0x001F /* SAMPLE_RATE_DETECT_D - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_D_SHIFT 0 /* SAMPLE_RATE_DETECT_D - [4:0] */ +#define ARIZONA_SAMPLE_RATE_DETECT_D_WIDTH 5 /* SAMPLE_RATE_DETECT_D - [4:0] */ + +/* + * R369 (0x171) - FLL1 Control 1 + */ +#define ARIZONA_FLL1_FREERUN 0x0002 /* FLL1_FREERUN */ +#define ARIZONA_FLL1_FREERUN_MASK 0x0002 /* FLL1_FREERUN */ +#define ARIZONA_FLL1_FREERUN_SHIFT 1 /* FLL1_FREERUN */ +#define ARIZONA_FLL1_FREERUN_WIDTH 1 /* FLL1_FREERUN */ +#define ARIZONA_FLL1_ENA 0x0001 /* FLL1_ENA */ +#define ARIZONA_FLL1_ENA_MASK 0x0001 /* FLL1_ENA */ +#define ARIZONA_FLL1_ENA_SHIFT 0 /* FLL1_ENA */ +#define ARIZONA_FLL1_ENA_WIDTH 1 /* FLL1_ENA */ + +/* + * R370 (0x172) - FLL1 Control 2 + */ +#define ARIZONA_FLL1_CTRL_UPD 0x8000 /* FLL1_CTRL_UPD */ +#define ARIZONA_FLL1_CTRL_UPD_MASK 0x8000 /* FLL1_CTRL_UPD */ +#define ARIZONA_FLL1_CTRL_UPD_SHIFT 15 /* FLL1_CTRL_UPD */ +#define ARIZONA_FLL1_CTRL_UPD_WIDTH 1 /* FLL1_CTRL_UPD */ +#define ARIZONA_FLL1_N_MASK 0x03FF /* FLL1_N - [9:0] */ +#define ARIZONA_FLL1_N_SHIFT 0 /* FLL1_N - [9:0] */ +#define ARIZONA_FLL1_N_WIDTH 10 /* FLL1_N - [9:0] */ + +/* + * R371 (0x173) - FLL1 Control 3 + */ +#define ARIZONA_FLL1_THETA_MASK 0xFFFF /* FLL1_THETA - [15:0] */ +#define ARIZONA_FLL1_THETA_SHIFT 0 /* FLL1_THETA - [15:0] */ +#define ARIZONA_FLL1_THETA_WIDTH 16 /* FLL1_THETA - [15:0] */ + +/* + * R372 (0x174) - FLL1 Control 4 + */ +#define ARIZONA_FLL1_LAMBDA_MASK 0xFFFF /* FLL1_LAMBDA - [15:0] */ +#define ARIZONA_FLL1_LAMBDA_SHIFT 0 /* FLL1_LAMBDA - [15:0] */ +#define ARIZONA_FLL1_LAMBDA_WIDTH 16 /* FLL1_LAMBDA - [15:0] */ + +/* + * R373 (0x175) - FLL1 Control 5 + */ +#define ARIZONA_FLL1_FRATIO_MASK 0x0700 /* FLL1_FRATIO - [10:8] */ +#define ARIZONA_FLL1_FRATIO_SHIFT 8 /* FLL1_FRATIO - [10:8] */ +#define ARIZONA_FLL1_FRATIO_WIDTH 3 /* FLL1_FRATIO - [10:8] */ +#define ARIZONA_FLL1_OUTDIV_MASK 0x000E /* FLL1_OUTDIV - [3:1] */ +#define ARIZONA_FLL1_OUTDIV_SHIFT 1 /* FLL1_OUTDIV - [3:1] */ +#define ARIZONA_FLL1_OUTDIV_WIDTH 3 /* FLL1_OUTDIV - [3:1] */ + +/* + * R374 (0x176) - FLL1 Control 6 + */ +#define ARIZONA_FLL1_CLK_REF_DIV_MASK 0x00C0 /* FLL1_CLK_REF_DIV - [7:6] */ +#define ARIZONA_FLL1_CLK_REF_DIV_SHIFT 6 /* FLL1_CLK_REF_DIV - [7:6] */ +#define ARIZONA_FLL1_CLK_REF_DIV_WIDTH 2 /* FLL1_CLK_REF_DIV - [7:6] */ +#define ARIZONA_FLL1_CLK_REF_SRC_MASK 0x000F /* FLL1_CLK_REF_SRC - [3:0] */ +#define ARIZONA_FLL1_CLK_REF_SRC_SHIFT 0 /* FLL1_CLK_REF_SRC - [3:0] */ +#define ARIZONA_FLL1_CLK_REF_SRC_WIDTH 4 /* FLL1_CLK_REF_SRC - [3:0] */ + +/* + * R375 (0x177) - FLL1 Loop Filter Test 1 + */ +#define ARIZONA_FLL1_FRC_INTEG_UPD 0x8000 /* FLL1_FRC_INTEG_UPD */ +#define ARIZONA_FLL1_FRC_INTEG_UPD_MASK 0x8000 /* FLL1_FRC_INTEG_UPD */ +#define ARIZONA_FLL1_FRC_INTEG_UPD_SHIFT 15 /* FLL1_FRC_INTEG_UPD */ +#define ARIZONA_FLL1_FRC_INTEG_UPD_WIDTH 1 /* FLL1_FRC_INTEG_UPD */ +#define ARIZONA_FLL1_FRC_INTEG_VAL_MASK 0x0FFF /* FLL1_FRC_INTEG_VAL - [11:0] */ +#define ARIZONA_FLL1_FRC_INTEG_VAL_SHIFT 0 /* FLL1_FRC_INTEG_VAL - [11:0] */ +#define ARIZONA_FLL1_FRC_INTEG_VAL_WIDTH 12 /* FLL1_FRC_INTEG_VAL - [11:0] */ + +/* + * R385 (0x181) - FLL1 Synchroniser 1 + */ +#define ARIZONA_FLL1_SYNC_ENA 0x0001 /* FLL1_SYNC_ENA */ +#define ARIZONA_FLL1_SYNC_ENA_MASK 0x0001 /* FLL1_SYNC_ENA */ +#define ARIZONA_FLL1_SYNC_ENA_SHIFT 0 /* FLL1_SYNC_ENA */ +#define ARIZONA_FLL1_SYNC_ENA_WIDTH 1 /* FLL1_SYNC_ENA */ + +/* + * R386 (0x182) - FLL1 Synchroniser 2 + */ +#define ARIZONA_FLL1_SYNC_N_MASK 0x03FF /* FLL1_SYNC_N - [9:0] */ +#define ARIZONA_FLL1_SYNC_N_SHIFT 0 /* FLL1_SYNC_N - [9:0] */ +#define ARIZONA_FLL1_SYNC_N_WIDTH 10 /* FLL1_SYNC_N - [9:0] */ + +/* + * R387 (0x183) - FLL1 Synchroniser 3 + */ +#define ARIZONA_FLL1_SYNC_THETA_MASK 0xFFFF /* FLL1_SYNC_THETA - [15:0] */ +#define ARIZONA_FLL1_SYNC_THETA_SHIFT 0 /* FLL1_SYNC_THETA - [15:0] */ +#define ARIZONA_FLL1_SYNC_THETA_WIDTH 16 /* FLL1_SYNC_THETA - [15:0] */ + +/* + * R388 (0x184) - FLL1 Synchroniser 4 + */ +#define ARIZONA_FLL1_SYNC_LAMBDA_MASK 0xFFFF /* FLL1_SYNC_LAMBDA - [15:0] */ +#define ARIZONA_FLL1_SYNC_LAMBDA_SHIFT 0 /* FLL1_SYNC_LAMBDA - [15:0] */ +#define ARIZONA_FLL1_SYNC_LAMBDA_WIDTH 16 /* FLL1_SYNC_LAMBDA - [15:0] */ + +/* + * R389 (0x185) - FLL1 Synchroniser 5 + */ +#define ARIZONA_FLL1_SYNC_FRATIO_MASK 0x0700 /* FLL1_SYNC_FRATIO - [10:8] */ +#define ARIZONA_FLL1_SYNC_FRATIO_SHIFT 8 /* FLL1_SYNC_FRATIO - [10:8] */ +#define ARIZONA_FLL1_SYNC_FRATIO_WIDTH 3 /* FLL1_SYNC_FRATIO - [10:8] */ + +/* + * R390 (0x186) - FLL1 Synchroniser 6 + */ +#define ARIZONA_FLL1_CLK_SYNC_DIV_MASK 0x00C0 /* FLL1_CLK_SYNC_DIV - [7:6] */ +#define ARIZONA_FLL1_CLK_SYNC_DIV_SHIFT 6 /* FLL1_CLK_SYNC_DIV - [7:6] */ +#define ARIZONA_FLL1_CLK_SYNC_DIV_WIDTH 2 /* FLL1_CLK_SYNC_DIV - [7:6] */ +#define ARIZONA_FLL1_CLK_SYNC_SRC_MASK 0x000F /* FLL1_CLK_SYNC_SRC - [3:0] */ +#define ARIZONA_FLL1_CLK_SYNC_SRC_SHIFT 0 /* FLL1_CLK_SYNC_SRC - [3:0] */ +#define ARIZONA_FLL1_CLK_SYNC_SRC_WIDTH 4 /* FLL1_CLK_SYNC_SRC - [3:0] */ + +/* + * R393 (0x189) - FLL1 Spread Spectrum + */ +#define ARIZONA_FLL1_SS_AMPL_MASK 0x0030 /* FLL1_SS_AMPL - [5:4] */ +#define ARIZONA_FLL1_SS_AMPL_SHIFT 4 /* FLL1_SS_AMPL - [5:4] */ +#define ARIZONA_FLL1_SS_AMPL_WIDTH 2 /* FLL1_SS_AMPL - [5:4] */ +#define ARIZONA_FLL1_SS_FREQ_MASK 0x000C /* FLL1_SS_FREQ - [3:2] */ +#define ARIZONA_FLL1_SS_FREQ_SHIFT 2 /* FLL1_SS_FREQ - [3:2] */ +#define ARIZONA_FLL1_SS_FREQ_WIDTH 2 /* FLL1_SS_FREQ - [3:2] */ +#define ARIZONA_FLL1_SS_SEL_MASK 0x0003 /* FLL1_SS_SEL - [1:0] */ +#define ARIZONA_FLL1_SS_SEL_SHIFT 0 /* FLL1_SS_SEL - [1:0] */ +#define ARIZONA_FLL1_SS_SEL_WIDTH 2 /* FLL1_SS_SEL - [1:0] */ + +/* + * R394 (0x18A) - FLL1 GPIO Clock + */ +#define ARIZONA_FLL1_GPDIV_MASK 0x00FE /* FLL1_GPDIV - [7:1] */ +#define ARIZONA_FLL1_GPDIV_SHIFT 1 /* FLL1_GPDIV - [7:1] */ +#define ARIZONA_FLL1_GPDIV_WIDTH 7 /* FLL1_GPDIV - [7:1] */ +#define ARIZONA_FLL1_GPDIV_ENA 0x0001 /* FLL1_GPDIV_ENA */ +#define ARIZONA_FLL1_GPDIV_ENA_MASK 0x0001 /* FLL1_GPDIV_ENA */ +#define ARIZONA_FLL1_GPDIV_ENA_SHIFT 0 /* FLL1_GPDIV_ENA */ +#define ARIZONA_FLL1_GPDIV_ENA_WIDTH 1 /* FLL1_GPDIV_ENA */ + +/* + * R401 (0x191) - FLL2 Control 1 + */ +#define ARIZONA_FLL2_FREERUN 0x0002 /* FLL2_FREERUN */ +#define ARIZONA_FLL2_FREERUN_MASK 0x0002 /* FLL2_FREERUN */ +#define ARIZONA_FLL2_FREERUN_SHIFT 1 /* FLL2_FREERUN */ +#define ARIZONA_FLL2_FREERUN_WIDTH 1 /* FLL2_FREERUN */ +#define ARIZONA_FLL2_ENA 0x0001 /* FLL2_ENA */ +#define ARIZONA_FLL2_ENA_MASK 0x0001 /* FLL2_ENA */ +#define ARIZONA_FLL2_ENA_SHIFT 0 /* FLL2_ENA */ +#define ARIZONA_FLL2_ENA_WIDTH 1 /* FLL2_ENA */ + +/* + * R402 (0x192) - FLL2 Control 2 + */ +#define ARIZONA_FLL2_CTRL_UPD 0x8000 /* FLL2_CTRL_UPD */ +#define ARIZONA_FLL2_CTRL_UPD_MASK 0x8000 /* FLL2_CTRL_UPD */ +#define ARIZONA_FLL2_CTRL_UPD_SHIFT 15 /* FLL2_CTRL_UPD */ +#define ARIZONA_FLL2_CTRL_UPD_WIDTH 1 /* FLL2_CTRL_UPD */ +#define ARIZONA_FLL2_N_MASK 0x03FF /* FLL2_N - [9:0] */ +#define ARIZONA_FLL2_N_SHIFT 0 /* FLL2_N - [9:0] */ +#define ARIZONA_FLL2_N_WIDTH 10 /* FLL2_N - [9:0] */ + +/* + * R403 (0x193) - FLL2 Control 3 + */ +#define ARIZONA_FLL2_THETA_MASK 0xFFFF /* FLL2_THETA - [15:0] */ +#define ARIZONA_FLL2_THETA_SHIFT 0 /* FLL2_THETA - [15:0] */ +#define ARIZONA_FLL2_THETA_WIDTH 16 /* FLL2_THETA - [15:0] */ + +/* + * R404 (0x194) - FLL2 Control 4 + */ +#define ARIZONA_FLL2_LAMBDA_MASK 0xFFFF /* FLL2_LAMBDA - [15:0] */ +#define ARIZONA_FLL2_LAMBDA_SHIFT 0 /* FLL2_LAMBDA - [15:0] */ +#define ARIZONA_FLL2_LAMBDA_WIDTH 16 /* FLL2_LAMBDA - [15:0] */ + +/* + * R405 (0x195) - FLL2 Control 5 + */ +#define ARIZONA_FLL2_FRATIO_MASK 0x0700 /* FLL2_FRATIO - [10:8] */ +#define ARIZONA_FLL2_FRATIO_SHIFT 8 /* FLL2_FRATIO - [10:8] */ +#define ARIZONA_FLL2_FRATIO_WIDTH 3 /* FLL2_FRATIO - [10:8] */ +#define ARIZONA_FLL2_OUTDIV_MASK 0x000E /* FLL2_OUTDIV - [3:1] */ +#define ARIZONA_FLL2_OUTDIV_SHIFT 1 /* FLL2_OUTDIV - [3:1] */ +#define ARIZONA_FLL2_OUTDIV_WIDTH 3 /* FLL2_OUTDIV - [3:1] */ + +/* + * R406 (0x196) - FLL2 Control 6 + */ +#define ARIZONA_FLL2_CLK_REF_DIV_MASK 0x00C0 /* FLL2_CLK_REF_DIV - [7:6] */ +#define ARIZONA_FLL2_CLK_REF_DIV_SHIFT 6 /* FLL2_CLK_REF_DIV - [7:6] */ +#define ARIZONA_FLL2_CLK_REF_DIV_WIDTH 2 /* FLL2_CLK_REF_DIV - [7:6] */ +#define ARIZONA_FLL2_CLK_REF_SRC_MASK 0x000F /* FLL2_CLK_REF_SRC - [3:0] */ +#define ARIZONA_FLL2_CLK_REF_SRC_SHIFT 0 /* FLL2_CLK_REF_SRC - [3:0] */ +#define ARIZONA_FLL2_CLK_REF_SRC_WIDTH 4 /* FLL2_CLK_REF_SRC - [3:0] */ + +/* + * R407 (0x197) - FLL2 Loop Filter Test 1 + */ +#define ARIZONA_FLL2_FRC_INTEG_UPD 0x8000 /* FLL2_FRC_INTEG_UPD */ +#define ARIZONA_FLL2_FRC_INTEG_UPD_MASK 0x8000 /* FLL2_FRC_INTEG_UPD */ +#define ARIZONA_FLL2_FRC_INTEG_UPD_SHIFT 15 /* FLL2_FRC_INTEG_UPD */ +#define ARIZONA_FLL2_FRC_INTEG_UPD_WIDTH 1 /* FLL2_FRC_INTEG_UPD */ +#define ARIZONA_FLL2_FRC_INTEG_VAL_MASK 0x0FFF /* FLL2_FRC_INTEG_VAL - [11:0] */ +#define ARIZONA_FLL2_FRC_INTEG_VAL_SHIFT 0 /* FLL2_FRC_INTEG_VAL - [11:0] */ +#define ARIZONA_FLL2_FRC_INTEG_VAL_WIDTH 12 /* FLL2_FRC_INTEG_VAL - [11:0] */ + +/* + * R417 (0x1A1) - FLL2 Synchroniser 1 + */ +#define ARIZONA_FLL2_SYNC_ENA 0x0001 /* FLL2_SYNC_ENA */ +#define ARIZONA_FLL2_SYNC_ENA_MASK 0x0001 /* FLL2_SYNC_ENA */ +#define ARIZONA_FLL2_SYNC_ENA_SHIFT 0 /* FLL2_SYNC_ENA */ +#define ARIZONA_FLL2_SYNC_ENA_WIDTH 1 /* FLL2_SYNC_ENA */ + +/* + * R418 (0x1A2) - FLL2 Synchroniser 2 + */ +#define ARIZONA_FLL2_SYNC_N_MASK 0x03FF /* FLL2_SYNC_N - [9:0] */ +#define ARIZONA_FLL2_SYNC_N_SHIFT 0 /* FLL2_SYNC_N - [9:0] */ +#define ARIZONA_FLL2_SYNC_N_WIDTH 10 /* FLL2_SYNC_N - [9:0] */ + +/* + * R419 (0x1A3) - FLL2 Synchroniser 3 + */ +#define ARIZONA_FLL2_SYNC_THETA_MASK 0xFFFF /* FLL2_SYNC_THETA - [15:0] */ +#define ARIZONA_FLL2_SYNC_THETA_SHIFT 0 /* FLL2_SYNC_THETA - [15:0] */ +#define ARIZONA_FLL2_SYNC_THETA_WIDTH 16 /* FLL2_SYNC_THETA - [15:0] */ + +/* + * R420 (0x1A4) - FLL2 Synchroniser 4 + */ +#define ARIZONA_FLL2_SYNC_LAMBDA_MASK 0xFFFF /* FLL2_SYNC_LAMBDA - [15:0] */ +#define ARIZONA_FLL2_SYNC_LAMBDA_SHIFT 0 /* FLL2_SYNC_LAMBDA - [15:0] */ +#define ARIZONA_FLL2_SYNC_LAMBDA_WIDTH 16 /* FLL2_SYNC_LAMBDA - [15:0] */ + +/* + * R421 (0x1A5) - FLL2 Synchroniser 5 + */ +#define ARIZONA_FLL2_SYNC_FRATIO_MASK 0x0700 /* FLL2_SYNC_FRATIO - [10:8] */ +#define ARIZONA_FLL2_SYNC_FRATIO_SHIFT 8 /* FLL2_SYNC_FRATIO - [10:8] */ +#define ARIZONA_FLL2_SYNC_FRATIO_WIDTH 3 /* FLL2_SYNC_FRATIO - [10:8] */ + +/* + * R422 (0x1A6) - FLL2 Synchroniser 6 + */ +#define ARIZONA_FLL2_CLK_SYNC_DIV_MASK 0x00C0 /* FLL2_CLK_SYNC_DIV - [7:6] */ +#define ARIZONA_FLL2_CLK_SYNC_DIV_SHIFT 6 /* FLL2_CLK_SYNC_DIV - [7:6] */ +#define ARIZONA_FLL2_CLK_SYNC_DIV_WIDTH 2 /* FLL2_CLK_SYNC_DIV - [7:6] */ +#define ARIZONA_FLL2_CLK_SYNC_SRC_MASK 0x000F /* FLL2_CLK_SYNC_SRC - [3:0] */ +#define ARIZONA_FLL2_CLK_SYNC_SRC_SHIFT 0 /* FLL2_CLK_SYNC_SRC - [3:0] */ +#define ARIZONA_FLL2_CLK_SYNC_SRC_WIDTH 4 /* FLL2_CLK_SYNC_SRC - [3:0] */ + +/* + * R425 (0x1A9) - FLL2 Spread Spectrum + */ +#define ARIZONA_FLL2_SS_AMPL_MASK 0x0030 /* FLL2_SS_AMPL - [5:4] */ +#define ARIZONA_FLL2_SS_AMPL_SHIFT 4 /* FLL2_SS_AMPL - [5:4] */ +#define ARIZONA_FLL2_SS_AMPL_WIDTH 2 /* FLL2_SS_AMPL - [5:4] */ +#define ARIZONA_FLL2_SS_FREQ_MASK 0x000C /* FLL2_SS_FREQ - [3:2] */ +#define ARIZONA_FLL2_SS_FREQ_SHIFT 2 /* FLL2_SS_FREQ - [3:2] */ +#define ARIZONA_FLL2_SS_FREQ_WIDTH 2 /* FLL2_SS_FREQ - [3:2] */ +#define ARIZONA_FLL2_SS_SEL_MASK 0x0003 /* FLL2_SS_SEL - [1:0] */ +#define ARIZONA_FLL2_SS_SEL_SHIFT 0 /* FLL2_SS_SEL - [1:0] */ +#define ARIZONA_FLL2_SS_SEL_WIDTH 2 /* FLL2_SS_SEL - [1:0] */ + +/* + * R426 (0x1AA) - FLL2 GPIO Clock + */ +#define ARIZONA_FLL2_GPDIV_MASK 0x00FE /* FLL2_GPDIV - [7:1] */ +#define ARIZONA_FLL2_GPDIV_SHIFT 1 /* FLL2_GPDIV - [7:1] */ +#define ARIZONA_FLL2_GPDIV_WIDTH 7 /* FLL2_GPDIV - [7:1] */ +#define ARIZONA_FLL2_GPDIV_ENA 0x0001 /* FLL2_GPDIV_ENA */ +#define ARIZONA_FLL2_GPDIV_ENA_MASK 0x0001 /* FLL2_GPDIV_ENA */ +#define ARIZONA_FLL2_GPDIV_ENA_SHIFT 0 /* FLL2_GPDIV_ENA */ +#define ARIZONA_FLL2_GPDIV_ENA_WIDTH 1 /* FLL2_GPDIV_ENA */ + +/* + * R512 (0x200) - Mic Charge Pump 1 + */ +#define ARIZONA_CPMIC_DISCH 0x0004 /* CPMIC_DISCH */ +#define ARIZONA_CPMIC_DISCH_MASK 0x0004 /* CPMIC_DISCH */ +#define ARIZONA_CPMIC_DISCH_SHIFT 2 /* CPMIC_DISCH */ +#define ARIZONA_CPMIC_DISCH_WIDTH 1 /* CPMIC_DISCH */ +#define ARIZONA_CPMIC_BYPASS 0x0002 /* CPMIC_BYPASS */ +#define ARIZONA_CPMIC_BYPASS_MASK 0x0002 /* CPMIC_BYPASS */ +#define ARIZONA_CPMIC_BYPASS_SHIFT 1 /* CPMIC_BYPASS */ +#define ARIZONA_CPMIC_BYPASS_WIDTH 1 /* CPMIC_BYPASS */ +#define ARIZONA_CPMIC_ENA 0x0001 /* CPMIC_ENA */ +#define ARIZONA_CPMIC_ENA_MASK 0x0001 /* CPMIC_ENA */ +#define ARIZONA_CPMIC_ENA_SHIFT 0 /* CPMIC_ENA */ +#define ARIZONA_CPMIC_ENA_WIDTH 1 /* CPMIC_ENA */ + +/* + * R528 (0x210) - LDO1 Control 1 + */ +#define ARIZONA_LDO1_VSEL_MASK 0x07E0 /* LDO1_VSEL - [10:5] */ +#define ARIZONA_LDO1_VSEL_SHIFT 5 /* LDO1_VSEL - [10:5] */ +#define ARIZONA_LDO1_VSEL_WIDTH 6 /* LDO1_VSEL - [10:5] */ +#define ARIZONA_LDO1_FAST 0x0010 /* LDO1_FAST */ +#define ARIZONA_LDO1_FAST_MASK 0x0010 /* LDO1_FAST */ +#define ARIZONA_LDO1_FAST_SHIFT 4 /* LDO1_FAST */ +#define ARIZONA_LDO1_FAST_WIDTH 1 /* LDO1_FAST */ +#define ARIZONA_LDO1_DISCH 0x0004 /* LDO1_DISCH */ +#define ARIZONA_LDO1_DISCH_MASK 0x0004 /* LDO1_DISCH */ +#define ARIZONA_LDO1_DISCH_SHIFT 2 /* LDO1_DISCH */ +#define ARIZONA_LDO1_DISCH_WIDTH 1 /* LDO1_DISCH */ +#define ARIZONA_LDO1_BYPASS 0x0002 /* LDO1_BYPASS */ +#define ARIZONA_LDO1_BYPASS_MASK 0x0002 /* LDO1_BYPASS */ +#define ARIZONA_LDO1_BYPASS_SHIFT 1 /* LDO1_BYPASS */ +#define ARIZONA_LDO1_BYPASS_WIDTH 1 /* LDO1_BYPASS */ +#define ARIZONA_LDO1_ENA 0x0001 /* LDO1_ENA */ +#define ARIZONA_LDO1_ENA_MASK 0x0001 /* LDO1_ENA */ +#define ARIZONA_LDO1_ENA_SHIFT 0 /* LDO1_ENA */ +#define ARIZONA_LDO1_ENA_WIDTH 1 /* LDO1_ENA */ + +/* + * R531 (0x213) - LDO2 Control 1 + */ +#define ARIZONA_LDO2_VSEL_MASK 0x07E0 /* LDO2_VSEL - [10:5] */ +#define ARIZONA_LDO2_VSEL_SHIFT 5 /* LDO2_VSEL - [10:5] */ +#define ARIZONA_LDO2_VSEL_WIDTH 6 /* LDO2_VSEL - [10:5] */ +#define ARIZONA_LDO2_FAST 0x0010 /* LDO2_FAST */ +#define ARIZONA_LDO2_FAST_MASK 0x0010 /* LDO2_FAST */ +#define ARIZONA_LDO2_FAST_SHIFT 4 /* LDO2_FAST */ +#define ARIZONA_LDO2_FAST_WIDTH 1 /* LDO2_FAST */ +#define ARIZONA_LDO2_DISCH 0x0004 /* LDO2_DISCH */ +#define ARIZONA_LDO2_DISCH_MASK 0x0004 /* LDO2_DISCH */ +#define ARIZONA_LDO2_DISCH_SHIFT 2 /* LDO2_DISCH */ +#define ARIZONA_LDO2_DISCH_WIDTH 1 /* LDO2_DISCH */ +#define ARIZONA_LDO2_BYPASS 0x0002 /* LDO2_BYPASS */ +#define ARIZONA_LDO2_BYPASS_MASK 0x0002 /* LDO2_BYPASS */ +#define ARIZONA_LDO2_BYPASS_SHIFT 1 /* LDO2_BYPASS */ +#define ARIZONA_LDO2_BYPASS_WIDTH 1 /* LDO2_BYPASS */ +#define ARIZONA_LDO2_ENA 0x0001 /* LDO2_ENA */ +#define ARIZONA_LDO2_ENA_MASK 0x0001 /* LDO2_ENA */ +#define ARIZONA_LDO2_ENA_SHIFT 0 /* LDO2_ENA */ +#define ARIZONA_LDO2_ENA_WIDTH 1 /* LDO2_ENA */ + +/* + * R536 (0x218) - Mic Bias Ctrl 1 + */ +#define ARIZONA_MICB1_EXT_CAP 0x8000 /* MICB1_EXT_CAP */ +#define ARIZONA_MICB1_EXT_CAP_MASK 0x8000 /* MICB1_EXT_CAP */ +#define ARIZONA_MICB1_EXT_CAP_SHIFT 15 /* MICB1_EXT_CAP */ +#define ARIZONA_MICB1_EXT_CAP_WIDTH 1 /* MICB1_EXT_CAP */ +#define ARIZONA_MICB1_LVL_MASK 0x01E0 /* MICB1_LVL - [8:5] */ +#define ARIZONA_MICB1_LVL_SHIFT 5 /* MICB1_LVL - [8:5] */ +#define ARIZONA_MICB1_LVL_WIDTH 4 /* MICB1_LVL - [8:5] */ +#define ARIZONA_MICB1_FAST 0x0010 /* MICB1_FAST */ +#define ARIZONA_MICB1_FAST_MASK 0x0010 /* MICB1_FAST */ +#define ARIZONA_MICB1_FAST_SHIFT 4 /* MICB1_FAST */ +#define ARIZONA_MICB1_FAST_WIDTH 1 /* MICB1_FAST */ +#define ARIZONA_MICB1_RATE 0x0008 /* MICB1_RATE */ +#define ARIZONA_MICB1_RATE_MASK 0x0008 /* MICB1_RATE */ +#define ARIZONA_MICB1_RATE_SHIFT 3 /* MICB1_RATE */ +#define ARIZONA_MICB1_RATE_WIDTH 1 /* MICB1_RATE */ +#define ARIZONA_MICB1_DISCH 0x0004 /* MICB1_DISCH */ +#define ARIZONA_MICB1_DISCH_MASK 0x0004 /* MICB1_DISCH */ +#define ARIZONA_MICB1_DISCH_SHIFT 2 /* MICB1_DISCH */ +#define ARIZONA_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */ +#define ARIZONA_MICB1_BYPASS 0x0002 /* MICB1_BYPASS */ +#define ARIZONA_MICB1_BYPASS_MASK 0x0002 /* MICB1_BYPASS */ +#define ARIZONA_MICB1_BYPASS_SHIFT 1 /* MICB1_BYPASS */ +#define ARIZONA_MICB1_BYPASS_WIDTH 1 /* MICB1_BYPASS */ +#define ARIZONA_MICB1_ENA 0x0001 /* MICB1_ENA */ +#define ARIZONA_MICB1_ENA_MASK 0x0001 /* MICB1_ENA */ +#define ARIZONA_MICB1_ENA_SHIFT 0 /* MICB1_ENA */ +#define ARIZONA_MICB1_ENA_WIDTH 1 /* MICB1_ENA */ + +/* + * R537 (0x219) - Mic Bias Ctrl 2 + */ +#define ARIZONA_MICB2_EXT_CAP 0x8000 /* MICB2_EXT_CAP */ +#define ARIZONA_MICB2_EXT_CAP_MASK 0x8000 /* MICB2_EXT_CAP */ +#define ARIZONA_MICB2_EXT_CAP_SHIFT 15 /* MICB2_EXT_CAP */ +#define ARIZONA_MICB2_EXT_CAP_WIDTH 1 /* MICB2_EXT_CAP */ +#define ARIZONA_MICB2_LVL_MASK 0x01E0 /* MICB2_LVL - [8:5] */ +#define ARIZONA_MICB2_LVL_SHIFT 5 /* MICB2_LVL - [8:5] */ +#define ARIZONA_MICB2_LVL_WIDTH 4 /* MICB2_LVL - [8:5] */ +#define ARIZONA_MICB2_FAST 0x0010 /* MICB2_FAST */ +#define ARIZONA_MICB2_FAST_MASK 0x0010 /* MICB2_FAST */ +#define ARIZONA_MICB2_FAST_SHIFT 4 /* MICB2_FAST */ +#define ARIZONA_MICB2_FAST_WIDTH 1 /* MICB2_FAST */ +#define ARIZONA_MICB2_RATE 0x0008 /* MICB2_RATE */ +#define ARIZONA_MICB2_RATE_MASK 0x0008 /* MICB2_RATE */ +#define ARIZONA_MICB2_RATE_SHIFT 3 /* MICB2_RATE */ +#define ARIZONA_MICB2_RATE_WIDTH 1 /* MICB2_RATE */ +#define ARIZONA_MICB2_DISCH 0x0004 /* MICB2_DISCH */ +#define ARIZONA_MICB2_DISCH_MASK 0x0004 /* MICB2_DISCH */ +#define ARIZONA_MICB2_DISCH_SHIFT 2 /* MICB2_DISCH */ +#define ARIZONA_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */ +#define ARIZONA_MICB2_BYPASS 0x0002 /* MICB2_BYPASS */ +#define ARIZONA_MICB2_BYPASS_MASK 0x0002 /* MICB2_BYPASS */ +#define ARIZONA_MICB2_BYPASS_SHIFT 1 /* MICB2_BYPASS */ +#define ARIZONA_MICB2_BYPASS_WIDTH 1 /* MICB2_BYPASS */ +#define ARIZONA_MICB2_ENA 0x0001 /* MICB2_ENA */ +#define ARIZONA_MICB2_ENA_MASK 0x0001 /* MICB2_ENA */ +#define ARIZONA_MICB2_ENA_SHIFT 0 /* MICB2_ENA */ +#define ARIZONA_MICB2_ENA_WIDTH 1 /* MICB2_ENA */ + +/* + * R538 (0x21A) - Mic Bias Ctrl 3 + */ +#define ARIZONA_MICB3_EXT_CAP 0x8000 /* MICB3_EXT_CAP */ +#define ARIZONA_MICB3_EXT_CAP_MASK 0x8000 /* MICB3_EXT_CAP */ +#define ARIZONA_MICB3_EXT_CAP_SHIFT 15 /* MICB3_EXT_CAP */ +#define ARIZONA_MICB3_EXT_CAP_WIDTH 1 /* MICB3_EXT_CAP */ +#define ARIZONA_MICB3_LVL_MASK 0x01E0 /* MICB3_LVL - [8:5] */ +#define ARIZONA_MICB3_LVL_SHIFT 5 /* MICB3_LVL - [8:5] */ +#define ARIZONA_MICB3_LVL_WIDTH 4 /* MICB3_LVL - [8:5] */ +#define ARIZONA_MICB3_FAST 0x0010 /* MICB3_FAST */ +#define ARIZONA_MICB3_FAST_MASK 0x0010 /* MICB3_FAST */ +#define ARIZONA_MICB3_FAST_SHIFT 4 /* MICB3_FAST */ +#define ARIZONA_MICB3_FAST_WIDTH 1 /* MICB3_FAST */ +#define ARIZONA_MICB3_RATE 0x0008 /* MICB3_RATE */ +#define ARIZONA_MICB3_RATE_MASK 0x0008 /* MICB3_RATE */ +#define ARIZONA_MICB3_RATE_SHIFT 3 /* MICB3_RATE */ +#define ARIZONA_MICB3_RATE_WIDTH 1 /* MICB3_RATE */ +#define ARIZONA_MICB3_DISCH 0x0004 /* MICB3_DISCH */ +#define ARIZONA_MICB3_DISCH_MASK 0x0004 /* MICB3_DISCH */ +#define ARIZONA_MICB3_DISCH_SHIFT 2 /* MICB3_DISCH */ +#define ARIZONA_MICB3_DISCH_WIDTH 1 /* MICB3_DISCH */ +#define ARIZONA_MICB3_BYPASS 0x0002 /* MICB3_BYPASS */ +#define ARIZONA_MICB3_BYPASS_MASK 0x0002 /* MICB3_BYPASS */ +#define ARIZONA_MICB3_BYPASS_SHIFT 1 /* MICB3_BYPASS */ +#define ARIZONA_MICB3_BYPASS_WIDTH 1 /* MICB3_BYPASS */ +#define ARIZONA_MICB3_ENA 0x0001 /* MICB3_ENA */ +#define ARIZONA_MICB3_ENA_MASK 0x0001 /* MICB3_ENA */ +#define ARIZONA_MICB3_ENA_SHIFT 0 /* MICB3_ENA */ +#define ARIZONA_MICB3_ENA_WIDTH 1 /* MICB3_ENA */ + +/* + * R659 (0x293) - Accessory Detect Mode 1 + */ +#define ARIZONA_ACCDET_SRC 0x2000 /* ACCDET_SRC */ +#define ARIZONA_ACCDET_SRC_MASK 0x2000 /* ACCDET_SRC */ +#define ARIZONA_ACCDET_SRC_SHIFT 13 /* ACCDET_SRC */ +#define ARIZONA_ACCDET_SRC_WIDTH 1 /* ACCDET_SRC */ +#define ARIZONA_ACCDET_MODE_MASK 0x0003 /* ACCDET_MODE - [1:0] */ +#define ARIZONA_ACCDET_MODE_SHIFT 0 /* ACCDET_MODE - [1:0] */ +#define ARIZONA_ACCDET_MODE_WIDTH 2 /* ACCDET_MODE - [1:0] */ + +/* + * R667 (0x29B) - Headphone Detect 1 + */ +#define ARIZONA_HP_STEP_SIZE 0x0100 /* HP_STEP_SIZE */ +#define ARIZONA_HP_STEP_SIZE_MASK 0x0100 /* HP_STEP_SIZE */ +#define ARIZONA_HP_STEP_SIZE_SHIFT 8 /* HP_STEP_SIZE */ +#define ARIZONA_HP_STEP_SIZE_WIDTH 1 /* HP_STEP_SIZE */ +#define ARIZONA_HP_HOLDTIME_MASK 0x00E0 /* HP_HOLDTIME - [7:5] */ +#define ARIZONA_HP_HOLDTIME_SHIFT 5 /* HP_HOLDTIME - [7:5] */ +#define ARIZONA_HP_HOLDTIME_WIDTH 3 /* HP_HOLDTIME - [7:5] */ +#define ARIZONA_HP_CLK_DIV_MASK 0x0018 /* HP_CLK_DIV - [4:3] */ +#define ARIZONA_HP_CLK_DIV_SHIFT 3 /* HP_CLK_DIV - [4:3] */ +#define ARIZONA_HP_CLK_DIV_WIDTH 2 /* HP_CLK_DIV - [4:3] */ +#define ARIZONA_HP_IDAC_STEER 0x0004 /* HP_IDAC_STEER */ +#define ARIZONA_HP_IDAC_STEER_MASK 0x0004 /* HP_IDAC_STEER */ +#define ARIZONA_HP_IDAC_STEER_SHIFT 2 /* HP_IDAC_STEER */ +#define ARIZONA_HP_IDAC_STEER_WIDTH 1 /* HP_IDAC_STEER */ +#define ARIZONA_HP_RATE 0x0002 /* HP_RATE */ +#define ARIZONA_HP_RATE_MASK 0x0002 /* HP_RATE */ +#define ARIZONA_HP_RATE_SHIFT 1 /* HP_RATE */ +#define ARIZONA_HP_RATE_WIDTH 1 /* HP_RATE */ +#define ARIZONA_HP_POLL 0x0001 /* HP_POLL */ +#define ARIZONA_HP_POLL_MASK 0x0001 /* HP_POLL */ +#define ARIZONA_HP_POLL_SHIFT 0 /* HP_POLL */ +#define ARIZONA_HP_POLL_WIDTH 1 /* HP_POLL */ + +/* + * R668 (0x29C) - Headphone Detect 2 + */ +#define ARIZONA_HP_DONE 0x0080 /* HP_DONE */ +#define ARIZONA_HP_DONE_MASK 0x0080 /* HP_DONE */ +#define ARIZONA_HP_DONE_SHIFT 7 /* HP_DONE */ +#define ARIZONA_HP_DONE_WIDTH 1 /* HP_DONE */ +#define ARIZONA_HP_LVL_MASK 0x007F /* HP_LVL - [6:0] */ +#define ARIZONA_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */ +#define ARIZONA_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */ + +/* + * R675 (0x2A3) - Mic Detect 1 + */ +#define ARIZONA_MICD_BIAS_STARTTIME_MASK 0xF000 /* MICD_BIAS_STARTTIME - [15:12] */ +#define ARIZONA_MICD_BIAS_STARTTIME_SHIFT 12 /* MICD_BIAS_STARTTIME - [15:12] */ +#define ARIZONA_MICD_BIAS_STARTTIME_WIDTH 4 /* MICD_BIAS_STARTTIME - [15:12] */ +#define ARIZONA_MICD_RATE_MASK 0x0F00 /* MICD_RATE - [11:8] */ +#define ARIZONA_MICD_RATE_SHIFT 8 /* MICD_RATE - [11:8] */ +#define ARIZONA_MICD_RATE_WIDTH 4 /* MICD_RATE - [11:8] */ +#define ARIZONA_MICD_BIAS_SRC_MASK 0x0030 /* MICD_BIAS_SRC - [5:4] */ +#define ARIZONA_MICD_BIAS_SRC_SHIFT 4 /* MICD_BIAS_SRC - [5:4] */ +#define ARIZONA_MICD_BIAS_SRC_WIDTH 2 /* MICD_BIAS_SRC - [5:4] */ +#define ARIZONA_MICD_DBTIME 0x0002 /* MICD_DBTIME */ +#define ARIZONA_MICD_DBTIME_MASK 0x0002 /* MICD_DBTIME */ +#define ARIZONA_MICD_DBTIME_SHIFT 1 /* MICD_DBTIME */ +#define ARIZONA_MICD_DBTIME_WIDTH 1 /* MICD_DBTIME */ +#define ARIZONA_MICD_ENA 0x0001 /* MICD_ENA */ +#define ARIZONA_MICD_ENA_MASK 0x0001 /* MICD_ENA */ +#define ARIZONA_MICD_ENA_SHIFT 0 /* MICD_ENA */ +#define ARIZONA_MICD_ENA_WIDTH 1 /* MICD_ENA */ + +/* + * R676 (0x2A4) - Mic Detect 2 + */ +#define ARIZONA_MICD_LVL_SEL_MASK 0x00FF /* MICD_LVL_SEL - [7:0] */ +#define ARIZONA_MICD_LVL_SEL_SHIFT 0 /* MICD_LVL_SEL - [7:0] */ +#define ARIZONA_MICD_LVL_SEL_WIDTH 8 /* MICD_LVL_SEL - [7:0] */ + +/* + * R677 (0x2A5) - Mic Detect 3 + */ +#define ARIZONA_MICD_LVL_MASK 0x07FC /* MICD_LVL - [10:2] */ +#define ARIZONA_MICD_LVL_SHIFT 2 /* MICD_LVL - [10:2] */ +#define ARIZONA_MICD_LVL_WIDTH 9 /* MICD_LVL - [10:2] */ +#define ARIZONA_MICD_VALID 0x0002 /* MICD_VALID */ +#define ARIZONA_MICD_VALID_MASK 0x0002 /* MICD_VALID */ +#define ARIZONA_MICD_VALID_SHIFT 1 /* MICD_VALID */ +#define ARIZONA_MICD_VALID_WIDTH 1 /* MICD_VALID */ +#define ARIZONA_MICD_STS 0x0001 /* MICD_STS */ +#define ARIZONA_MICD_STS_MASK 0x0001 /* MICD_STS */ +#define ARIZONA_MICD_STS_SHIFT 0 /* MICD_STS */ +#define ARIZONA_MICD_STS_WIDTH 1 /* MICD_STS */ + +/* + * R707 (0x2C3) - Mic noise mix control 1 + */ +#define ARIZONA_MICMUTE_RATE_MASK 0x7800 /* MICMUTE_RATE - [14:11] */ +#define ARIZONA_MICMUTE_RATE_SHIFT 11 /* MICMUTE_RATE - [14:11] */ +#define ARIZONA_MICMUTE_RATE_WIDTH 4 /* MICMUTE_RATE - [14:11] */ +#define ARIZONA_MICMUTE_MIX_ENA 0x0040 /* MICMUTE_MIX_ENA */ +#define ARIZONA_MICMUTE_MIX_ENA_MASK 0x0040 /* MICMUTE_MIX_ENA */ +#define ARIZONA_MICMUTE_MIX_ENA_SHIFT 6 /* MICMUTE_MIX_ENA */ +#define ARIZONA_MICMUTE_MIX_ENA_WIDTH 1 /* MICMUTE_MIX_ENA */ + +/* + * R715 (0x2CB) - Isolation control + */ +#define ARIZONA_ISOLATE_DCVDD1 0x0001 /* ISOLATE_DCVDD1 */ +#define ARIZONA_ISOLATE_DCVDD1_MASK 0x0001 /* ISOLATE_DCVDD1 */ +#define ARIZONA_ISOLATE_DCVDD1_SHIFT 0 /* ISOLATE_DCVDD1 */ +#define ARIZONA_ISOLATE_DCVDD1_WIDTH 1 /* ISOLATE_DCVDD1 */ + +/* + * R723 (0x2D3) - Jack detect analogue + */ +#define ARIZONA_JD2_ENA 0x0002 /* JD2_ENA */ +#define ARIZONA_JD2_ENA_MASK 0x0002 /* JD2_ENA */ +#define ARIZONA_JD2_ENA_SHIFT 1 /* JD2_ENA */ +#define ARIZONA_JD2_ENA_WIDTH 1 /* JD2_ENA */ +#define ARIZONA_JD1_ENA 0x0001 /* JD1_ENA */ +#define ARIZONA_JD1_ENA_MASK 0x0001 /* JD1_ENA */ +#define ARIZONA_JD1_ENA_SHIFT 0 /* JD1_ENA */ +#define ARIZONA_JD1_ENA_WIDTH 1 /* JD1_ENA */ + +/* + * R768 (0x300) - Input Enables + */ +#define ARIZONA_IN3L_ENA 0x0020 /* IN3L_ENA */ +#define ARIZONA_IN3L_ENA_MASK 0x0020 /* IN3L_ENA */ +#define ARIZONA_IN3L_ENA_SHIFT 5 /* IN3L_ENA */ +#define ARIZONA_IN3L_ENA_WIDTH 1 /* IN3L_ENA */ +#define ARIZONA_IN3R_ENA 0x0010 /* IN3R_ENA */ +#define ARIZONA_IN3R_ENA_MASK 0x0010 /* IN3R_ENA */ +#define ARIZONA_IN3R_ENA_SHIFT 4 /* IN3R_ENA */ +#define ARIZONA_IN3R_ENA_WIDTH 1 /* IN3R_ENA */ +#define ARIZONA_IN2L_ENA 0x0008 /* IN2L_ENA */ +#define ARIZONA_IN2L_ENA_MASK 0x0008 /* IN2L_ENA */ +#define ARIZONA_IN2L_ENA_SHIFT 3 /* IN2L_ENA */ +#define ARIZONA_IN2L_ENA_WIDTH 1 /* IN2L_ENA */ +#define ARIZONA_IN2R_ENA 0x0004 /* IN2R_ENA */ +#define ARIZONA_IN2R_ENA_MASK 0x0004 /* IN2R_ENA */ +#define ARIZONA_IN2R_ENA_SHIFT 2 /* IN2R_ENA */ +#define ARIZONA_IN2R_ENA_WIDTH 1 /* IN2R_ENA */ +#define ARIZONA_IN1L_ENA 0x0002 /* IN1L_ENA */ +#define ARIZONA_IN1L_ENA_MASK 0x0002 /* IN1L_ENA */ +#define ARIZONA_IN1L_ENA_SHIFT 1 /* IN1L_ENA */ +#define ARIZONA_IN1L_ENA_WIDTH 1 /* IN1L_ENA */ +#define ARIZONA_IN1R_ENA 0x0001 /* IN1R_ENA */ +#define ARIZONA_IN1R_ENA_MASK 0x0001 /* IN1R_ENA */ +#define ARIZONA_IN1R_ENA_SHIFT 0 /* IN1R_ENA */ +#define ARIZONA_IN1R_ENA_WIDTH 1 /* IN1R_ENA */ + +/* + * R776 (0x308) - Input Rate + */ +#define ARIZONA_IN_RATE_MASK 0x7800 /* IN_RATE - [14:11] */ +#define ARIZONA_IN_RATE_SHIFT 11 /* IN_RATE - [14:11] */ +#define ARIZONA_IN_RATE_WIDTH 4 /* IN_RATE - [14:11] */ + +/* + * R777 (0x309) - Input Volume Ramp + */ +#define ARIZONA_IN_VD_RAMP_MASK 0x0070 /* IN_VD_RAMP - [6:4] */ +#define ARIZONA_IN_VD_RAMP_SHIFT 4 /* IN_VD_RAMP - [6:4] */ +#define ARIZONA_IN_VD_RAMP_WIDTH 3 /* IN_VD_RAMP - [6:4] */ +#define ARIZONA_IN_VI_RAMP_MASK 0x0007 /* IN_VI_RAMP - [2:0] */ +#define ARIZONA_IN_VI_RAMP_SHIFT 0 /* IN_VI_RAMP - [2:0] */ +#define ARIZONA_IN_VI_RAMP_WIDTH 3 /* IN_VI_RAMP - [2:0] */ + +/* + * R784 (0x310) - IN1L Control + */ +#define ARIZONA_IN1_OSR_MASK 0x6000 /* IN1_OSR - [14:13] */ +#define ARIZONA_IN1_OSR_SHIFT 13 /* IN1_OSR - [14:13] */ +#define ARIZONA_IN1_OSR_WIDTH 2 /* IN1_OSR - [14:13] */ +#define ARIZONA_IN1_DMIC_SUP_MASK 0x1800 /* IN1_DMIC_SUP - [12:11] */ +#define ARIZONA_IN1_DMIC_SUP_SHIFT 11 /* IN1_DMIC_SUP - [12:11] */ +#define ARIZONA_IN1_DMIC_SUP_WIDTH 2 /* IN1_DMIC_SUP - [12:11] */ +#define ARIZONA_IN1_MODE_MASK 0x0600 /* IN1_MODE - [10:9] */ +#define ARIZONA_IN1_MODE_SHIFT 9 /* IN1_MODE - [10:9] */ +#define ARIZONA_IN1_MODE_WIDTH 2 /* IN1_MODE - [10:9] */ +#define ARIZONA_IN1L_PGA_VOL_MASK 0x00FE /* IN1L_PGA_VOL - [7:1] */ +#define ARIZONA_IN1L_PGA_VOL_SHIFT 1 /* IN1L_PGA_VOL - [7:1] */ +#define ARIZONA_IN1L_PGA_VOL_WIDTH 7 /* IN1L_PGA_VOL - [7:1] */ + +/* + * R785 (0x311) - ADC Digital Volume 1L + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN1L_MUTE 0x0100 /* IN1L_MUTE */ +#define ARIZONA_IN1L_MUTE_MASK 0x0100 /* IN1L_MUTE */ +#define ARIZONA_IN1L_MUTE_SHIFT 8 /* IN1L_MUTE */ +#define ARIZONA_IN1L_MUTE_WIDTH 1 /* IN1L_MUTE */ +#define ARIZONA_IN1L_DIG_VOL_MASK 0x00FF /* IN1L_DIG_VOL - [7:0] */ +#define ARIZONA_IN1L_DIG_VOL_SHIFT 0 /* IN1L_DIG_VOL - [7:0] */ +#define ARIZONA_IN1L_DIG_VOL_WIDTH 8 /* IN1L_DIG_VOL - [7:0] */ + +/* + * R786 (0x312) - DMIC1L Control + */ +#define ARIZONA_IN1_DMICL_DLY_MASK 0x003F /* IN1_DMICL_DLY - [5:0] */ +#define ARIZONA_IN1_DMICL_DLY_SHIFT 0 /* IN1_DMICL_DLY - [5:0] */ +#define ARIZONA_IN1_DMICL_DLY_WIDTH 6 /* IN1_DMICL_DLY - [5:0] */ + +/* + * R788 (0x314) - IN1R Control + */ +#define ARIZONA_IN1R_PGA_VOL_MASK 0x00FE /* IN1R_PGA_VOL - [7:1] */ +#define ARIZONA_IN1R_PGA_VOL_SHIFT 1 /* IN1R_PGA_VOL - [7:1] */ +#define ARIZONA_IN1R_PGA_VOL_WIDTH 7 /* IN1R_PGA_VOL - [7:1] */ + +/* + * R789 (0x315) - ADC Digital Volume 1R + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN1R_MUTE 0x0100 /* IN1R_MUTE */ +#define ARIZONA_IN1R_MUTE_MASK 0x0100 /* IN1R_MUTE */ +#define ARIZONA_IN1R_MUTE_SHIFT 8 /* IN1R_MUTE */ +#define ARIZONA_IN1R_MUTE_WIDTH 1 /* IN1R_MUTE */ +#define ARIZONA_IN1R_DIG_VOL_MASK 0x00FF /* IN1R_DIG_VOL - [7:0] */ +#define ARIZONA_IN1R_DIG_VOL_SHIFT 0 /* IN1R_DIG_VOL - [7:0] */ +#define ARIZONA_IN1R_DIG_VOL_WIDTH 8 /* IN1R_DIG_VOL - [7:0] */ + +/* + * R790 (0x316) - DMIC1R Control + */ +#define ARIZONA_IN1_DMICR_DLY_MASK 0x003F /* IN1_DMICR_DLY - [5:0] */ +#define ARIZONA_IN1_DMICR_DLY_SHIFT 0 /* IN1_DMICR_DLY - [5:0] */ +#define ARIZONA_IN1_DMICR_DLY_WIDTH 6 /* IN1_DMICR_DLY - [5:0] */ + +/* + * R792 (0x318) - IN2L Control + */ +#define ARIZONA_IN2_OSR_MASK 0x6000 /* IN2_OSR - [14:13] */ +#define ARIZONA_IN2_OSR_SHIFT 13 /* IN2_OSR - [14:13] */ +#define ARIZONA_IN2_OSR_WIDTH 2 /* IN2_OSR - [14:13] */ +#define ARIZONA_IN2_DMIC_SUP_MASK 0x1800 /* IN2_DMIC_SUP - [12:11] */ +#define ARIZONA_IN2_DMIC_SUP_SHIFT 11 /* IN2_DMIC_SUP - [12:11] */ +#define ARIZONA_IN2_DMIC_SUP_WIDTH 2 /* IN2_DMIC_SUP - [12:11] */ +#define ARIZONA_IN2_MODE_MASK 0x0600 /* IN2_MODE - [10:9] */ +#define ARIZONA_IN2_MODE_SHIFT 9 /* IN2_MODE - [10:9] */ +#define ARIZONA_IN2_MODE_WIDTH 2 /* IN2_MODE - [10:9] */ +#define ARIZONA_IN2L_PGA_VOL_MASK 0x00FE /* IN2L_PGA_VOL - [7:1] */ +#define ARIZONA_IN2L_PGA_VOL_SHIFT 1 /* IN2L_PGA_VOL - [7:1] */ +#define ARIZONA_IN2L_PGA_VOL_WIDTH 7 /* IN2L_PGA_VOL - [7:1] */ + +/* + * R793 (0x319) - ADC Digital Volume 2L + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN2L_MUTE 0x0100 /* IN2L_MUTE */ +#define ARIZONA_IN2L_MUTE_MASK 0x0100 /* IN2L_MUTE */ +#define ARIZONA_IN2L_MUTE_SHIFT 8 /* IN2L_MUTE */ +#define ARIZONA_IN2L_MUTE_WIDTH 1 /* IN2L_MUTE */ +#define ARIZONA_IN2L_DIG_VOL_MASK 0x00FF /* IN2L_DIG_VOL - [7:0] */ +#define ARIZONA_IN2L_DIG_VOL_SHIFT 0 /* IN2L_DIG_VOL - [7:0] */ +#define ARIZONA_IN2L_DIG_VOL_WIDTH 8 /* IN2L_DIG_VOL - [7:0] */ + +/* + * R794 (0x31A) - DMIC2L Control + */ +#define ARIZONA_IN2_DMICL_DLY_MASK 0x003F /* IN2_DMICL_DLY - [5:0] */ +#define ARIZONA_IN2_DMICL_DLY_SHIFT 0 /* IN2_DMICL_DLY - [5:0] */ +#define ARIZONA_IN2_DMICL_DLY_WIDTH 6 /* IN2_DMICL_DLY - [5:0] */ + +/* + * R796 (0x31C) - IN2R Control + */ +#define ARIZONA_IN2R_PGA_VOL_MASK 0x00FE /* IN2R_PGA_VOL - [7:1] */ +#define ARIZONA_IN2R_PGA_VOL_SHIFT 1 /* IN2R_PGA_VOL - [7:1] */ +#define ARIZONA_IN2R_PGA_VOL_WIDTH 7 /* IN2R_PGA_VOL - [7:1] */ + +/* + * R797 (0x31D) - ADC Digital Volume 2R + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN2R_MUTE 0x0100 /* IN2R_MUTE */ +#define ARIZONA_IN2R_MUTE_MASK 0x0100 /* IN2R_MUTE */ +#define ARIZONA_IN2R_MUTE_SHIFT 8 /* IN2R_MUTE */ +#define ARIZONA_IN2R_MUTE_WIDTH 1 /* IN2R_MUTE */ +#define ARIZONA_IN2R_DIG_VOL_MASK 0x00FF /* IN2R_DIG_VOL - [7:0] */ +#define ARIZONA_IN2R_DIG_VOL_SHIFT 0 /* IN2R_DIG_VOL - [7:0] */ +#define ARIZONA_IN2R_DIG_VOL_WIDTH 8 /* IN2R_DIG_VOL - [7:0] */ + +/* + * R798 (0x31E) - DMIC2R Control + */ +#define ARIZONA_IN2_DMICR_DLY_MASK 0x003F /* IN2_DMICR_DLY - [5:0] */ +#define ARIZONA_IN2_DMICR_DLY_SHIFT 0 /* IN2_DMICR_DLY - [5:0] */ +#define ARIZONA_IN2_DMICR_DLY_WIDTH 6 /* IN2_DMICR_DLY - [5:0] */ + +/* + * R800 (0x320) - IN3L Control + */ +#define ARIZONA_IN3_OSR_MASK 0x6000 /* IN3_OSR - [14:13] */ +#define ARIZONA_IN3_OSR_SHIFT 13 /* IN3_OSR - [14:13] */ +#define ARIZONA_IN3_OSR_WIDTH 2 /* IN3_OSR - [14:13] */ +#define ARIZONA_IN3_DMIC_SUP_MASK 0x1800 /* IN3_DMIC_SUP - [12:11] */ +#define ARIZONA_IN3_DMIC_SUP_SHIFT 11 /* IN3_DMIC_SUP - [12:11] */ +#define ARIZONA_IN3_DMIC_SUP_WIDTH 2 /* IN3_DMIC_SUP - [12:11] */ +#define ARIZONA_IN3_MODE_MASK 0x0600 /* IN3_MODE - [10:9] */ +#define ARIZONA_IN3_MODE_SHIFT 9 /* IN3_MODE - [10:9] */ +#define ARIZONA_IN3_MODE_WIDTH 2 /* IN3_MODE - [10:9] */ +#define ARIZONA_IN3L_PGA_VOL_MASK 0x00FE /* IN3L_PGA_VOL - [7:1] */ +#define ARIZONA_IN3L_PGA_VOL_SHIFT 1 /* IN3L_PGA_VOL - [7:1] */ +#define ARIZONA_IN3L_PGA_VOL_WIDTH 7 /* IN3L_PGA_VOL - [7:1] */ + +/* + * R801 (0x321) - ADC Digital Volume 3L + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN3L_MUTE 0x0100 /* IN3L_MUTE */ +#define ARIZONA_IN3L_MUTE_MASK 0x0100 /* IN3L_MUTE */ +#define ARIZONA_IN3L_MUTE_SHIFT 8 /* IN3L_MUTE */ +#define ARIZONA_IN3L_MUTE_WIDTH 1 /* IN3L_MUTE */ +#define ARIZONA_IN3L_DIG_VOL_MASK 0x00FF /* IN3L_DIG_VOL - [7:0] */ +#define ARIZONA_IN3L_DIG_VOL_SHIFT 0 /* IN3L_DIG_VOL - [7:0] */ +#define ARIZONA_IN3L_DIG_VOL_WIDTH 8 /* IN3L_DIG_VOL - [7:0] */ + +/* + * R802 (0x322) - DMIC3L Control + */ +#define ARIZONA_IN3_DMICL_DLY_MASK 0x003F /* IN3_DMICL_DLY - [5:0] */ +#define ARIZONA_IN3_DMICL_DLY_SHIFT 0 /* IN3_DMICL_DLY - [5:0] */ +#define ARIZONA_IN3_DMICL_DLY_WIDTH 6 /* IN3_DMICL_DLY - [5:0] */ + +/* + * R804 (0x324) - IN3R Control + */ +#define ARIZONA_IN3R_PGA_VOL_MASK 0x00FE /* IN3R_PGA_VOL - [7:1] */ +#define ARIZONA_IN3R_PGA_VOL_SHIFT 1 /* IN3R_PGA_VOL - [7:1] */ +#define ARIZONA_IN3R_PGA_VOL_WIDTH 7 /* IN3R_PGA_VOL - [7:1] */ + +/* + * R805 (0x325) - ADC Digital Volume 3R + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN3R_MUTE 0x0100 /* IN3R_MUTE */ +#define ARIZONA_IN3R_MUTE_MASK 0x0100 /* IN3R_MUTE */ +#define ARIZONA_IN3R_MUTE_SHIFT 8 /* IN3R_MUTE */ +#define ARIZONA_IN3R_MUTE_WIDTH 1 /* IN3R_MUTE */ +#define ARIZONA_IN3R_DIG_VOL_MASK 0x00FF /* IN3R_DIG_VOL - [7:0] */ +#define ARIZONA_IN3R_DIG_VOL_SHIFT 0 /* IN3R_DIG_VOL - [7:0] */ +#define ARIZONA_IN3R_DIG_VOL_WIDTH 8 /* IN3R_DIG_VOL - [7:0] */ + +/* + * R806 (0x326) - DMIC3R Control + */ +#define ARIZONA_IN3_DMICR_DLY_MASK 0x003F /* IN3_DMICR_DLY - [5:0] */ +#define ARIZONA_IN3_DMICR_DLY_SHIFT 0 /* IN3_DMICR_DLY - [5:0] */ +#define ARIZONA_IN3_DMICR_DLY_WIDTH 6 /* IN3_DMICR_DLY - [5:0] */ + +/* + * R1024 (0x400) - Output Enables 1 + */ +#define ARIZONA_OUT5L_ENA 0x0200 /* OUT5L_ENA */ +#define ARIZONA_OUT5L_ENA_MASK 0x0200 /* OUT5L_ENA */ +#define ARIZONA_OUT5L_ENA_SHIFT 9 /* OUT5L_ENA */ +#define ARIZONA_OUT5L_ENA_WIDTH 1 /* OUT5L_ENA */ +#define ARIZONA_OUT5R_ENA 0x0100 /* OUT5R_ENA */ +#define ARIZONA_OUT5R_ENA_MASK 0x0100 /* OUT5R_ENA */ +#define ARIZONA_OUT5R_ENA_SHIFT 8 /* OUT5R_ENA */ +#define ARIZONA_OUT5R_ENA_WIDTH 1 /* OUT5R_ENA */ +#define ARIZONA_OUT4L_ENA 0x0080 /* OUT4L_ENA */ +#define ARIZONA_OUT4L_ENA_MASK 0x0080 /* OUT4L_ENA */ +#define ARIZONA_OUT4L_ENA_SHIFT 7 /* OUT4L_ENA */ +#define ARIZONA_OUT4L_ENA_WIDTH 1 /* OUT4L_ENA */ +#define ARIZONA_OUT4R_ENA 0x0040 /* OUT4R_ENA */ +#define ARIZONA_OUT4R_ENA_MASK 0x0040 /* OUT4R_ENA */ +#define ARIZONA_OUT4R_ENA_SHIFT 6 /* OUT4R_ENA */ +#define ARIZONA_OUT4R_ENA_WIDTH 1 /* OUT4R_ENA */ +#define ARIZONA_OUT3L_ENA 0x0020 /* OUT3L_ENA */ +#define ARIZONA_OUT3L_ENA_MASK 0x0020 /* OUT3L_ENA */ +#define ARIZONA_OUT3L_ENA_SHIFT 5 /* OUT3L_ENA */ +#define ARIZONA_OUT3L_ENA_WIDTH 1 /* OUT3L_ENA */ +#define ARIZONA_OUT3R_ENA 0x0010 /* OUT3R_ENA */ +#define ARIZONA_OUT3R_ENA_MASK 0x0010 /* OUT3R_ENA */ +#define ARIZONA_OUT3R_ENA_SHIFT 4 /* OUT3R_ENA */ +#define ARIZONA_OUT3R_ENA_WIDTH 1 /* OUT3R_ENA */ +#define ARIZONA_OUT2L_ENA 0x0008 /* OUT2L_ENA */ +#define ARIZONA_OUT2L_ENA_MASK 0x0008 /* OUT2L_ENA */ +#define ARIZONA_OUT2L_ENA_SHIFT 3 /* OUT2L_ENA */ +#define ARIZONA_OUT2L_ENA_WIDTH 1 /* OUT2L_ENA */ +#define ARIZONA_OUT2R_ENA 0x0004 /* OUT2R_ENA */ +#define ARIZONA_OUT2R_ENA_MASK 0x0004 /* OUT2R_ENA */ +#define ARIZONA_OUT2R_ENA_SHIFT 2 /* OUT2R_ENA */ +#define ARIZONA_OUT2R_ENA_WIDTH 1 /* OUT2R_ENA */ +#define ARIZONA_OUT1L_ENA 0x0002 /* OUT1L_ENA */ +#define ARIZONA_OUT1L_ENA_MASK 0x0002 /* OUT1L_ENA */ +#define ARIZONA_OUT1L_ENA_SHIFT 1 /* OUT1L_ENA */ +#define ARIZONA_OUT1L_ENA_WIDTH 1 /* OUT1L_ENA */ +#define ARIZONA_OUT1R_ENA 0x0001 /* OUT1R_ENA */ +#define ARIZONA_OUT1R_ENA_MASK 0x0001 /* OUT1R_ENA */ +#define ARIZONA_OUT1R_ENA_SHIFT 0 /* OUT1R_ENA */ +#define ARIZONA_OUT1R_ENA_WIDTH 1 /* OUT1R_ENA */ + +/* + * R1025 (0x401) - Output Status 1 + */ +#define ARIZONA_OUT6L_ENA_STS 0x0800 /* OUT6L_ENA_STS */ +#define ARIZONA_OUT6L_ENA_STS_MASK 0x0800 /* OUT6L_ENA_STS */ +#define ARIZONA_OUT6L_ENA_STS_SHIFT 11 /* OUT6L_ENA_STS */ +#define ARIZONA_OUT6L_ENA_STS_WIDTH 1 /* OUT6L_ENA_STS */ +#define ARIZONA_OUT6R_ENA_STS 0x0400 /* OUT6R_ENA_STS */ +#define ARIZONA_OUT6R_ENA_STS_MASK 0x0400 /* OUT6R_ENA_STS */ +#define ARIZONA_OUT6R_ENA_STS_SHIFT 10 /* OUT6R_ENA_STS */ +#define ARIZONA_OUT6R_ENA_STS_WIDTH 1 /* OUT6R_ENA_STS */ +#define ARIZONA_OUT5L_ENA_STS 0x0200 /* OUT5L_ENA_STS */ +#define ARIZONA_OUT5L_ENA_STS_MASK 0x0200 /* OUT5L_ENA_STS */ +#define ARIZONA_OUT5L_ENA_STS_SHIFT 9 /* OUT5L_ENA_STS */ +#define ARIZONA_OUT5L_ENA_STS_WIDTH 1 /* OUT5L_ENA_STS */ +#define ARIZONA_OUT5R_ENA_STS 0x0100 /* OUT5R_ENA_STS */ +#define ARIZONA_OUT5R_ENA_STS_MASK 0x0100 /* OUT5R_ENA_STS */ +#define ARIZONA_OUT5R_ENA_STS_SHIFT 8 /* OUT5R_ENA_STS */ +#define ARIZONA_OUT5R_ENA_STS_WIDTH 1 /* OUT5R_ENA_STS */ +#define ARIZONA_OUT4L_ENA_STS 0x0080 /* OUT4L_ENA_STS */ +#define ARIZONA_OUT4L_ENA_STS_MASK 0x0080 /* OUT4L_ENA_STS */ +#define ARIZONA_OUT4L_ENA_STS_SHIFT 7 /* OUT4L_ENA_STS */ +#define ARIZONA_OUT4L_ENA_STS_WIDTH 1 /* OUT4L_ENA_STS */ +#define ARIZONA_OUT4R_ENA_STS 0x0040 /* OUT4R_ENA_STS */ +#define ARIZONA_OUT4R_ENA_STS_MASK 0x0040 /* OUT4R_ENA_STS */ +#define ARIZONA_OUT4R_ENA_STS_SHIFT 6 /* OUT4R_ENA_STS */ +#define ARIZONA_OUT4R_ENA_STS_WIDTH 1 /* OUT4R_ENA_STS */ + +/* + * R1032 (0x408) - Output Rate 1 + */ +#define ARIZONA_OUT_RATE_MASK 0x7800 /* OUT_RATE - [14:11] */ +#define ARIZONA_OUT_RATE_SHIFT 11 /* OUT_RATE - [14:11] */ +#define ARIZONA_OUT_RATE_WIDTH 4 /* OUT_RATE - [14:11] */ + +/* + * R1033 (0x409) - Output Volume Ramp + */ +#define ARIZONA_OUT_VD_RAMP_MASK 0x0070 /* OUT_VD_RAMP - [6:4] */ +#define ARIZONA_OUT_VD_RAMP_SHIFT 4 /* OUT_VD_RAMP - [6:4] */ +#define ARIZONA_OUT_VD_RAMP_WIDTH 3 /* OUT_VD_RAMP - [6:4] */ +#define ARIZONA_OUT_VI_RAMP_MASK 0x0007 /* OUT_VI_RAMP - [2:0] */ +#define ARIZONA_OUT_VI_RAMP_SHIFT 0 /* OUT_VI_RAMP - [2:0] */ +#define ARIZONA_OUT_VI_RAMP_WIDTH 3 /* OUT_VI_RAMP - [2:0] */ + +/* + * R1040 (0x410) - Output Path Config 1L + */ +#define ARIZONA_OUT1_LP_MODE 0x8000 /* OUT1_LP_MODE */ +#define ARIZONA_OUT1_LP_MODE_MASK 0x8000 /* OUT1_LP_MODE */ +#define ARIZONA_OUT1_LP_MODE_SHIFT 15 /* OUT1_LP_MODE */ +#define ARIZONA_OUT1_LP_MODE_WIDTH 1 /* OUT1_LP_MODE */ +#define ARIZONA_OUT1_OSR 0x2000 /* OUT1_OSR */ +#define ARIZONA_OUT1_OSR_MASK 0x2000 /* OUT1_OSR */ +#define ARIZONA_OUT1_OSR_SHIFT 13 /* OUT1_OSR */ +#define ARIZONA_OUT1_OSR_WIDTH 1 /* OUT1_OSR */ +#define ARIZONA_OUT1_MONO 0x1000 /* OUT1_MONO */ +#define ARIZONA_OUT1_MONO_MASK 0x1000 /* OUT1_MONO */ +#define ARIZONA_OUT1_MONO_SHIFT 12 /* OUT1_MONO */ +#define ARIZONA_OUT1_MONO_WIDTH 1 /* OUT1_MONO */ +#define ARIZONA_OUT1L_ANC_SRC_MASK 0x0C00 /* OUT1L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT1L_ANC_SRC_SHIFT 10 /* OUT1L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT1L_ANC_SRC_WIDTH 2 /* OUT1L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT1L_PGA_VOL_MASK 0x00FE /* OUT1L_PGA_VOL - [7:1] */ +#define ARIZONA_OUT1L_PGA_VOL_SHIFT 1 /* OUT1L_PGA_VOL - [7:1] */ +#define ARIZONA_OUT1L_PGA_VOL_WIDTH 7 /* OUT1L_PGA_VOL - [7:1] */ + +/* + * R1041 (0x411) - DAC Digital Volume 1L + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT1L_MUTE 0x0100 /* OUT1L_MUTE */ +#define ARIZONA_OUT1L_MUTE_MASK 0x0100 /* OUT1L_MUTE */ +#define ARIZONA_OUT1L_MUTE_SHIFT 8 /* OUT1L_MUTE */ +#define ARIZONA_OUT1L_MUTE_WIDTH 1 /* OUT1L_MUTE */ +#define ARIZONA_OUT1L_VOL_MASK 0x00FF /* OUT1L_VOL - [7:0] */ +#define ARIZONA_OUT1L_VOL_SHIFT 0 /* OUT1L_VOL - [7:0] */ +#define ARIZONA_OUT1L_VOL_WIDTH 8 /* OUT1L_VOL - [7:0] */ + +/* + * R1042 (0x412) - DAC Volume Limit 1L + */ +#define ARIZONA_OUT1L_VOL_LIM_MASK 0x00FF /* OUT1L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT1L_VOL_LIM_SHIFT 0 /* OUT1L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT1L_VOL_LIM_WIDTH 8 /* OUT1L_VOL_LIM - [7:0] */ + +/* + * R1043 (0x413) - Noise Gate Select 1L + */ +#define ARIZONA_OUT1L_NGATE_SRC_MASK 0x0FFF /* OUT1L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT1L_NGATE_SRC_SHIFT 0 /* OUT1L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT1L_NGATE_SRC_WIDTH 12 /* OUT1L_NGATE_SRC - [11:0] */ + +/* + * R1044 (0x414) - Output Path Config 1R + */ +#define ARIZONA_OUT1R_ANC_SRC_MASK 0x0C00 /* OUT1R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT1R_ANC_SRC_SHIFT 10 /* OUT1R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT1R_ANC_SRC_WIDTH 2 /* OUT1R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT1R_PGA_VOL_MASK 0x00FE /* OUT1R_PGA_VOL - [7:1] */ +#define ARIZONA_OUT1R_PGA_VOL_SHIFT 1 /* OUT1R_PGA_VOL - [7:1] */ +#define ARIZONA_OUT1R_PGA_VOL_WIDTH 7 /* OUT1R_PGA_VOL - [7:1] */ + +/* + * R1045 (0x415) - DAC Digital Volume 1R + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT1R_MUTE 0x0100 /* OUT1R_MUTE */ +#define ARIZONA_OUT1R_MUTE_MASK 0x0100 /* OUT1R_MUTE */ +#define ARIZONA_OUT1R_MUTE_SHIFT 8 /* OUT1R_MUTE */ +#define ARIZONA_OUT1R_MUTE_WIDTH 1 /* OUT1R_MUTE */ +#define ARIZONA_OUT1R_VOL_MASK 0x00FF /* OUT1R_VOL - [7:0] */ +#define ARIZONA_OUT1R_VOL_SHIFT 0 /* OUT1R_VOL - [7:0] */ +#define ARIZONA_OUT1R_VOL_WIDTH 8 /* OUT1R_VOL - [7:0] */ + +/* + * R1046 (0x416) - DAC Volume Limit 1R + */ +#define ARIZONA_OUT1R_VOL_LIM_MASK 0x00FF /* OUT1R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT1R_VOL_LIM_SHIFT 0 /* OUT1R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT1R_VOL_LIM_WIDTH 8 /* OUT1R_VOL_LIM - [7:0] */ + +/* + * R1047 (0x417) - Noise Gate Select 1R + */ +#define ARIZONA_OUT1R_NGATE_SRC_MASK 0x0FFF /* OUT1R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT1R_NGATE_SRC_SHIFT 0 /* OUT1R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT1R_NGATE_SRC_WIDTH 12 /* OUT1R_NGATE_SRC - [11:0] */ + +/* + * R1048 (0x418) - Output Path Config 2L + */ +#define ARIZONA_OUT2_LP_MODE 0x8000 /* OUT2_LP_MODE */ +#define ARIZONA_OUT2_LP_MODE_MASK 0x8000 /* OUT2_LP_MODE */ +#define ARIZONA_OUT2_LP_MODE_SHIFT 15 /* OUT2_LP_MODE */ +#define ARIZONA_OUT2_LP_MODE_WIDTH 1 /* OUT2_LP_MODE */ +#define ARIZONA_OUT2_OSR 0x2000 /* OUT2_OSR */ +#define ARIZONA_OUT2_OSR_MASK 0x2000 /* OUT2_OSR */ +#define ARIZONA_OUT2_OSR_SHIFT 13 /* OUT2_OSR */ +#define ARIZONA_OUT2_OSR_WIDTH 1 /* OUT2_OSR */ +#define ARIZONA_OUT2_MONO 0x1000 /* OUT2_MONO */ +#define ARIZONA_OUT2_MONO_MASK 0x1000 /* OUT2_MONO */ +#define ARIZONA_OUT2_MONO_SHIFT 12 /* OUT2_MONO */ +#define ARIZONA_OUT2_MONO_WIDTH 1 /* OUT2_MONO */ +#define ARIZONA_OUT2L_ANC_SRC_MASK 0x0C00 /* OUT2L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT2L_ANC_SRC_SHIFT 10 /* OUT2L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT2L_ANC_SRC_WIDTH 2 /* OUT2L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT2L_PGA_VOL_MASK 0x00FE /* OUT2L_PGA_VOL - [7:1] */ +#define ARIZONA_OUT2L_PGA_VOL_SHIFT 1 /* OUT2L_PGA_VOL - [7:1] */ +#define ARIZONA_OUT2L_PGA_VOL_WIDTH 7 /* OUT2L_PGA_VOL - [7:1] */ + +/* + * R1049 (0x419) - DAC Digital Volume 2L + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT2L_MUTE 0x0100 /* OUT2L_MUTE */ +#define ARIZONA_OUT2L_MUTE_MASK 0x0100 /* OUT2L_MUTE */ +#define ARIZONA_OUT2L_MUTE_SHIFT 8 /* OUT2L_MUTE */ +#define ARIZONA_OUT2L_MUTE_WIDTH 1 /* OUT2L_MUTE */ +#define ARIZONA_OUT2L_VOL_MASK 0x00FF /* OUT2L_VOL - [7:0] */ +#define ARIZONA_OUT2L_VOL_SHIFT 0 /* OUT2L_VOL - [7:0] */ +#define ARIZONA_OUT2L_VOL_WIDTH 8 /* OUT2L_VOL - [7:0] */ + +/* + * R1050 (0x41A) - DAC Volume Limit 2L + */ +#define ARIZONA_OUT2L_VOL_LIM_MASK 0x00FF /* OUT2L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT2L_VOL_LIM_SHIFT 0 /* OUT2L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT2L_VOL_LIM_WIDTH 8 /* OUT2L_VOL_LIM - [7:0] */ + +/* + * R1051 (0x41B) - Noise Gate Select 2L + */ +#define ARIZONA_OUT2L_NGATE_SRC_MASK 0x0FFF /* OUT2L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT2L_NGATE_SRC_SHIFT 0 /* OUT2L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT2L_NGATE_SRC_WIDTH 12 /* OUT2L_NGATE_SRC - [11:0] */ + +/* + * R1052 (0x41C) - Output Path Config 2R + */ +#define ARIZONA_OUT2R_ANC_SRC_MASK 0x0C00 /* OUT2R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT2R_ANC_SRC_SHIFT 10 /* OUT2R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT2R_ANC_SRC_WIDTH 2 /* OUT2R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT2R_PGA_VOL_MASK 0x00FE /* OUT2R_PGA_VOL - [7:1] */ +#define ARIZONA_OUT2R_PGA_VOL_SHIFT 1 /* OUT2R_PGA_VOL - [7:1] */ +#define ARIZONA_OUT2R_PGA_VOL_WIDTH 7 /* OUT2R_PGA_VOL - [7:1] */ + +/* + * R1053 (0x41D) - DAC Digital Volume 2R + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT2R_MUTE 0x0100 /* OUT2R_MUTE */ +#define ARIZONA_OUT2R_MUTE_MASK 0x0100 /* OUT2R_MUTE */ +#define ARIZONA_OUT2R_MUTE_SHIFT 8 /* OUT2R_MUTE */ +#define ARIZONA_OUT2R_MUTE_WIDTH 1 /* OUT2R_MUTE */ +#define ARIZONA_OUT2R_VOL_MASK 0x00FF /* OUT2R_VOL - [7:0] */ +#define ARIZONA_OUT2R_VOL_SHIFT 0 /* OUT2R_VOL - [7:0] */ +#define ARIZONA_OUT2R_VOL_WIDTH 8 /* OUT2R_VOL - [7:0] */ + +/* + * R1054 (0x41E) - DAC Volume Limit 2R + */ +#define ARIZONA_OUT2R_VOL_LIM_MASK 0x00FF /* OUT2R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT2R_VOL_LIM_SHIFT 0 /* OUT2R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT2R_VOL_LIM_WIDTH 8 /* OUT2R_VOL_LIM - [7:0] */ + +/* + * R1055 (0x41F) - Noise Gate Select 2R + */ +#define ARIZONA_OUT2R_NGATE_SRC_MASK 0x0FFF /* OUT2R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT2R_NGATE_SRC_SHIFT 0 /* OUT2R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT2R_NGATE_SRC_WIDTH 12 /* OUT2R_NGATE_SRC - [11:0] */ + +/* + * R1056 (0x420) - Output Path Config 3L + */ +#define ARIZONA_OUT3_LP_MODE 0x8000 /* OUT3_LP_MODE */ +#define ARIZONA_OUT3_LP_MODE_MASK 0x8000 /* OUT3_LP_MODE */ +#define ARIZONA_OUT3_LP_MODE_SHIFT 15 /* OUT3_LP_MODE */ +#define ARIZONA_OUT3_LP_MODE_WIDTH 1 /* OUT3_LP_MODE */ +#define ARIZONA_OUT3_OSR 0x2000 /* OUT3_OSR */ +#define ARIZONA_OUT3_OSR_MASK 0x2000 /* OUT3_OSR */ +#define ARIZONA_OUT3_OSR_SHIFT 13 /* OUT3_OSR */ +#define ARIZONA_OUT3_OSR_WIDTH 1 /* OUT3_OSR */ +#define ARIZONA_OUT3_MONO 0x1000 /* OUT3_MONO */ +#define ARIZONA_OUT3_MONO_MASK 0x1000 /* OUT3_MONO */ +#define ARIZONA_OUT3_MONO_SHIFT 12 /* OUT3_MONO */ +#define ARIZONA_OUT3_MONO_WIDTH 1 /* OUT3_MONO */ +#define ARIZONA_OUT3L_ANC_SRC_MASK 0x0C00 /* OUT3L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT3L_ANC_SRC_SHIFT 10 /* OUT3L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT3L_ANC_SRC_WIDTH 2 /* OUT3L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT3L_PGA_VOL_MASK 0x00FE /* OUT3L_PGA_VOL - [7:1] */ +#define ARIZONA_OUT3L_PGA_VOL_SHIFT 1 /* OUT3L_PGA_VOL - [7:1] */ +#define ARIZONA_OUT3L_PGA_VOL_WIDTH 7 /* OUT3L_PGA_VOL - [7:1] */ + +/* + * R1057 (0x421) - DAC Digital Volume 3L + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT3L_MUTE 0x0100 /* OUT3L_MUTE */ +#define ARIZONA_OUT3L_MUTE_MASK 0x0100 /* OUT3L_MUTE */ +#define ARIZONA_OUT3L_MUTE_SHIFT 8 /* OUT3L_MUTE */ +#define ARIZONA_OUT3L_MUTE_WIDTH 1 /* OUT3L_MUTE */ +#define ARIZONA_OUT3L_VOL_MASK 0x00FF /* OUT3L_VOL - [7:0] */ +#define ARIZONA_OUT3L_VOL_SHIFT 0 /* OUT3L_VOL - [7:0] */ +#define ARIZONA_OUT3L_VOL_WIDTH 8 /* OUT3L_VOL - [7:0] */ + +/* + * R1058 (0x422) - DAC Volume Limit 3L + */ +#define ARIZONA_OUT3L_VOL_LIM_MASK 0x00FF /* OUT3L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT3L_VOL_LIM_SHIFT 0 /* OUT3L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT3L_VOL_LIM_WIDTH 8 /* OUT3L_VOL_LIM - [7:0] */ + +/* + * R1059 (0x423) - Noise Gate Select 3L + */ +#define ARIZONA_OUT3_NGATE_SRC_MASK 0x0FFF /* OUT3_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT3_NGATE_SRC_SHIFT 0 /* OUT3_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT3_NGATE_SRC_WIDTH 12 /* OUT3_NGATE_SRC - [11:0] */ + +/* + * R1060 (0x424) - Output Path Config 3R + */ +#define ARIZONA_OUT3R_PGA_VOL_MASK 0x00FE /* OUT3R_PGA_VOL - [7:1] */ +#define ARIZONA_OUT3R_PGA_VOL_SHIFT 1 /* OUT3R_PGA_VOL - [7:1] */ +#define ARIZONA_OUT3R_PGA_VOL_WIDTH 7 /* OUT3R_PGA_VOL - [7:1] */ + +/* + * R1061 (0x425) - DAC Digital Volume 3R + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT3R_MUTE 0x0100 /* OUT3R_MUTE */ +#define ARIZONA_OUT3R_MUTE_MASK 0x0100 /* OUT3R_MUTE */ +#define ARIZONA_OUT3R_MUTE_SHIFT 8 /* OUT3R_MUTE */ +#define ARIZONA_OUT3R_MUTE_WIDTH 1 /* OUT3R_MUTE */ +#define ARIZONA_OUT3R_VOL_MASK 0x00FF /* OUT3R_VOL - [7:0] */ +#define ARIZONA_OUT3R_VOL_SHIFT 0 /* OUT3R_VOL - [7:0] */ +#define ARIZONA_OUT3R_VOL_WIDTH 8 /* OUT3R_VOL - [7:0] */ + +/* + * R1062 (0x426) - DAC Volume Limit 3R + */ +#define ARIZONA_OUT3R_ANC_SRC_MASK 0x0C00 /* OUT3R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT3R_ANC_SRC_SHIFT 10 /* OUT3R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT3R_ANC_SRC_WIDTH 2 /* OUT3R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT3R_VOL_LIM_MASK 0x00FF /* OUT3R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT3R_VOL_LIM_SHIFT 0 /* OUT3R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT3R_VOL_LIM_WIDTH 8 /* OUT3R_VOL_LIM - [7:0] */ + +/* + * R1064 (0x428) - Output Path Config 4L + */ +#define ARIZONA_OUT4_OSR 0x2000 /* OUT4_OSR */ +#define ARIZONA_OUT4_OSR_MASK 0x2000 /* OUT4_OSR */ +#define ARIZONA_OUT4_OSR_SHIFT 13 /* OUT4_OSR */ +#define ARIZONA_OUT4_OSR_WIDTH 1 /* OUT4_OSR */ +#define ARIZONA_OUT4L_ANC_SRC_MASK 0x0C00 /* OUT4L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT4L_ANC_SRC_SHIFT 10 /* OUT4L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT4L_ANC_SRC_WIDTH 2 /* OUT4L_ANC_SRC - [11:10] */ + +/* + * R1065 (0x429) - DAC Digital Volume 4L + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT4L_MUTE 0x0100 /* OUT4L_MUTE */ +#define ARIZONA_OUT4L_MUTE_MASK 0x0100 /* OUT4L_MUTE */ +#define ARIZONA_OUT4L_MUTE_SHIFT 8 /* OUT4L_MUTE */ +#define ARIZONA_OUT4L_MUTE_WIDTH 1 /* OUT4L_MUTE */ +#define ARIZONA_OUT4L_VOL_MASK 0x00FF /* OUT4L_VOL - [7:0] */ +#define ARIZONA_OUT4L_VOL_SHIFT 0 /* OUT4L_VOL - [7:0] */ +#define ARIZONA_OUT4L_VOL_WIDTH 8 /* OUT4L_VOL - [7:0] */ + +/* + * R1066 (0x42A) - Out Volume 4L + */ +#define ARIZONA_OUT4L_VOL_LIM_MASK 0x00FF /* OUT4L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT4L_VOL_LIM_SHIFT 0 /* OUT4L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT4L_VOL_LIM_WIDTH 8 /* OUT4L_VOL_LIM - [7:0] */ + +/* + * R1067 (0x42B) - Noise Gate Select 4L + */ +#define ARIZONA_OUT4L_NGATE_SRC_MASK 0x0FFF /* OUT4L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT4L_NGATE_SRC_SHIFT 0 /* OUT4L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT4L_NGATE_SRC_WIDTH 12 /* OUT4L_NGATE_SRC - [11:0] */ + +/* + * R1068 (0x42C) - Output Path Config 4R + */ +#define ARIZONA_OUT4R_ANC_SRC_MASK 0x0C00 /* OUT4R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT4R_ANC_SRC_SHIFT 10 /* OUT4R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT4R_ANC_SRC_WIDTH 2 /* OUT4R_ANC_SRC - [11:10] */ + +/* + * R1069 (0x42D) - DAC Digital Volume 4R + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT4R_MUTE 0x0100 /* OUT4R_MUTE */ +#define ARIZONA_OUT4R_MUTE_MASK 0x0100 /* OUT4R_MUTE */ +#define ARIZONA_OUT4R_MUTE_SHIFT 8 /* OUT4R_MUTE */ +#define ARIZONA_OUT4R_MUTE_WIDTH 1 /* OUT4R_MUTE */ +#define ARIZONA_OUT4R_VOL_MASK 0x00FF /* OUT4R_VOL - [7:0] */ +#define ARIZONA_OUT4R_VOL_SHIFT 0 /* OUT4R_VOL - [7:0] */ +#define ARIZONA_OUT4R_VOL_WIDTH 8 /* OUT4R_VOL - [7:0] */ + +/* + * R1070 (0x42E) - Out Volume 4R + */ +#define ARIZONA_OUT4R_VOL_LIM_MASK 0x00FF /* OUT4R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT4R_VOL_LIM_SHIFT 0 /* OUT4R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT4R_VOL_LIM_WIDTH 8 /* OUT4R_VOL_LIM - [7:0] */ + +/* + * R1071 (0x42F) - Noise Gate Select 4R + */ +#define ARIZONA_OUT4R_NGATE_SRC_MASK 0x0FFF /* OUT4R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT4R_NGATE_SRC_SHIFT 0 /* OUT4R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT4R_NGATE_SRC_WIDTH 12 /* OUT4R_NGATE_SRC - [11:0] */ + +/* + * R1072 (0x430) - Output Path Config 5L + */ +#define ARIZONA_OUT5_OSR 0x2000 /* OUT5_OSR */ +#define ARIZONA_OUT5_OSR_MASK 0x2000 /* OUT5_OSR */ +#define ARIZONA_OUT5_OSR_SHIFT 13 /* OUT5_OSR */ +#define ARIZONA_OUT5_OSR_WIDTH 1 /* OUT5_OSR */ +#define ARIZONA_OUT5L_ANC_SRC_MASK 0x0C00 /* OUT5L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT5L_ANC_SRC_SHIFT 10 /* OUT5L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT5L_ANC_SRC_WIDTH 2 /* OUT5L_ANC_SRC - [11:10] */ + +/* + * R1073 (0x431) - DAC Digital Volume 5L + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT5L_MUTE 0x0100 /* OUT5L_MUTE */ +#define ARIZONA_OUT5L_MUTE_MASK 0x0100 /* OUT5L_MUTE */ +#define ARIZONA_OUT5L_MUTE_SHIFT 8 /* OUT5L_MUTE */ +#define ARIZONA_OUT5L_MUTE_WIDTH 1 /* OUT5L_MUTE */ +#define ARIZONA_OUT5L_VOL_MASK 0x00FF /* OUT5L_VOL - [7:0] */ +#define ARIZONA_OUT5L_VOL_SHIFT 0 /* OUT5L_VOL - [7:0] */ +#define ARIZONA_OUT5L_VOL_WIDTH 8 /* OUT5L_VOL - [7:0] */ + +/* + * R1074 (0x432) - DAC Volume Limit 5L + */ +#define ARIZONA_OUT5L_VOL_LIM_MASK 0x00FF /* OUT5L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT5L_VOL_LIM_SHIFT 0 /* OUT5L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT5L_VOL_LIM_WIDTH 8 /* OUT5L_VOL_LIM - [7:0] */ + +/* + * R1075 (0x433) - Noise Gate Select 5L + */ +#define ARIZONA_OUT5L_NGATE_SRC_MASK 0x0FFF /* OUT5L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT5L_NGATE_SRC_SHIFT 0 /* OUT5L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT5L_NGATE_SRC_WIDTH 12 /* OUT5L_NGATE_SRC - [11:0] */ + +/* + * R1076 (0x434) - Output Path Config 5R + */ +#define ARIZONA_OUT5R_ANC_SRC_MASK 0x0C00 /* OUT5R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT5R_ANC_SRC_SHIFT 10 /* OUT5R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT5R_ANC_SRC_WIDTH 2 /* OUT5R_ANC_SRC - [11:10] */ + +/* + * R1077 (0x435) - DAC Digital Volume 5R + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT5R_MUTE 0x0100 /* OUT5R_MUTE */ +#define ARIZONA_OUT5R_MUTE_MASK 0x0100 /* OUT5R_MUTE */ +#define ARIZONA_OUT5R_MUTE_SHIFT 8 /* OUT5R_MUTE */ +#define ARIZONA_OUT5R_MUTE_WIDTH 1 /* OUT5R_MUTE */ +#define ARIZONA_OUT5R_VOL_MASK 0x00FF /* OUT5R_VOL - [7:0] */ +#define ARIZONA_OUT5R_VOL_SHIFT 0 /* OUT5R_VOL - [7:0] */ +#define ARIZONA_OUT5R_VOL_WIDTH 8 /* OUT5R_VOL - [7:0] */ + +/* + * R1078 (0x436) - DAC Volume Limit 5R + */ +#define ARIZONA_OUT5R_VOL_LIM_MASK 0x00FF /* OUT5R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT5R_VOL_LIM_SHIFT 0 /* OUT5R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT5R_VOL_LIM_WIDTH 8 /* OUT5R_VOL_LIM - [7:0] */ + +/* + * R1079 (0x437) - Noise Gate Select 5R + */ +#define ARIZONA_OUT5R_NGATE_SRC_MASK 0x0FFF /* OUT5R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT5R_NGATE_SRC_SHIFT 0 /* OUT5R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT5R_NGATE_SRC_WIDTH 12 /* OUT5R_NGATE_SRC - [11:0] */ + +/* + * R1104 (0x450) - DAC AEC Control 1 + */ +#define ARIZONA_AEC_LOOPBACK_SRC_MASK 0x003C /* AEC_LOOPBACK_SRC - [5:2] */ +#define ARIZONA_AEC_LOOPBACK_SRC_SHIFT 2 /* AEC_LOOPBACK_SRC - [5:2] */ +#define ARIZONA_AEC_LOOPBACK_SRC_WIDTH 4 /* AEC_LOOPBACK_SRC - [5:2] */ +#define ARIZONA_AEC_ENA_STS 0x0002 /* AEC_ENA_STS */ +#define ARIZONA_AEC_ENA_STS_MASK 0x0002 /* AEC_ENA_STS */ +#define ARIZONA_AEC_ENA_STS_SHIFT 1 /* AEC_ENA_STS */ +#define ARIZONA_AEC_ENA_STS_WIDTH 1 /* AEC_ENA_STS */ +#define ARIZONA_AEC_LOOPBACK_ENA 0x0001 /* AEC_LOOPBACK_ENA */ +#define ARIZONA_AEC_LOOPBACK_ENA_MASK 0x0001 /* AEC_LOOPBACK_ENA */ +#define ARIZONA_AEC_LOOPBACK_ENA_SHIFT 0 /* AEC_LOOPBACK_ENA */ +#define ARIZONA_AEC_LOOPBACK_ENA_WIDTH 1 /* AEC_LOOPBACK_ENA */ + +/* + * R1112 (0x458) - Noise Gate Control + */ +#define ARIZONA_NGATE_HOLD_MASK 0x0030 /* NGATE_HOLD - [5:4] */ +#define ARIZONA_NGATE_HOLD_SHIFT 4 /* NGATE_HOLD - [5:4] */ +#define ARIZONA_NGATE_HOLD_WIDTH 2 /* NGATE_HOLD - [5:4] */ +#define ARIZONA_NGATE_THR_MASK 0x000E /* NGATE_THR - [3:1] */ +#define ARIZONA_NGATE_THR_SHIFT 1 /* NGATE_THR - [3:1] */ +#define ARIZONA_NGATE_THR_WIDTH 3 /* NGATE_THR - [3:1] */ +#define ARIZONA_NGATE_ENA 0x0001 /* NGATE_ENA */ +#define ARIZONA_NGATE_ENA_MASK 0x0001 /* NGATE_ENA */ +#define ARIZONA_NGATE_ENA_SHIFT 0 /* NGATE_ENA */ +#define ARIZONA_NGATE_ENA_WIDTH 1 /* NGATE_ENA */ + +/* + * R1168 (0x490) - PDM SPK1 CTRL 1 + */ +#define ARIZONA_SPK1R_MUTE 0x2000 /* SPK1R_MUTE */ +#define ARIZONA_SPK1R_MUTE_MASK 0x2000 /* SPK1R_MUTE */ +#define ARIZONA_SPK1R_MUTE_SHIFT 13 /* SPK1R_MUTE */ +#define ARIZONA_SPK1R_MUTE_WIDTH 1 /* SPK1R_MUTE */ +#define ARIZONA_SPK1L_MUTE 0x1000 /* SPK1L_MUTE */ +#define ARIZONA_SPK1L_MUTE_MASK 0x1000 /* SPK1L_MUTE */ +#define ARIZONA_SPK1L_MUTE_SHIFT 12 /* SPK1L_MUTE */ +#define ARIZONA_SPK1L_MUTE_WIDTH 1 /* SPK1L_MUTE */ +#define ARIZONA_SPK1_MUTE_ENDIAN 0x0100 /* SPK1_MUTE_ENDIAN */ +#define ARIZONA_SPK1_MUTE_ENDIAN_MASK 0x0100 /* SPK1_MUTE_ENDIAN */ +#define ARIZONA_SPK1_MUTE_ENDIAN_SHIFT 8 /* SPK1_MUTE_ENDIAN */ +#define ARIZONA_SPK1_MUTE_ENDIAN_WIDTH 1 /* SPK1_MUTE_ENDIAN */ +#define ARIZONA_SPK1_MUTE_SEQ1_MASK 0x00FF /* SPK1_MUTE_SEQ1 - [7:0] */ +#define ARIZONA_SPK1_MUTE_SEQ1_SHIFT 0 /* SPK1_MUTE_SEQ1 - [7:0] */ +#define ARIZONA_SPK1_MUTE_SEQ1_WIDTH 8 /* SPK1_MUTE_SEQ1 - [7:0] */ + +/* + * R1169 (0x491) - PDM SPK1 CTRL 2 + */ +#define ARIZONA_SPK1_FMT 0x0001 /* SPK1_FMT */ +#define ARIZONA_SPK1_FMT_MASK 0x0001 /* SPK1_FMT */ +#define ARIZONA_SPK1_FMT_SHIFT 0 /* SPK1_FMT */ +#define ARIZONA_SPK1_FMT_WIDTH 1 /* SPK1_FMT */ + +/* + * R1244 (0x4DC) - DAC comp 1 + */ +#define ARIZONA_OUT_COMP_COEFF_MASK 0xFFFF /* OUT_COMP_COEFF - [15:0] */ +#define ARIZONA_OUT_COMP_COEFF_SHIFT 0 /* OUT_COMP_COEFF - [15:0] */ +#define ARIZONA_OUT_COMP_COEFF_WIDTH 16 /* OUT_COMP_COEFF - [15:0] */ + +/* + * R1245 (0x4DD) - DAC comp 2 + */ +#define ARIZONA_OUT_COMP_COEFF_1 0x0002 /* OUT_COMP_COEFF */ +#define ARIZONA_OUT_COMP_COEFF_1_MASK 0x0002 /* OUT_COMP_COEFF */ +#define ARIZONA_OUT_COMP_COEFF_1_SHIFT 1 /* OUT_COMP_COEFF */ +#define ARIZONA_OUT_COMP_COEFF_1_WIDTH 1 /* OUT_COMP_COEFF */ +#define ARIZONA_OUT_COMP_COEFF_SEL 0x0001 /* OUT_COMP_COEFF_SEL */ +#define ARIZONA_OUT_COMP_COEFF_SEL_MASK 0x0001 /* OUT_COMP_COEFF_SEL */ +#define ARIZONA_OUT_COMP_COEFF_SEL_SHIFT 0 /* OUT_COMP_COEFF_SEL */ +#define ARIZONA_OUT_COMP_COEFF_SEL_WIDTH 1 /* OUT_COMP_COEFF_SEL */ + +/* + * R1246 (0x4DE) - DAC comp 3 + */ +#define ARIZONA_AEC_COMP_COEFF_MASK 0xFFFF /* AEC_COMP_COEFF - [15:0] */ +#define ARIZONA_AEC_COMP_COEFF_SHIFT 0 /* AEC_COMP_COEFF - [15:0] */ +#define ARIZONA_AEC_COMP_COEFF_WIDTH 16 /* AEC_COMP_COEFF - [15:0] */ + +/* + * R1247 (0x4DF) - DAC comp 4 + */ +#define ARIZONA_AEC_COMP_COEFF_1 0x0002 /* AEC_COMP_COEFF */ +#define ARIZONA_AEC_COMP_COEFF_1_MASK 0x0002 /* AEC_COMP_COEFF */ +#define ARIZONA_AEC_COMP_COEFF_1_SHIFT 1 /* AEC_COMP_COEFF */ +#define ARIZONA_AEC_COMP_COEFF_1_WIDTH 1 /* AEC_COMP_COEFF */ +#define ARIZONA_AEC_COMP_COEFF_SEL 0x0001 /* AEC_COMP_COEFF_SEL */ +#define ARIZONA_AEC_COMP_COEFF_SEL_MASK 0x0001 /* AEC_COMP_COEFF_SEL */ +#define ARIZONA_AEC_COMP_COEFF_SEL_SHIFT 0 /* AEC_COMP_COEFF_SEL */ +#define ARIZONA_AEC_COMP_COEFF_SEL_WIDTH 1 /* AEC_COMP_COEFF_SEL */ + +/* + * R1280 (0x500) - AIF1 BCLK Ctrl + */ +#define ARIZONA_AIF1_BCLK_INV 0x0080 /* AIF1_BCLK_INV */ +#define ARIZONA_AIF1_BCLK_INV_MASK 0x0080 /* AIF1_BCLK_INV */ +#define ARIZONA_AIF1_BCLK_INV_SHIFT 7 /* AIF1_BCLK_INV */ +#define ARIZONA_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */ +#define ARIZONA_AIF1_BCLK_FRC 0x0040 /* AIF1_BCLK_FRC */ +#define ARIZONA_AIF1_BCLK_FRC_MASK 0x0040 /* AIF1_BCLK_FRC */ +#define ARIZONA_AIF1_BCLK_FRC_SHIFT 6 /* AIF1_BCLK_FRC */ +#define ARIZONA_AIF1_BCLK_FRC_WIDTH 1 /* AIF1_BCLK_FRC */ +#define ARIZONA_AIF1_BCLK_MSTR 0x0020 /* AIF1_BCLK_MSTR */ +#define ARIZONA_AIF1_BCLK_MSTR_MASK 0x0020 /* AIF1_BCLK_MSTR */ +#define ARIZONA_AIF1_BCLK_MSTR_SHIFT 5 /* AIF1_BCLK_MSTR */ +#define ARIZONA_AIF1_BCLK_MSTR_WIDTH 1 /* AIF1_BCLK_MSTR */ +#define ARIZONA_AIF1_BCLK_FREQ_MASK 0x001F /* AIF1_BCLK_FREQ - [4:0] */ +#define ARIZONA_AIF1_BCLK_FREQ_SHIFT 0 /* AIF1_BCLK_FREQ - [4:0] */ +#define ARIZONA_AIF1_BCLK_FREQ_WIDTH 5 /* AIF1_BCLK_FREQ - [4:0] */ + +/* + * R1281 (0x501) - AIF1 Tx Pin Ctrl + */ +#define ARIZONA_AIF1TX_DAT_TRI 0x0020 /* AIF1TX_DAT_TRI */ +#define ARIZONA_AIF1TX_DAT_TRI_MASK 0x0020 /* AIF1TX_DAT_TRI */ +#define ARIZONA_AIF1TX_DAT_TRI_SHIFT 5 /* AIF1TX_DAT_TRI */ +#define ARIZONA_AIF1TX_DAT_TRI_WIDTH 1 /* AIF1TX_DAT_TRI */ +#define ARIZONA_AIF1TX_LRCLK_SRC 0x0008 /* AIF1TX_LRCLK_SRC */ +#define ARIZONA_AIF1TX_LRCLK_SRC_MASK 0x0008 /* AIF1TX_LRCLK_SRC */ +#define ARIZONA_AIF1TX_LRCLK_SRC_SHIFT 3 /* AIF1TX_LRCLK_SRC */ +#define ARIZONA_AIF1TX_LRCLK_SRC_WIDTH 1 /* AIF1TX_LRCLK_SRC */ +#define ARIZONA_AIF1TX_LRCLK_INV 0x0004 /* AIF1TX_LRCLK_INV */ +#define ARIZONA_AIF1TX_LRCLK_INV_MASK 0x0004 /* AIF1TX_LRCLK_INV */ +#define ARIZONA_AIF1TX_LRCLK_INV_SHIFT 2 /* AIF1TX_LRCLK_INV */ +#define ARIZONA_AIF1TX_LRCLK_INV_WIDTH 1 /* AIF1TX_LRCLK_INV */ +#define ARIZONA_AIF1TX_LRCLK_FRC 0x0002 /* AIF1TX_LRCLK_FRC */ +#define ARIZONA_AIF1TX_LRCLK_FRC_MASK 0x0002 /* AIF1TX_LRCLK_FRC */ +#define ARIZONA_AIF1TX_LRCLK_FRC_SHIFT 1 /* AIF1TX_LRCLK_FRC */ +#define ARIZONA_AIF1TX_LRCLK_FRC_WIDTH 1 /* AIF1TX_LRCLK_FRC */ +#define ARIZONA_AIF1TX_LRCLK_MSTR 0x0001 /* AIF1TX_LRCLK_MSTR */ +#define ARIZONA_AIF1TX_LRCLK_MSTR_MASK 0x0001 /* AIF1TX_LRCLK_MSTR */ +#define ARIZONA_AIF1TX_LRCLK_MSTR_SHIFT 0 /* AIF1TX_LRCLK_MSTR */ +#define ARIZONA_AIF1TX_LRCLK_MSTR_WIDTH 1 /* AIF1TX_LRCLK_MSTR */ + +/* + * R1282 (0x502) - AIF1 Rx Pin Ctrl + */ +#define ARIZONA_AIF1RX_LRCLK_INV 0x0004 /* AIF1RX_LRCLK_INV */ +#define ARIZONA_AIF1RX_LRCLK_INV_MASK 0x0004 /* AIF1RX_LRCLK_INV */ +#define ARIZONA_AIF1RX_LRCLK_INV_SHIFT 2 /* AIF1RX_LRCLK_INV */ +#define ARIZONA_AIF1RX_LRCLK_INV_WIDTH 1 /* AIF1RX_LRCLK_INV */ +#define ARIZONA_AIF1RX_LRCLK_FRC 0x0002 /* AIF1RX_LRCLK_FRC */ +#define ARIZONA_AIF1RX_LRCLK_FRC_MASK 0x0002 /* AIF1RX_LRCLK_FRC */ +#define ARIZONA_AIF1RX_LRCLK_FRC_SHIFT 1 /* AIF1RX_LRCLK_FRC */ +#define ARIZONA_AIF1RX_LRCLK_FRC_WIDTH 1 /* AIF1RX_LRCLK_FRC */ +#define ARIZONA_AIF1RX_LRCLK_MSTR 0x0001 /* AIF1RX_LRCLK_MSTR */ +#define ARIZONA_AIF1RX_LRCLK_MSTR_MASK 0x0001 /* AIF1RX_LRCLK_MSTR */ +#define ARIZONA_AIF1RX_LRCLK_MSTR_SHIFT 0 /* AIF1RX_LRCLK_MSTR */ +#define ARIZONA_AIF1RX_LRCLK_MSTR_WIDTH 1 /* AIF1RX_LRCLK_MSTR */ + +/* + * R1283 (0x503) - AIF1 Rate Ctrl + */ +#define ARIZONA_AIF1_RATE_MASK 0x7800 /* AIF1_RATE - [14:11] */ +#define ARIZONA_AIF1_RATE_SHIFT 11 /* AIF1_RATE - [14:11] */ +#define ARIZONA_AIF1_RATE_WIDTH 4 /* AIF1_RATE - [14:11] */ +#define ARIZONA_AIF1_TRI 0x0040 /* AIF1_TRI */ +#define ARIZONA_AIF1_TRI_MASK 0x0040 /* AIF1_TRI */ +#define ARIZONA_AIF1_TRI_SHIFT 6 /* AIF1_TRI */ +#define ARIZONA_AIF1_TRI_WIDTH 1 /* AIF1_TRI */ + +/* + * R1284 (0x504) - AIF1 Format + */ +#define ARIZONA_AIF1_FMT_MASK 0x0007 /* AIF1_FMT - [2:0] */ +#define ARIZONA_AIF1_FMT_SHIFT 0 /* AIF1_FMT - [2:0] */ +#define ARIZONA_AIF1_FMT_WIDTH 3 /* AIF1_FMT - [2:0] */ + +/* + * R1285 (0x505) - AIF1 Tx BCLK Rate + */ +#define ARIZONA_AIF1TX_BCPF_MASK 0x1FFF /* AIF1TX_BCPF - [12:0] */ +#define ARIZONA_AIF1TX_BCPF_SHIFT 0 /* AIF1TX_BCPF - [12:0] */ +#define ARIZONA_AIF1TX_BCPF_WIDTH 13 /* AIF1TX_BCPF - [12:0] */ + +/* + * R1286 (0x506) - AIF1 Rx BCLK Rate + */ +#define ARIZONA_AIF1RX_BCPF_MASK 0x1FFF /* AIF1RX_BCPF - [12:0] */ +#define ARIZONA_AIF1RX_BCPF_SHIFT 0 /* AIF1RX_BCPF - [12:0] */ +#define ARIZONA_AIF1RX_BCPF_WIDTH 13 /* AIF1RX_BCPF - [12:0] */ + +/* + * R1287 (0x507) - AIF1 Frame Ctrl 1 + */ +#define ARIZONA_AIF1TX_WL_MASK 0x3F00 /* AIF1TX_WL - [13:8] */ +#define ARIZONA_AIF1TX_WL_SHIFT 8 /* AIF1TX_WL - [13:8] */ +#define ARIZONA_AIF1TX_WL_WIDTH 6 /* AIF1TX_WL - [13:8] */ +#define ARIZONA_AIF1TX_SLOT_LEN_MASK 0x00FF /* AIF1TX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF1TX_SLOT_LEN_SHIFT 0 /* AIF1TX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF1TX_SLOT_LEN_WIDTH 8 /* AIF1TX_SLOT_LEN - [7:0] */ + +/* + * R1288 (0x508) - AIF1 Frame Ctrl 2 + */ +#define ARIZONA_AIF1RX_WL_MASK 0x3F00 /* AIF1RX_WL - [13:8] */ +#define ARIZONA_AIF1RX_WL_SHIFT 8 /* AIF1RX_WL - [13:8] */ +#define ARIZONA_AIF1RX_WL_WIDTH 6 /* AIF1RX_WL - [13:8] */ +#define ARIZONA_AIF1RX_SLOT_LEN_MASK 0x00FF /* AIF1RX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF1RX_SLOT_LEN_SHIFT 0 /* AIF1RX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF1RX_SLOT_LEN_WIDTH 8 /* AIF1RX_SLOT_LEN - [7:0] */ + +/* + * R1289 (0x509) - AIF1 Frame Ctrl 3 + */ +#define ARIZONA_AIF1TX1_SLOT_MASK 0x003F /* AIF1TX1_SLOT - [5:0] */ +#define ARIZONA_AIF1TX1_SLOT_SHIFT 0 /* AIF1TX1_SLOT - [5:0] */ +#define ARIZONA_AIF1TX1_SLOT_WIDTH 6 /* AIF1TX1_SLOT - [5:0] */ + +/* + * R1290 (0x50A) - AIF1 Frame Ctrl 4 + */ +#define ARIZONA_AIF1TX2_SLOT_MASK 0x003F /* AIF1TX2_SLOT - [5:0] */ +#define ARIZONA_AIF1TX2_SLOT_SHIFT 0 /* AIF1TX2_SLOT - [5:0] */ +#define ARIZONA_AIF1TX2_SLOT_WIDTH 6 /* AIF1TX2_SLOT - [5:0] */ + +/* + * R1291 (0x50B) - AIF1 Frame Ctrl 5 + */ +#define ARIZONA_AIF1TX3_SLOT_MASK 0x003F /* AIF1TX3_SLOT - [5:0] */ +#define ARIZONA_AIF1TX3_SLOT_SHIFT 0 /* AIF1TX3_SLOT - [5:0] */ +#define ARIZONA_AIF1TX3_SLOT_WIDTH 6 /* AIF1TX3_SLOT - [5:0] */ + +/* + * R1292 (0x50C) - AIF1 Frame Ctrl 6 + */ +#define ARIZONA_AIF1TX4_SLOT_MASK 0x003F /* AIF1TX4_SLOT - [5:0] */ +#define ARIZONA_AIF1TX4_SLOT_SHIFT 0 /* AIF1TX4_SLOT - [5:0] */ +#define ARIZONA_AIF1TX4_SLOT_WIDTH 6 /* AIF1TX4_SLOT - [5:0] */ + +/* + * R1293 (0x50D) - AIF1 Frame Ctrl 7 + */ +#define ARIZONA_AIF1TX5_SLOT_MASK 0x003F /* AIF1TX5_SLOT - [5:0] */ +#define ARIZONA_AIF1TX5_SLOT_SHIFT 0 /* AIF1TX5_SLOT - [5:0] */ +#define ARIZONA_AIF1TX5_SLOT_WIDTH 6 /* AIF1TX5_SLOT - [5:0] */ + +/* + * R1294 (0x50E) - AIF1 Frame Ctrl 8 + */ +#define ARIZONA_AIF1TX6_SLOT_MASK 0x003F /* AIF1TX6_SLOT - [5:0] */ +#define ARIZONA_AIF1TX6_SLOT_SHIFT 0 /* AIF1TX6_SLOT - [5:0] */ +#define ARIZONA_AIF1TX6_SLOT_WIDTH 6 /* AIF1TX6_SLOT - [5:0] */ + +/* + * R1295 (0x50F) - AIF1 Frame Ctrl 9 + */ +#define ARIZONA_AIF1TX7_SLOT_MASK 0x003F /* AIF1TX7_SLOT - [5:0] */ +#define ARIZONA_AIF1TX7_SLOT_SHIFT 0 /* AIF1TX7_SLOT - [5:0] */ +#define ARIZONA_AIF1TX7_SLOT_WIDTH 6 /* AIF1TX7_SLOT - [5:0] */ + +/* + * R1296 (0x510) - AIF1 Frame Ctrl 10 + */ +#define ARIZONA_AIF1TX8_SLOT_MASK 0x003F /* AIF1TX8_SLOT - [5:0] */ +#define ARIZONA_AIF1TX8_SLOT_SHIFT 0 /* AIF1TX8_SLOT - [5:0] */ +#define ARIZONA_AIF1TX8_SLOT_WIDTH 6 /* AIF1TX8_SLOT - [5:0] */ + +/* + * R1297 (0x511) - AIF1 Frame Ctrl 11 + */ +#define ARIZONA_AIF1RX1_SLOT_MASK 0x003F /* AIF1RX1_SLOT - [5:0] */ +#define ARIZONA_AIF1RX1_SLOT_SHIFT 0 /* AIF1RX1_SLOT - [5:0] */ +#define ARIZONA_AIF1RX1_SLOT_WIDTH 6 /* AIF1RX1_SLOT - [5:0] */ + +/* + * R1298 (0x512) - AIF1 Frame Ctrl 12 + */ +#define ARIZONA_AIF1RX2_SLOT_MASK 0x003F /* AIF1RX2_SLOT - [5:0] */ +#define ARIZONA_AIF1RX2_SLOT_SHIFT 0 /* AIF1RX2_SLOT - [5:0] */ +#define ARIZONA_AIF1RX2_SLOT_WIDTH 6 /* AIF1RX2_SLOT - [5:0] */ + +/* + * R1299 (0x513) - AIF1 Frame Ctrl 13 + */ +#define ARIZONA_AIF1RX3_SLOT_MASK 0x003F /* AIF1RX3_SLOT - [5:0] */ +#define ARIZONA_AIF1RX3_SLOT_SHIFT 0 /* AIF1RX3_SLOT - [5:0] */ +#define ARIZONA_AIF1RX3_SLOT_WIDTH 6 /* AIF1RX3_SLOT - [5:0] */ + +/* + * R1300 (0x514) - AIF1 Frame Ctrl 14 + */ +#define ARIZONA_AIF1RX4_SLOT_MASK 0x003F /* AIF1RX4_SLOT - [5:0] */ +#define ARIZONA_AIF1RX4_SLOT_SHIFT 0 /* AIF1RX4_SLOT - [5:0] */ +#define ARIZONA_AIF1RX4_SLOT_WIDTH 6 /* AIF1RX4_SLOT - [5:0] */ + +/* + * R1301 (0x515) - AIF1 Frame Ctrl 15 + */ +#define ARIZONA_AIF1RX5_SLOT_MASK 0x003F /* AIF1RX5_SLOT - [5:0] */ +#define ARIZONA_AIF1RX5_SLOT_SHIFT 0 /* AIF1RX5_SLOT - [5:0] */ +#define ARIZONA_AIF1RX5_SLOT_WIDTH 6 /* AIF1RX5_SLOT - [5:0] */ + +/* + * R1302 (0x516) - AIF1 Frame Ctrl 16 + */ +#define ARIZONA_AIF1RX6_SLOT_MASK 0x003F /* AIF1RX6_SLOT - [5:0] */ +#define ARIZONA_AIF1RX6_SLOT_SHIFT 0 /* AIF1RX6_SLOT - [5:0] */ +#define ARIZONA_AIF1RX6_SLOT_WIDTH 6 /* AIF1RX6_SLOT - [5:0] */ + +/* + * R1303 (0x517) - AIF1 Frame Ctrl 17 + */ +#define ARIZONA_AIF1RX7_SLOT_MASK 0x003F /* AIF1RX7_SLOT - [5:0] */ +#define ARIZONA_AIF1RX7_SLOT_SHIFT 0 /* AIF1RX7_SLOT - [5:0] */ +#define ARIZONA_AIF1RX7_SLOT_WIDTH 6 /* AIF1RX7_SLOT - [5:0] */ + +/* + * R1304 (0x518) - AIF1 Frame Ctrl 18 + */ +#define ARIZONA_AIF1RX8_SLOT_MASK 0x003F /* AIF1RX8_SLOT - [5:0] */ +#define ARIZONA_AIF1RX8_SLOT_SHIFT 0 /* AIF1RX8_SLOT - [5:0] */ +#define ARIZONA_AIF1RX8_SLOT_WIDTH 6 /* AIF1RX8_SLOT - [5:0] */ + +/* + * R1305 (0x519) - AIF1 Tx Enables + */ +#define ARIZONA_AIF1TX8_ENA 0x0080 /* AIF1TX8_ENA */ +#define ARIZONA_AIF1TX8_ENA_MASK 0x0080 /* AIF1TX8_ENA */ +#define ARIZONA_AIF1TX8_ENA_SHIFT 7 /* AIF1TX8_ENA */ +#define ARIZONA_AIF1TX8_ENA_WIDTH 1 /* AIF1TX8_ENA */ +#define ARIZONA_AIF1TX7_ENA 0x0040 /* AIF1TX7_ENA */ +#define ARIZONA_AIF1TX7_ENA_MASK 0x0040 /* AIF1TX7_ENA */ +#define ARIZONA_AIF1TX7_ENA_SHIFT 6 /* AIF1TX7_ENA */ +#define ARIZONA_AIF1TX7_ENA_WIDTH 1 /* AIF1TX7_ENA */ +#define ARIZONA_AIF1TX6_ENA 0x0020 /* AIF1TX6_ENA */ +#define ARIZONA_AIF1TX6_ENA_MASK 0x0020 /* AIF1TX6_ENA */ +#define ARIZONA_AIF1TX6_ENA_SHIFT 5 /* AIF1TX6_ENA */ +#define ARIZONA_AIF1TX6_ENA_WIDTH 1 /* AIF1TX6_ENA */ +#define ARIZONA_AIF1TX5_ENA 0x0010 /* AIF1TX5_ENA */ +#define ARIZONA_AIF1TX5_ENA_MASK 0x0010 /* AIF1TX5_ENA */ +#define ARIZONA_AIF1TX5_ENA_SHIFT 4 /* AIF1TX5_ENA */ +#define ARIZONA_AIF1TX5_ENA_WIDTH 1 /* AIF1TX5_ENA */ +#define ARIZONA_AIF1TX4_ENA 0x0008 /* AIF1TX4_ENA */ +#define ARIZONA_AIF1TX4_ENA_MASK 0x0008 /* AIF1TX4_ENA */ +#define ARIZONA_AIF1TX4_ENA_SHIFT 3 /* AIF1TX4_ENA */ +#define ARIZONA_AIF1TX4_ENA_WIDTH 1 /* AIF1TX4_ENA */ +#define ARIZONA_AIF1TX3_ENA 0x0004 /* AIF1TX3_ENA */ +#define ARIZONA_AIF1TX3_ENA_MASK 0x0004 /* AIF1TX3_ENA */ +#define ARIZONA_AIF1TX3_ENA_SHIFT 2 /* AIF1TX3_ENA */ +#define ARIZONA_AIF1TX3_ENA_WIDTH 1 /* AIF1TX3_ENA */ +#define ARIZONA_AIF1TX2_ENA 0x0002 /* AIF1TX2_ENA */ +#define ARIZONA_AIF1TX2_ENA_MASK 0x0002 /* AIF1TX2_ENA */ +#define ARIZONA_AIF1TX2_ENA_SHIFT 1 /* AIF1TX2_ENA */ +#define ARIZONA_AIF1TX2_ENA_WIDTH 1 /* AIF1TX2_ENA */ +#define ARIZONA_AIF1TX1_ENA 0x0001 /* AIF1TX1_ENA */ +#define ARIZONA_AIF1TX1_ENA_MASK 0x0001 /* AIF1TX1_ENA */ +#define ARIZONA_AIF1TX1_ENA_SHIFT 0 /* AIF1TX1_ENA */ +#define ARIZONA_AIF1TX1_ENA_WIDTH 1 /* AIF1TX1_ENA */ + +/* + * R1306 (0x51A) - AIF1 Rx Enables + */ +#define ARIZONA_AIF1RX8_ENA 0x0080 /* AIF1RX8_ENA */ +#define ARIZONA_AIF1RX8_ENA_MASK 0x0080 /* AIF1RX8_ENA */ +#define ARIZONA_AIF1RX8_ENA_SHIFT 7 /* AIF1RX8_ENA */ +#define ARIZONA_AIF1RX8_ENA_WIDTH 1 /* AIF1RX8_ENA */ +#define ARIZONA_AIF1RX7_ENA 0x0040 /* AIF1RX7_ENA */ +#define ARIZONA_AIF1RX7_ENA_MASK 0x0040 /* AIF1RX7_ENA */ +#define ARIZONA_AIF1RX7_ENA_SHIFT 6 /* AIF1RX7_ENA */ +#define ARIZONA_AIF1RX7_ENA_WIDTH 1 /* AIF1RX7_ENA */ +#define ARIZONA_AIF1RX6_ENA 0x0020 /* AIF1RX6_ENA */ +#define ARIZONA_AIF1RX6_ENA_MASK 0x0020 /* AIF1RX6_ENA */ +#define ARIZONA_AIF1RX6_ENA_SHIFT 5 /* AIF1RX6_ENA */ +#define ARIZONA_AIF1RX6_ENA_WIDTH 1 /* AIF1RX6_ENA */ +#define ARIZONA_AIF1RX5_ENA 0x0010 /* AIF1RX5_ENA */ +#define ARIZONA_AIF1RX5_ENA_MASK 0x0010 /* AIF1RX5_ENA */ +#define ARIZONA_AIF1RX5_ENA_SHIFT 4 /* AIF1RX5_ENA */ +#define ARIZONA_AIF1RX5_ENA_WIDTH 1 /* AIF1RX5_ENA */ +#define ARIZONA_AIF1RX4_ENA 0x0008 /* AIF1RX4_ENA */ +#define ARIZONA_AIF1RX4_ENA_MASK 0x0008 /* AIF1RX4_ENA */ +#define ARIZONA_AIF1RX4_ENA_SHIFT 3 /* AIF1RX4_ENA */ +#define ARIZONA_AIF1RX4_ENA_WIDTH 1 /* AIF1RX4_ENA */ +#define ARIZONA_AIF1RX3_ENA 0x0004 /* AIF1RX3_ENA */ +#define ARIZONA_AIF1RX3_ENA_MASK 0x0004 /* AIF1RX3_ENA */ +#define ARIZONA_AIF1RX3_ENA_SHIFT 2 /* AIF1RX3_ENA */ +#define ARIZONA_AIF1RX3_ENA_WIDTH 1 /* AIF1RX3_ENA */ +#define ARIZONA_AIF1RX2_ENA 0x0002 /* AIF1RX2_ENA */ +#define ARIZONA_AIF1RX2_ENA_MASK 0x0002 /* AIF1RX2_ENA */ +#define ARIZONA_AIF1RX2_ENA_SHIFT 1 /* AIF1RX2_ENA */ +#define ARIZONA_AIF1RX2_ENA_WIDTH 1 /* AIF1RX2_ENA */ +#define ARIZONA_AIF1RX1_ENA 0x0001 /* AIF1RX1_ENA */ +#define ARIZONA_AIF1RX1_ENA_MASK 0x0001 /* AIF1RX1_ENA */ +#define ARIZONA_AIF1RX1_ENA_SHIFT 0 /* AIF1RX1_ENA */ +#define ARIZONA_AIF1RX1_ENA_WIDTH 1 /* AIF1RX1_ENA */ + +/* + * R1307 (0x51B) - AIF1 Force Write + */ +#define ARIZONA_AIF1_FRC_WR 0x0001 /* AIF1_FRC_WR */ +#define ARIZONA_AIF1_FRC_WR_MASK 0x0001 /* AIF1_FRC_WR */ +#define ARIZONA_AIF1_FRC_WR_SHIFT 0 /* AIF1_FRC_WR */ +#define ARIZONA_AIF1_FRC_WR_WIDTH 1 /* AIF1_FRC_WR */ + +/* + * R1344 (0x540) - AIF2 BCLK Ctrl + */ +#define ARIZONA_AIF2_BCLK_INV 0x0080 /* AIF2_BCLK_INV */ +#define ARIZONA_AIF2_BCLK_INV_MASK 0x0080 /* AIF2_BCLK_INV */ +#define ARIZONA_AIF2_BCLK_INV_SHIFT 7 /* AIF2_BCLK_INV */ +#define ARIZONA_AIF2_BCLK_INV_WIDTH 1 /* AIF2_BCLK_INV */ +#define ARIZONA_AIF2_BCLK_FRC 0x0040 /* AIF2_BCLK_FRC */ +#define ARIZONA_AIF2_BCLK_FRC_MASK 0x0040 /* AIF2_BCLK_FRC */ +#define ARIZONA_AIF2_BCLK_FRC_SHIFT 6 /* AIF2_BCLK_FRC */ +#define ARIZONA_AIF2_BCLK_FRC_WIDTH 1 /* AIF2_BCLK_FRC */ +#define ARIZONA_AIF2_BCLK_MSTR 0x0020 /* AIF2_BCLK_MSTR */ +#define ARIZONA_AIF2_BCLK_MSTR_MASK 0x0020 /* AIF2_BCLK_MSTR */ +#define ARIZONA_AIF2_BCLK_MSTR_SHIFT 5 /* AIF2_BCLK_MSTR */ +#define ARIZONA_AIF2_BCLK_MSTR_WIDTH 1 /* AIF2_BCLK_MSTR */ +#define ARIZONA_AIF2_BCLK_FREQ_MASK 0x001F /* AIF2_BCLK_FREQ - [4:0] */ +#define ARIZONA_AIF2_BCLK_FREQ_SHIFT 0 /* AIF2_BCLK_FREQ - [4:0] */ +#define ARIZONA_AIF2_BCLK_FREQ_WIDTH 5 /* AIF2_BCLK_FREQ - [4:0] */ + +/* + * R1345 (0x541) - AIF2 Tx Pin Ctrl + */ +#define ARIZONA_AIF2TX_DAT_TRI 0x0020 /* AIF2TX_DAT_TRI */ +#define ARIZONA_AIF2TX_DAT_TRI_MASK 0x0020 /* AIF2TX_DAT_TRI */ +#define ARIZONA_AIF2TX_DAT_TRI_SHIFT 5 /* AIF2TX_DAT_TRI */ +#define ARIZONA_AIF2TX_DAT_TRI_WIDTH 1 /* AIF2TX_DAT_TRI */ +#define ARIZONA_AIF2TX_LRCLK_SRC 0x0008 /* AIF2TX_LRCLK_SRC */ +#define ARIZONA_AIF2TX_LRCLK_SRC_MASK 0x0008 /* AIF2TX_LRCLK_SRC */ +#define ARIZONA_AIF2TX_LRCLK_SRC_SHIFT 3 /* AIF2TX_LRCLK_SRC */ +#define ARIZONA_AIF2TX_LRCLK_SRC_WIDTH 1 /* AIF2TX_LRCLK_SRC */ +#define ARIZONA_AIF2TX_LRCLK_INV 0x0004 /* AIF2TX_LRCLK_INV */ +#define ARIZONA_AIF2TX_LRCLK_INV_MASK 0x0004 /* AIF2TX_LRCLK_INV */ +#define ARIZONA_AIF2TX_LRCLK_INV_SHIFT 2 /* AIF2TX_LRCLK_INV */ +#define ARIZONA_AIF2TX_LRCLK_INV_WIDTH 1 /* AIF2TX_LRCLK_INV */ +#define ARIZONA_AIF2TX_LRCLK_FRC 0x0002 /* AIF2TX_LRCLK_FRC */ +#define ARIZONA_AIF2TX_LRCLK_FRC_MASK 0x0002 /* AIF2TX_LRCLK_FRC */ +#define ARIZONA_AIF2TX_LRCLK_FRC_SHIFT 1 /* AIF2TX_LRCLK_FRC */ +#define ARIZONA_AIF2TX_LRCLK_FRC_WIDTH 1 /* AIF2TX_LRCLK_FRC */ +#define ARIZONA_AIF2TX_LRCLK_MSTR 0x0001 /* AIF2TX_LRCLK_MSTR */ +#define ARIZONA_AIF2TX_LRCLK_MSTR_MASK 0x0001 /* AIF2TX_LRCLK_MSTR */ +#define ARIZONA_AIF2TX_LRCLK_MSTR_SHIFT 0 /* AIF2TX_LRCLK_MSTR */ +#define ARIZONA_AIF2TX_LRCLK_MSTR_WIDTH 1 /* AIF2TX_LRCLK_MSTR */ + +/* + * R1346 (0x542) - AIF2 Rx Pin Ctrl + */ +#define ARIZONA_AIF2RX_LRCLK_INV 0x0004 /* AIF2RX_LRCLK_INV */ +#define ARIZONA_AIF2RX_LRCLK_INV_MASK 0x0004 /* AIF2RX_LRCLK_INV */ +#define ARIZONA_AIF2RX_LRCLK_INV_SHIFT 2 /* AIF2RX_LRCLK_INV */ +#define ARIZONA_AIF2RX_LRCLK_INV_WIDTH 1 /* AIF2RX_LRCLK_INV */ +#define ARIZONA_AIF2RX_LRCLK_FRC 0x0002 /* AIF2RX_LRCLK_FRC */ +#define ARIZONA_AIF2RX_LRCLK_FRC_MASK 0x0002 /* AIF2RX_LRCLK_FRC */ +#define ARIZONA_AIF2RX_LRCLK_FRC_SHIFT 1 /* AIF2RX_LRCLK_FRC */ +#define ARIZONA_AIF2RX_LRCLK_FRC_WIDTH 1 /* AIF2RX_LRCLK_FRC */ +#define ARIZONA_AIF2RX_LRCLK_MSTR 0x0001 /* AIF2RX_LRCLK_MSTR */ +#define ARIZONA_AIF2RX_LRCLK_MSTR_MASK 0x0001 /* AIF2RX_LRCLK_MSTR */ +#define ARIZONA_AIF2RX_LRCLK_MSTR_SHIFT 0 /* AIF2RX_LRCLK_MSTR */ +#define ARIZONA_AIF2RX_LRCLK_MSTR_WIDTH 1 /* AIF2RX_LRCLK_MSTR */ + +/* + * R1347 (0x543) - AIF2 Rate Ctrl + */ +#define ARIZONA_AIF2_RATE_MASK 0x7800 /* AIF2_RATE - [14:11] */ +#define ARIZONA_AIF2_RATE_SHIFT 11 /* AIF2_RATE - [14:11] */ +#define ARIZONA_AIF2_RATE_WIDTH 4 /* AIF2_RATE - [14:11] */ +#define ARIZONA_AIF2_TRI 0x0040 /* AIF2_TRI */ +#define ARIZONA_AIF2_TRI_MASK 0x0040 /* AIF2_TRI */ +#define ARIZONA_AIF2_TRI_SHIFT 6 /* AIF2_TRI */ +#define ARIZONA_AIF2_TRI_WIDTH 1 /* AIF2_TRI */ + +/* + * R1348 (0x544) - AIF2 Format + */ +#define ARIZONA_AIF2_FMT_MASK 0x0007 /* AIF2_FMT - [2:0] */ +#define ARIZONA_AIF2_FMT_SHIFT 0 /* AIF2_FMT - [2:0] */ +#define ARIZONA_AIF2_FMT_WIDTH 3 /* AIF2_FMT - [2:0] */ + +/* + * R1349 (0x545) - AIF2 Tx BCLK Rate + */ +#define ARIZONA_AIF2TX_BCPF_MASK 0x1FFF /* AIF2TX_BCPF - [12:0] */ +#define ARIZONA_AIF2TX_BCPF_SHIFT 0 /* AIF2TX_BCPF - [12:0] */ +#define ARIZONA_AIF2TX_BCPF_WIDTH 13 /* AIF2TX_BCPF - [12:0] */ + +/* + * R1350 (0x546) - AIF2 Rx BCLK Rate + */ +#define ARIZONA_AIF2RX_BCPF_MASK 0x1FFF /* AIF2RX_BCPF - [12:0] */ +#define ARIZONA_AIF2RX_BCPF_SHIFT 0 /* AIF2RX_BCPF - [12:0] */ +#define ARIZONA_AIF2RX_BCPF_WIDTH 13 /* AIF2RX_BCPF - [12:0] */ + +/* + * R1351 (0x547) - AIF2 Frame Ctrl 1 + */ +#define ARIZONA_AIF2TX_WL_MASK 0x3F00 /* AIF2TX_WL - [13:8] */ +#define ARIZONA_AIF2TX_WL_SHIFT 8 /* AIF2TX_WL - [13:8] */ +#define ARIZONA_AIF2TX_WL_WIDTH 6 /* AIF2TX_WL - [13:8] */ +#define ARIZONA_AIF2TX_SLOT_LEN_MASK 0x00FF /* AIF2TX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF2TX_SLOT_LEN_SHIFT 0 /* AIF2TX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF2TX_SLOT_LEN_WIDTH 8 /* AIF2TX_SLOT_LEN - [7:0] */ + +/* + * R1352 (0x548) - AIF2 Frame Ctrl 2 + */ +#define ARIZONA_AIF2RX_WL_MASK 0x3F00 /* AIF2RX_WL - [13:8] */ +#define ARIZONA_AIF2RX_WL_SHIFT 8 /* AIF2RX_WL - [13:8] */ +#define ARIZONA_AIF2RX_WL_WIDTH 6 /* AIF2RX_WL - [13:8] */ +#define ARIZONA_AIF2RX_SLOT_LEN_MASK 0x00FF /* AIF2RX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF2RX_SLOT_LEN_SHIFT 0 /* AIF2RX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF2RX_SLOT_LEN_WIDTH 8 /* AIF2RX_SLOT_LEN - [7:0] */ + +/* + * R1353 (0x549) - AIF2 Frame Ctrl 3 + */ +#define ARIZONA_AIF2TX1_SLOT_MASK 0x003F /* AIF2TX1_SLOT - [5:0] */ +#define ARIZONA_AIF2TX1_SLOT_SHIFT 0 /* AIF2TX1_SLOT - [5:0] */ +#define ARIZONA_AIF2TX1_SLOT_WIDTH 6 /* AIF2TX1_SLOT - [5:0] */ + +/* + * R1354 (0x54A) - AIF2 Frame Ctrl 4 + */ +#define ARIZONA_AIF2TX2_SLOT_MASK 0x003F /* AIF2TX2_SLOT - [5:0] */ +#define ARIZONA_AIF2TX2_SLOT_SHIFT 0 /* AIF2TX2_SLOT - [5:0] */ +#define ARIZONA_AIF2TX2_SLOT_WIDTH 6 /* AIF2TX2_SLOT - [5:0] */ + +/* + * R1361 (0x551) - AIF2 Frame Ctrl 11 + */ +#define ARIZONA_AIF2RX1_SLOT_MASK 0x003F /* AIF2RX1_SLOT - [5:0] */ +#define ARIZONA_AIF2RX1_SLOT_SHIFT 0 /* AIF2RX1_SLOT - [5:0] */ +#define ARIZONA_AIF2RX1_SLOT_WIDTH 6 /* AIF2RX1_SLOT - [5:0] */ + +/* + * R1362 (0x552) - AIF2 Frame Ctrl 12 + */ +#define ARIZONA_AIF2RX2_SLOT_MASK 0x003F /* AIF2RX2_SLOT - [5:0] */ +#define ARIZONA_AIF2RX2_SLOT_SHIFT 0 /* AIF2RX2_SLOT - [5:0] */ +#define ARIZONA_AIF2RX2_SLOT_WIDTH 6 /* AIF2RX2_SLOT - [5:0] */ + +/* + * R1369 (0x559) - AIF2 Tx Enables + */ +#define ARIZONA_AIF2TX2_ENA 0x0002 /* AIF2TX2_ENA */ +#define ARIZONA_AIF2TX2_ENA_MASK 0x0002 /* AIF2TX2_ENA */ +#define ARIZONA_AIF2TX2_ENA_SHIFT 1 /* AIF2TX2_ENA */ +#define ARIZONA_AIF2TX2_ENA_WIDTH 1 /* AIF2TX2_ENA */ +#define ARIZONA_AIF2TX1_ENA 0x0001 /* AIF2TX1_ENA */ +#define ARIZONA_AIF2TX1_ENA_MASK 0x0001 /* AIF2TX1_ENA */ +#define ARIZONA_AIF2TX1_ENA_SHIFT 0 /* AIF2TX1_ENA */ +#define ARIZONA_AIF2TX1_ENA_WIDTH 1 /* AIF2TX1_ENA */ + +/* + * R1370 (0x55A) - AIF2 Rx Enables + */ +#define ARIZONA_AIF2RX2_ENA 0x0002 /* AIF2RX2_ENA */ +#define ARIZONA_AIF2RX2_ENA_MASK 0x0002 /* AIF2RX2_ENA */ +#define ARIZONA_AIF2RX2_ENA_SHIFT 1 /* AIF2RX2_ENA */ +#define ARIZONA_AIF2RX2_ENA_WIDTH 1 /* AIF2RX2_ENA */ +#define ARIZONA_AIF2RX1_ENA 0x0001 /* AIF2RX1_ENA */ +#define ARIZONA_AIF2RX1_ENA_MASK 0x0001 /* AIF2RX1_ENA */ +#define ARIZONA_AIF2RX1_ENA_SHIFT 0 /* AIF2RX1_ENA */ +#define ARIZONA_AIF2RX1_ENA_WIDTH 1 /* AIF2RX1_ENA */ + +/* + * R1371 (0x55B) - AIF2 Force Write + */ +#define ARIZONA_AIF2_FRC_WR 0x0001 /* AIF2_FRC_WR */ +#define ARIZONA_AIF2_FRC_WR_MASK 0x0001 /* AIF2_FRC_WR */ +#define ARIZONA_AIF2_FRC_WR_SHIFT 0 /* AIF2_FRC_WR */ +#define ARIZONA_AIF2_FRC_WR_WIDTH 1 /* AIF2_FRC_WR */ + +/* + * R1408 (0x580) - AIF3 BCLK Ctrl + */ +#define ARIZONA_AIF3_BCLK_INV 0x0080 /* AIF3_BCLK_INV */ +#define ARIZONA_AIF3_BCLK_INV_MASK 0x0080 /* AIF3_BCLK_INV */ +#define ARIZONA_AIF3_BCLK_INV_SHIFT 7 /* AIF3_BCLK_INV */ +#define ARIZONA_AIF3_BCLK_INV_WIDTH 1 /* AIF3_BCLK_INV */ +#define ARIZONA_AIF3_BCLK_FRC 0x0040 /* AIF3_BCLK_FRC */ +#define ARIZONA_AIF3_BCLK_FRC_MASK 0x0040 /* AIF3_BCLK_FRC */ +#define ARIZONA_AIF3_BCLK_FRC_SHIFT 6 /* AIF3_BCLK_FRC */ +#define ARIZONA_AIF3_BCLK_FRC_WIDTH 1 /* AIF3_BCLK_FRC */ +#define ARIZONA_AIF3_BCLK_MSTR 0x0020 /* AIF3_BCLK_MSTR */ +#define ARIZONA_AIF3_BCLK_MSTR_MASK 0x0020 /* AIF3_BCLK_MSTR */ +#define ARIZONA_AIF3_BCLK_MSTR_SHIFT 5 /* AIF3_BCLK_MSTR */ +#define ARIZONA_AIF3_BCLK_MSTR_WIDTH 1 /* AIF3_BCLK_MSTR */ +#define ARIZONA_AIF3_BCLK_FREQ_MASK 0x001F /* AIF3_BCLK_FREQ - [4:0] */ +#define ARIZONA_AIF3_BCLK_FREQ_SHIFT 0 /* AIF3_BCLK_FREQ - [4:0] */ +#define ARIZONA_AIF3_BCLK_FREQ_WIDTH 5 /* AIF3_BCLK_FREQ - [4:0] */ + +/* + * R1409 (0x581) - AIF3 Tx Pin Ctrl + */ +#define ARIZONA_AIF3TX_DAT_TRI 0x0020 /* AIF3TX_DAT_TRI */ +#define ARIZONA_AIF3TX_DAT_TRI_MASK 0x0020 /* AIF3TX_DAT_TRI */ +#define ARIZONA_AIF3TX_DAT_TRI_SHIFT 5 /* AIF3TX_DAT_TRI */ +#define ARIZONA_AIF3TX_DAT_TRI_WIDTH 1 /* AIF3TX_DAT_TRI */ +#define ARIZONA_AIF3TX_LRCLK_SRC 0x0008 /* AIF3TX_LRCLK_SRC */ +#define ARIZONA_AIF3TX_LRCLK_SRC_MASK 0x0008 /* AIF3TX_LRCLK_SRC */ +#define ARIZONA_AIF3TX_LRCLK_SRC_SHIFT 3 /* AIF3TX_LRCLK_SRC */ +#define ARIZONA_AIF3TX_LRCLK_SRC_WIDTH 1 /* AIF3TX_LRCLK_SRC */ +#define ARIZONA_AIF3TX_LRCLK_INV 0x0004 /* AIF3TX_LRCLK_INV */ +#define ARIZONA_AIF3TX_LRCLK_INV_MASK 0x0004 /* AIF3TX_LRCLK_INV */ +#define ARIZONA_AIF3TX_LRCLK_INV_SHIFT 2 /* AIF3TX_LRCLK_INV */ +#define ARIZONA_AIF3TX_LRCLK_INV_WIDTH 1 /* AIF3TX_LRCLK_INV */ +#define ARIZONA_AIF3TX_LRCLK_FRC 0x0002 /* AIF3TX_LRCLK_FRC */ +#define ARIZONA_AIF3TX_LRCLK_FRC_MASK 0x0002 /* AIF3TX_LRCLK_FRC */ +#define ARIZONA_AIF3TX_LRCLK_FRC_SHIFT 1 /* AIF3TX_LRCLK_FRC */ +#define ARIZONA_AIF3TX_LRCLK_FRC_WIDTH 1 /* AIF3TX_LRCLK_FRC */ +#define ARIZONA_AIF3TX_LRCLK_MSTR 0x0001 /* AIF3TX_LRCLK_MSTR */ +#define ARIZONA_AIF3TX_LRCLK_MSTR_MASK 0x0001 /* AIF3TX_LRCLK_MSTR */ +#define ARIZONA_AIF3TX_LRCLK_MSTR_SHIFT 0 /* AIF3TX_LRCLK_MSTR */ +#define ARIZONA_AIF3TX_LRCLK_MSTR_WIDTH 1 /* AIF3TX_LRCLK_MSTR */ + +/* + * R1410 (0x582) - AIF3 Rx Pin Ctrl + */ +#define ARIZONA_AIF3RX_LRCLK_INV 0x0004 /* AIF3RX_LRCLK_INV */ +#define ARIZONA_AIF3RX_LRCLK_INV_MASK 0x0004 /* AIF3RX_LRCLK_INV */ +#define ARIZONA_AIF3RX_LRCLK_INV_SHIFT 2 /* AIF3RX_LRCLK_INV */ +#define ARIZONA_AIF3RX_LRCLK_INV_WIDTH 1 /* AIF3RX_LRCLK_INV */ +#define ARIZONA_AIF3RX_LRCLK_FRC 0x0002 /* AIF3RX_LRCLK_FRC */ +#define ARIZONA_AIF3RX_LRCLK_FRC_MASK 0x0002 /* AIF3RX_LRCLK_FRC */ +#define ARIZONA_AIF3RX_LRCLK_FRC_SHIFT 1 /* AIF3RX_LRCLK_FRC */ +#define ARIZONA_AIF3RX_LRCLK_FRC_WIDTH 1 /* AIF3RX_LRCLK_FRC */ +#define ARIZONA_AIF3RX_LRCLK_MSTR 0x0001 /* AIF3RX_LRCLK_MSTR */ +#define ARIZONA_AIF3RX_LRCLK_MSTR_MASK 0x0001 /* AIF3RX_LRCLK_MSTR */ +#define ARIZONA_AIF3RX_LRCLK_MSTR_SHIFT 0 /* AIF3RX_LRCLK_MSTR */ +#define ARIZONA_AIF3RX_LRCLK_MSTR_WIDTH 1 /* AIF3RX_LRCLK_MSTR */ + +/* + * R1411 (0x583) - AIF3 Rate Ctrl + */ +#define ARIZONA_AIF3_RATE_MASK 0x7800 /* AIF3_RATE - [14:11] */ +#define ARIZONA_AIF3_RATE_SHIFT 11 /* AIF3_RATE - [14:11] */ +#define ARIZONA_AIF3_RATE_WIDTH 4 /* AIF3_RATE - [14:11] */ +#define ARIZONA_AIF3_TRI 0x0040 /* AIF3_TRI */ +#define ARIZONA_AIF3_TRI_MASK 0x0040 /* AIF3_TRI */ +#define ARIZONA_AIF3_TRI_SHIFT 6 /* AIF3_TRI */ +#define ARIZONA_AIF3_TRI_WIDTH 1 /* AIF3_TRI */ + +/* + * R1412 (0x584) - AIF3 Format + */ +#define ARIZONA_AIF3_FMT_MASK 0x0007 /* AIF3_FMT - [2:0] */ +#define ARIZONA_AIF3_FMT_SHIFT 0 /* AIF3_FMT - [2:0] */ +#define ARIZONA_AIF3_FMT_WIDTH 3 /* AIF3_FMT - [2:0] */ + +/* + * R1413 (0x585) - AIF3 Tx BCLK Rate + */ +#define ARIZONA_AIF3TX_BCPF_MASK 0x1FFF /* AIF3TX_BCPF - [12:0] */ +#define ARIZONA_AIF3TX_BCPF_SHIFT 0 /* AIF3TX_BCPF - [12:0] */ +#define ARIZONA_AIF3TX_BCPF_WIDTH 13 /* AIF3TX_BCPF - [12:0] */ + +/* + * R1414 (0x586) - AIF3 Rx BCLK Rate + */ +#define ARIZONA_AIF3RX_BCPF_MASK 0x1FFF /* AIF3RX_BCPF - [12:0] */ +#define ARIZONA_AIF3RX_BCPF_SHIFT 0 /* AIF3RX_BCPF - [12:0] */ +#define ARIZONA_AIF3RX_BCPF_WIDTH 13 /* AIF3RX_BCPF - [12:0] */ + +/* + * R1415 (0x587) - AIF3 Frame Ctrl 1 + */ +#define ARIZONA_AIF3TX_WL_MASK 0x3F00 /* AIF3TX_WL - [13:8] */ +#define ARIZONA_AIF3TX_WL_SHIFT 8 /* AIF3TX_WL - [13:8] */ +#define ARIZONA_AIF3TX_WL_WIDTH 6 /* AIF3TX_WL - [13:8] */ +#define ARIZONA_AIF3TX_SLOT_LEN_MASK 0x00FF /* AIF3TX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF3TX_SLOT_LEN_SHIFT 0 /* AIF3TX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF3TX_SLOT_LEN_WIDTH 8 /* AIF3TX_SLOT_LEN - [7:0] */ + +/* + * R1416 (0x588) - AIF3 Frame Ctrl 2 + */ +#define ARIZONA_AIF3RX_WL_MASK 0x3F00 /* AIF3RX_WL - [13:8] */ +#define ARIZONA_AIF3RX_WL_SHIFT 8 /* AIF3RX_WL - [13:8] */ +#define ARIZONA_AIF3RX_WL_WIDTH 6 /* AIF3RX_WL - [13:8] */ +#define ARIZONA_AIF3RX_SLOT_LEN_MASK 0x00FF /* AIF3RX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF3RX_SLOT_LEN_SHIFT 0 /* AIF3RX_SLOT_LEN - [7:0] */ +#define ARIZONA_AIF3RX_SLOT_LEN_WIDTH 8 /* AIF3RX_SLOT_LEN - [7:0] */ + +/* + * R1417 (0x589) - AIF3 Frame Ctrl 3 + */ +#define ARIZONA_AIF3TX1_SLOT_MASK 0x003F /* AIF3TX1_SLOT - [5:0] */ +#define ARIZONA_AIF3TX1_SLOT_SHIFT 0 /* AIF3TX1_SLOT - [5:0] */ +#define ARIZONA_AIF3TX1_SLOT_WIDTH 6 /* AIF3TX1_SLOT - [5:0] */ + +/* + * R1418 (0x58A) - AIF3 Frame Ctrl 4 + */ +#define ARIZONA_AIF3TX2_SLOT_MASK 0x003F /* AIF3TX2_SLOT - [5:0] */ +#define ARIZONA_AIF3TX2_SLOT_SHIFT 0 /* AIF3TX2_SLOT - [5:0] */ +#define ARIZONA_AIF3TX2_SLOT_WIDTH 6 /* AIF3TX2_SLOT - [5:0] */ + +/* + * R1425 (0x591) - AIF3 Frame Ctrl 11 + */ +#define ARIZONA_AIF3RX1_SLOT_MASK 0x003F /* AIF3RX1_SLOT - [5:0] */ +#define ARIZONA_AIF3RX1_SLOT_SHIFT 0 /* AIF3RX1_SLOT - [5:0] */ +#define ARIZONA_AIF3RX1_SLOT_WIDTH 6 /* AIF3RX1_SLOT - [5:0] */ + +/* + * R1426 (0x592) - AIF3 Frame Ctrl 12 + */ +#define ARIZONA_AIF3RX2_SLOT_MASK 0x003F /* AIF3RX2_SLOT - [5:0] */ +#define ARIZONA_AIF3RX2_SLOT_SHIFT 0 /* AIF3RX2_SLOT - [5:0] */ +#define ARIZONA_AIF3RX2_SLOT_WIDTH 6 /* AIF3RX2_SLOT - [5:0] */ + +/* + * R1433 (0x599) - AIF3 Tx Enables + */ +#define ARIZONA_AIF3TX2_ENA 0x0002 /* AIF3TX2_ENA */ +#define ARIZONA_AIF3TX2_ENA_MASK 0x0002 /* AIF3TX2_ENA */ +#define ARIZONA_AIF3TX2_ENA_SHIFT 1 /* AIF3TX2_ENA */ +#define ARIZONA_AIF3TX2_ENA_WIDTH 1 /* AIF3TX2_ENA */ +#define ARIZONA_AIF3TX1_ENA 0x0001 /* AIF3TX1_ENA */ +#define ARIZONA_AIF3TX1_ENA_MASK 0x0001 /* AIF3TX1_ENA */ +#define ARIZONA_AIF3TX1_ENA_SHIFT 0 /* AIF3TX1_ENA */ +#define ARIZONA_AIF3TX1_ENA_WIDTH 1 /* AIF3TX1_ENA */ + +/* + * R1434 (0x59A) - AIF3 Rx Enables + */ +#define ARIZONA_AIF3RX2_ENA 0x0002 /* AIF3RX2_ENA */ +#define ARIZONA_AIF3RX2_ENA_MASK 0x0002 /* AIF3RX2_ENA */ +#define ARIZONA_AIF3RX2_ENA_SHIFT 1 /* AIF3RX2_ENA */ +#define ARIZONA_AIF3RX2_ENA_WIDTH 1 /* AIF3RX2_ENA */ +#define ARIZONA_AIF3RX1_ENA 0x0001 /* AIF3RX1_ENA */ +#define ARIZONA_AIF3RX1_ENA_MASK 0x0001 /* AIF3RX1_ENA */ +#define ARIZONA_AIF3RX1_ENA_SHIFT 0 /* AIF3RX1_ENA */ +#define ARIZONA_AIF3RX1_ENA_WIDTH 1 /* AIF3RX1_ENA */ + +/* + * R1435 (0x59B) - AIF3 Force Write + */ +#define ARIZONA_AIF3_FRC_WR 0x0001 /* AIF3_FRC_WR */ +#define ARIZONA_AIF3_FRC_WR_MASK 0x0001 /* AIF3_FRC_WR */ +#define ARIZONA_AIF3_FRC_WR_SHIFT 0 /* AIF3_FRC_WR */ +#define ARIZONA_AIF3_FRC_WR_WIDTH 1 /* AIF3_FRC_WR */ + +/* + * R1507 (0x5E3) - SLIMbus Framer Ref Gear + */ +#define ARIZONA_SLIMCLK_SRC 0x0010 /* SLIMCLK_SRC */ +#define ARIZONA_SLIMCLK_SRC_MASK 0x0010 /* SLIMCLK_SRC */ +#define ARIZONA_SLIMCLK_SRC_SHIFT 4 /* SLIMCLK_SRC */ +#define ARIZONA_SLIMCLK_SRC_WIDTH 1 /* SLIMCLK_SRC */ +#define ARIZONA_FRAMER_REF_GEAR_MASK 0x000F /* FRAMER_REF_GEAR - [3:0] */ +#define ARIZONA_FRAMER_REF_GEAR_SHIFT 0 /* FRAMER_REF_GEAR - [3:0] */ +#define ARIZONA_FRAMER_REF_GEAR_WIDTH 4 /* FRAMER_REF_GEAR - [3:0] */ + +/* + * R1509 (0x5E5) - SLIMbus Rates 1 + */ +#define ARIZONA_SLIMRX2_RATE_MASK 0x7800 /* SLIMRX2_RATE - [14:11] */ +#define ARIZONA_SLIMRX2_RATE_SHIFT 11 /* SLIMRX2_RATE - [14:11] */ +#define ARIZONA_SLIMRX2_RATE_WIDTH 4 /* SLIMRX2_RATE - [14:11] */ +#define ARIZONA_SLIMRX1_RATE_MASK 0x0078 /* SLIMRX1_RATE - [6:3] */ +#define ARIZONA_SLIMRX1_RATE_SHIFT 3 /* SLIMRX1_RATE - [6:3] */ +#define ARIZONA_SLIMRX1_RATE_WIDTH 4 /* SLIMRX1_RATE - [6:3] */ + +/* + * R1510 (0x5E6) - SLIMbus Rates 2 + */ +#define ARIZONA_SLIMRX4_RATE_MASK 0x7800 /* SLIMRX4_RATE - [14:11] */ +#define ARIZONA_SLIMRX4_RATE_SHIFT 11 /* SLIMRX4_RATE - [14:11] */ +#define ARIZONA_SLIMRX4_RATE_WIDTH 4 /* SLIMRX4_RATE - [14:11] */ +#define ARIZONA_SLIMRX3_RATE_MASK 0x0078 /* SLIMRX3_RATE - [6:3] */ +#define ARIZONA_SLIMRX3_RATE_SHIFT 3 /* SLIMRX3_RATE - [6:3] */ +#define ARIZONA_SLIMRX3_RATE_WIDTH 4 /* SLIMRX3_RATE - [6:3] */ + +/* + * R1511 (0x5E7) - SLIMbus Rates 3 + */ +#define ARIZONA_SLIMRX6_RATE_MASK 0x7800 /* SLIMRX6_RATE - [14:11] */ +#define ARIZONA_SLIMRX6_RATE_SHIFT 11 /* SLIMRX6_RATE - [14:11] */ +#define ARIZONA_SLIMRX6_RATE_WIDTH 4 /* SLIMRX6_RATE - [14:11] */ +#define ARIZONA_SLIMRX5_RATE_MASK 0x0078 /* SLIMRX5_RATE - [6:3] */ +#define ARIZONA_SLIMRX5_RATE_SHIFT 3 /* SLIMRX5_RATE - [6:3] */ +#define ARIZONA_SLIMRX5_RATE_WIDTH 4 /* SLIMRX5_RATE - [6:3] */ + +/* + * R1512 (0x5E8) - SLIMbus Rates 4 + */ +#define ARIZONA_SLIMRX8_RATE_MASK 0x7800 /* SLIMRX8_RATE - [14:11] */ +#define ARIZONA_SLIMRX8_RATE_SHIFT 11 /* SLIMRX8_RATE - [14:11] */ +#define ARIZONA_SLIMRX8_RATE_WIDTH 4 /* SLIMRX8_RATE - [14:11] */ +#define ARIZONA_SLIMRX7_RATE_MASK 0x0078 /* SLIMRX7_RATE - [6:3] */ +#define ARIZONA_SLIMRX7_RATE_SHIFT 3 /* SLIMRX7_RATE - [6:3] */ +#define ARIZONA_SLIMRX7_RATE_WIDTH 4 /* SLIMRX7_RATE - [6:3] */ + +/* + * R1513 (0x5E9) - SLIMbus Rates 5 + */ +#define ARIZONA_SLIMTX2_RATE_MASK 0x7800 /* SLIMTX2_RATE - [14:11] */ +#define ARIZONA_SLIMTX2_RATE_SHIFT 11 /* SLIMTX2_RATE - [14:11] */ +#define ARIZONA_SLIMTX2_RATE_WIDTH 4 /* SLIMTX2_RATE - [14:11] */ +#define ARIZONA_SLIMTX1_RATE_MASK 0x0078 /* SLIMTX1_RATE - [6:3] */ +#define ARIZONA_SLIMTX1_RATE_SHIFT 3 /* SLIMTX1_RATE - [6:3] */ +#define ARIZONA_SLIMTX1_RATE_WIDTH 4 /* SLIMTX1_RATE - [6:3] */ + +/* + * R1514 (0x5EA) - SLIMbus Rates 6 + */ +#define ARIZONA_SLIMTX4_RATE_MASK 0x7800 /* SLIMTX4_RATE - [14:11] */ +#define ARIZONA_SLIMTX4_RATE_SHIFT 11 /* SLIMTX4_RATE - [14:11] */ +#define ARIZONA_SLIMTX4_RATE_WIDTH 4 /* SLIMTX4_RATE - [14:11] */ +#define ARIZONA_SLIMTX3_RATE_MASK 0x0078 /* SLIMTX3_RATE - [6:3] */ +#define ARIZONA_SLIMTX3_RATE_SHIFT 3 /* SLIMTX3_RATE - [6:3] */ +#define ARIZONA_SLIMTX3_RATE_WIDTH 4 /* SLIMTX3_RATE - [6:3] */ + +/* + * R1515 (0x5EB) - SLIMbus Rates 7 + */ +#define ARIZONA_SLIMTX6_RATE_MASK 0x7800 /* SLIMTX6_RATE - [14:11] */ +#define ARIZONA_SLIMTX6_RATE_SHIFT 11 /* SLIMTX6_RATE - [14:11] */ +#define ARIZONA_SLIMTX6_RATE_WIDTH 4 /* SLIMTX6_RATE - [14:11] */ +#define ARIZONA_SLIMTX5_RATE_MASK 0x0078 /* SLIMTX5_RATE - [6:3] */ +#define ARIZONA_SLIMTX5_RATE_SHIFT 3 /* SLIMTX5_RATE - [6:3] */ +#define ARIZONA_SLIMTX5_RATE_WIDTH 4 /* SLIMTX5_RATE - [6:3] */ + +/* + * R1516 (0x5EC) - SLIMbus Rates 8 + */ +#define ARIZONA_SLIMTX8_RATE_MASK 0x7800 /* SLIMTX8_RATE - [14:11] */ +#define ARIZONA_SLIMTX8_RATE_SHIFT 11 /* SLIMTX8_RATE - [14:11] */ +#define ARIZONA_SLIMTX8_RATE_WIDTH 4 /* SLIMTX8_RATE - [14:11] */ +#define ARIZONA_SLIMTX7_RATE_MASK 0x0078 /* SLIMTX7_RATE - [6:3] */ +#define ARIZONA_SLIMTX7_RATE_SHIFT 3 /* SLIMTX7_RATE - [6:3] */ +#define ARIZONA_SLIMTX7_RATE_WIDTH 4 /* SLIMTX7_RATE - [6:3] */ + +/* + * R1525 (0x5F5) - SLIMbus RX Channel Enable + */ +#define ARIZONA_SLIMRX8_ENA 0x0080 /* SLIMRX8_ENA */ +#define ARIZONA_SLIMRX8_ENA_MASK 0x0080 /* SLIMRX8_ENA */ +#define ARIZONA_SLIMRX8_ENA_SHIFT 7 /* SLIMRX8_ENA */ +#define ARIZONA_SLIMRX8_ENA_WIDTH 1 /* SLIMRX8_ENA */ +#define ARIZONA_SLIMRX7_ENA 0x0040 /* SLIMRX7_ENA */ +#define ARIZONA_SLIMRX7_ENA_MASK 0x0040 /* SLIMRX7_ENA */ +#define ARIZONA_SLIMRX7_ENA_SHIFT 6 /* SLIMRX7_ENA */ +#define ARIZONA_SLIMRX7_ENA_WIDTH 1 /* SLIMRX7_ENA */ +#define ARIZONA_SLIMRX6_ENA 0x0020 /* SLIMRX6_ENA */ +#define ARIZONA_SLIMRX6_ENA_MASK 0x0020 /* SLIMRX6_ENA */ +#define ARIZONA_SLIMRX6_ENA_SHIFT 5 /* SLIMRX6_ENA */ +#define ARIZONA_SLIMRX6_ENA_WIDTH 1 /* SLIMRX6_ENA */ +#define ARIZONA_SLIMRX5_ENA 0x0010 /* SLIMRX5_ENA */ +#define ARIZONA_SLIMRX5_ENA_MASK 0x0010 /* SLIMRX5_ENA */ +#define ARIZONA_SLIMRX5_ENA_SHIFT 4 /* SLIMRX5_ENA */ +#define ARIZONA_SLIMRX5_ENA_WIDTH 1 /* SLIMRX5_ENA */ +#define ARIZONA_SLIMRX4_ENA 0x0008 /* SLIMRX4_ENA */ +#define ARIZONA_SLIMRX4_ENA_MASK 0x0008 /* SLIMRX4_ENA */ +#define ARIZONA_SLIMRX4_ENA_SHIFT 3 /* SLIMRX4_ENA */ +#define ARIZONA_SLIMRX4_ENA_WIDTH 1 /* SLIMRX4_ENA */ +#define ARIZONA_SLIMRX3_ENA 0x0004 /* SLIMRX3_ENA */ +#define ARIZONA_SLIMRX3_ENA_MASK 0x0004 /* SLIMRX3_ENA */ +#define ARIZONA_SLIMRX3_ENA_SHIFT 2 /* SLIMRX3_ENA */ +#define ARIZONA_SLIMRX3_ENA_WIDTH 1 /* SLIMRX3_ENA */ +#define ARIZONA_SLIMRX2_ENA 0x0002 /* SLIMRX2_ENA */ +#define ARIZONA_SLIMRX2_ENA_MASK 0x0002 /* SLIMRX2_ENA */ +#define ARIZONA_SLIMRX2_ENA_SHIFT 1 /* SLIMRX2_ENA */ +#define ARIZONA_SLIMRX2_ENA_WIDTH 1 /* SLIMRX2_ENA */ +#define ARIZONA_SLIMRX1_ENA 0x0001 /* SLIMRX1_ENA */ +#define ARIZONA_SLIMRX1_ENA_MASK 0x0001 /* SLIMRX1_ENA */ +#define ARIZONA_SLIMRX1_ENA_SHIFT 0 /* SLIMRX1_ENA */ +#define ARIZONA_SLIMRX1_ENA_WIDTH 1 /* SLIMRX1_ENA */ + +/* + * R1526 (0x5F6) - SLIMbus TX Channel Enable + */ +#define ARIZONA_SLIMTX8_ENA 0x0080 /* SLIMTX8_ENA */ +#define ARIZONA_SLIMTX8_ENA_MASK 0x0080 /* SLIMTX8_ENA */ +#define ARIZONA_SLIMTX8_ENA_SHIFT 7 /* SLIMTX8_ENA */ +#define ARIZONA_SLIMTX8_ENA_WIDTH 1 /* SLIMTX8_ENA */ +#define ARIZONA_SLIMTX7_ENA 0x0040 /* SLIMTX7_ENA */ +#define ARIZONA_SLIMTX7_ENA_MASK 0x0040 /* SLIMTX7_ENA */ +#define ARIZONA_SLIMTX7_ENA_SHIFT 6 /* SLIMTX7_ENA */ +#define ARIZONA_SLIMTX7_ENA_WIDTH 1 /* SLIMTX7_ENA */ +#define ARIZONA_SLIMTX6_ENA 0x0020 /* SLIMTX6_ENA */ +#define ARIZONA_SLIMTX6_ENA_MASK 0x0020 /* SLIMTX6_ENA */ +#define ARIZONA_SLIMTX6_ENA_SHIFT 5 /* SLIMTX6_ENA */ +#define ARIZONA_SLIMTX6_ENA_WIDTH 1 /* SLIMTX6_ENA */ +#define ARIZONA_SLIMTX5_ENA 0x0010 /* SLIMTX5_ENA */ +#define ARIZONA_SLIMTX5_ENA_MASK 0x0010 /* SLIMTX5_ENA */ +#define ARIZONA_SLIMTX5_ENA_SHIFT 4 /* SLIMTX5_ENA */ +#define ARIZONA_SLIMTX5_ENA_WIDTH 1 /* SLIMTX5_ENA */ +#define ARIZONA_SLIMTX4_ENA 0x0008 /* SLIMTX4_ENA */ +#define ARIZONA_SLIMTX4_ENA_MASK 0x0008 /* SLIMTX4_ENA */ +#define ARIZONA_SLIMTX4_ENA_SHIFT 3 /* SLIMTX4_ENA */ +#define ARIZONA_SLIMTX4_ENA_WIDTH 1 /* SLIMTX4_ENA */ +#define ARIZONA_SLIMTX3_ENA 0x0004 /* SLIMTX3_ENA */ +#define ARIZONA_SLIMTX3_ENA_MASK 0x0004 /* SLIMTX3_ENA */ +#define ARIZONA_SLIMTX3_ENA_SHIFT 2 /* SLIMTX3_ENA */ +#define ARIZONA_SLIMTX3_ENA_WIDTH 1 /* SLIMTX3_ENA */ +#define ARIZONA_SLIMTX2_ENA 0x0002 /* SLIMTX2_ENA */ +#define ARIZONA_SLIMTX2_ENA_MASK 0x0002 /* SLIMTX2_ENA */ +#define ARIZONA_SLIMTX2_ENA_SHIFT 1 /* SLIMTX2_ENA */ +#define ARIZONA_SLIMTX2_ENA_WIDTH 1 /* SLIMTX2_ENA */ +#define ARIZONA_SLIMTX1_ENA 0x0001 /* SLIMTX1_ENA */ +#define ARIZONA_SLIMTX1_ENA_MASK 0x0001 /* SLIMTX1_ENA */ +#define ARIZONA_SLIMTX1_ENA_SHIFT 0 /* SLIMTX1_ENA */ +#define ARIZONA_SLIMTX1_ENA_WIDTH 1 /* SLIMTX1_ENA */ + +/* + * R1527 (0x5F7) - SLIMbus RX Port Status + */ +#define ARIZONA_SLIMRX8_PORT_STS 0x0080 /* SLIMRX8_PORT_STS */ +#define ARIZONA_SLIMRX8_PORT_STS_MASK 0x0080 /* SLIMRX8_PORT_STS */ +#define ARIZONA_SLIMRX8_PORT_STS_SHIFT 7 /* SLIMRX8_PORT_STS */ +#define ARIZONA_SLIMRX8_PORT_STS_WIDTH 1 /* SLIMRX8_PORT_STS */ +#define ARIZONA_SLIMRX7_PORT_STS 0x0040 /* SLIMRX7_PORT_STS */ +#define ARIZONA_SLIMRX7_PORT_STS_MASK 0x0040 /* SLIMRX7_PORT_STS */ +#define ARIZONA_SLIMRX7_PORT_STS_SHIFT 6 /* SLIMRX7_PORT_STS */ +#define ARIZONA_SLIMRX7_PORT_STS_WIDTH 1 /* SLIMRX7_PORT_STS */ +#define ARIZONA_SLIMRX6_PORT_STS 0x0020 /* SLIMRX6_PORT_STS */ +#define ARIZONA_SLIMRX6_PORT_STS_MASK 0x0020 /* SLIMRX6_PORT_STS */ +#define ARIZONA_SLIMRX6_PORT_STS_SHIFT 5 /* SLIMRX6_PORT_STS */ +#define ARIZONA_SLIMRX6_PORT_STS_WIDTH 1 /* SLIMRX6_PORT_STS */ +#define ARIZONA_SLIMRX5_PORT_STS 0x0010 /* SLIMRX5_PORT_STS */ +#define ARIZONA_SLIMRX5_PORT_STS_MASK 0x0010 /* SLIMRX5_PORT_STS */ +#define ARIZONA_SLIMRX5_PORT_STS_SHIFT 4 /* SLIMRX5_PORT_STS */ +#define ARIZONA_SLIMRX5_PORT_STS_WIDTH 1 /* SLIMRX5_PORT_STS */ +#define ARIZONA_SLIMRX4_PORT_STS 0x0008 /* SLIMRX4_PORT_STS */ +#define ARIZONA_SLIMRX4_PORT_STS_MASK 0x0008 /* SLIMRX4_PORT_STS */ +#define ARIZONA_SLIMRX4_PORT_STS_SHIFT 3 /* SLIMRX4_PORT_STS */ +#define ARIZONA_SLIMRX4_PORT_STS_WIDTH 1 /* SLIMRX4_PORT_STS */ +#define ARIZONA_SLIMRX3_PORT_STS 0x0004 /* SLIMRX3_PORT_STS */ +#define ARIZONA_SLIMRX3_PORT_STS_MASK 0x0004 /* SLIMRX3_PORT_STS */ +#define ARIZONA_SLIMRX3_PORT_STS_SHIFT 2 /* SLIMRX3_PORT_STS */ +#define ARIZONA_SLIMRX3_PORT_STS_WIDTH 1 /* SLIMRX3_PORT_STS */ +#define ARIZONA_SLIMRX2_PORT_STS 0x0002 /* SLIMRX2_PORT_STS */ +#define ARIZONA_SLIMRX2_PORT_STS_MASK 0x0002 /* SLIMRX2_PORT_STS */ +#define ARIZONA_SLIMRX2_PORT_STS_SHIFT 1 /* SLIMRX2_PORT_STS */ +#define ARIZONA_SLIMRX2_PORT_STS_WIDTH 1 /* SLIMRX2_PORT_STS */ +#define ARIZONA_SLIMRX1_PORT_STS 0x0001 /* SLIMRX1_PORT_STS */ +#define ARIZONA_SLIMRX1_PORT_STS_MASK 0x0001 /* SLIMRX1_PORT_STS */ +#define ARIZONA_SLIMRX1_PORT_STS_SHIFT 0 /* SLIMRX1_PORT_STS */ +#define ARIZONA_SLIMRX1_PORT_STS_WIDTH 1 /* SLIMRX1_PORT_STS */ + +/* + * R1528 (0x5F8) - SLIMbus TX Port Status + */ +#define ARIZONA_SLIMTX8_PORT_STS 0x0080 /* SLIMTX8_PORT_STS */ +#define ARIZONA_SLIMTX8_PORT_STS_MASK 0x0080 /* SLIMTX8_PORT_STS */ +#define ARIZONA_SLIMTX8_PORT_STS_SHIFT 7 /* SLIMTX8_PORT_STS */ +#define ARIZONA_SLIMTX8_PORT_STS_WIDTH 1 /* SLIMTX8_PORT_STS */ +#define ARIZONA_SLIMTX7_PORT_STS 0x0040 /* SLIMTX7_PORT_STS */ +#define ARIZONA_SLIMTX7_PORT_STS_MASK 0x0040 /* SLIMTX7_PORT_STS */ +#define ARIZONA_SLIMTX7_PORT_STS_SHIFT 6 /* SLIMTX7_PORT_STS */ +#define ARIZONA_SLIMTX7_PORT_STS_WIDTH 1 /* SLIMTX7_PORT_STS */ +#define ARIZONA_SLIMTX6_PORT_STS 0x0020 /* SLIMTX6_PORT_STS */ +#define ARIZONA_SLIMTX6_PORT_STS_MASK 0x0020 /* SLIMTX6_PORT_STS */ +#define ARIZONA_SLIMTX6_PORT_STS_SHIFT 5 /* SLIMTX6_PORT_STS */ +#define ARIZONA_SLIMTX6_PORT_STS_WIDTH 1 /* SLIMTX6_PORT_STS */ +#define ARIZONA_SLIMTX5_PORT_STS 0x0010 /* SLIMTX5_PORT_STS */ +#define ARIZONA_SLIMTX5_PORT_STS_MASK 0x0010 /* SLIMTX5_PORT_STS */ +#define ARIZONA_SLIMTX5_PORT_STS_SHIFT 4 /* SLIMTX5_PORT_STS */ +#define ARIZONA_SLIMTX5_PORT_STS_WIDTH 1 /* SLIMTX5_PORT_STS */ +#define ARIZONA_SLIMTX4_PORT_STS 0x0008 /* SLIMTX4_PORT_STS */ +#define ARIZONA_SLIMTX4_PORT_STS_MASK 0x0008 /* SLIMTX4_PORT_STS */ +#define ARIZONA_SLIMTX4_PORT_STS_SHIFT 3 /* SLIMTX4_PORT_STS */ +#define ARIZONA_SLIMTX4_PORT_STS_WIDTH 1 /* SLIMTX4_PORT_STS */ +#define ARIZONA_SLIMTX3_PORT_STS 0x0004 /* SLIMTX3_PORT_STS */ +#define ARIZONA_SLIMTX3_PORT_STS_MASK 0x0004 /* SLIMTX3_PORT_STS */ +#define ARIZONA_SLIMTX3_PORT_STS_SHIFT 2 /* SLIMTX3_PORT_STS */ +#define ARIZONA_SLIMTX3_PORT_STS_WIDTH 1 /* SLIMTX3_PORT_STS */ +#define ARIZONA_SLIMTX2_PORT_STS 0x0002 /* SLIMTX2_PORT_STS */ +#define ARIZONA_SLIMTX2_PORT_STS_MASK 0x0002 /* SLIMTX2_PORT_STS */ +#define ARIZONA_SLIMTX2_PORT_STS_SHIFT 1 /* SLIMTX2_PORT_STS */ +#define ARIZONA_SLIMTX2_PORT_STS_WIDTH 1 /* SLIMTX2_PORT_STS */ +#define ARIZONA_SLIMTX1_PORT_STS 0x0001 /* SLIMTX1_PORT_STS */ +#define ARIZONA_SLIMTX1_PORT_STS_MASK 0x0001 /* SLIMTX1_PORT_STS */ +#define ARIZONA_SLIMTX1_PORT_STS_SHIFT 0 /* SLIMTX1_PORT_STS */ +#define ARIZONA_SLIMTX1_PORT_STS_WIDTH 1 /* SLIMTX1_PORT_STS */ + +/* + * R3087 (0xC0F) - IRQ CTRL 1 + */ +#define ARIZONA_IRQ_POL 0x0400 /* IRQ_POL */ +#define ARIZONA_IRQ_POL_MASK 0x0400 /* IRQ_POL */ +#define ARIZONA_IRQ_POL_SHIFT 10 /* IRQ_POL */ +#define ARIZONA_IRQ_POL_WIDTH 1 /* IRQ_POL */ +#define ARIZONA_IRQ_OP_CFG 0x0200 /* IRQ_OP_CFG */ +#define ARIZONA_IRQ_OP_CFG_MASK 0x0200 /* IRQ_OP_CFG */ +#define ARIZONA_IRQ_OP_CFG_SHIFT 9 /* IRQ_OP_CFG */ +#define ARIZONA_IRQ_OP_CFG_WIDTH 1 /* IRQ_OP_CFG */ + +/* + * R3088 (0xC10) - GPIO Debounce Config + */ +#define ARIZONA_GP_DBTIME_MASK 0xF000 /* GP_DBTIME - [15:12] */ +#define ARIZONA_GP_DBTIME_SHIFT 12 /* GP_DBTIME - [15:12] */ +#define ARIZONA_GP_DBTIME_WIDTH 4 /* GP_DBTIME - [15:12] */ + +/* + * R3104 (0xC20) - Misc Pad Ctrl 1 + */ +#define ARIZONA_LDO1ENA_PD 0x8000 /* LDO1ENA_PD */ +#define ARIZONA_LDO1ENA_PD_MASK 0x8000 /* LDO1ENA_PD */ +#define ARIZONA_LDO1ENA_PD_SHIFT 15 /* LDO1ENA_PD */ +#define ARIZONA_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */ +#define ARIZONA_MCLK2_PD 0x2000 /* MCLK2_PD */ +#define ARIZONA_MCLK2_PD_MASK 0x2000 /* MCLK2_PD */ +#define ARIZONA_MCLK2_PD_SHIFT 13 /* MCLK2_PD */ +#define ARIZONA_MCLK2_PD_WIDTH 1 /* MCLK2_PD */ +#define ARIZONA_RSTB_PU 0x0002 /* RSTB_PU */ +#define ARIZONA_RSTB_PU_MASK 0x0002 /* RSTB_PU */ +#define ARIZONA_RSTB_PU_SHIFT 1 /* RSTB_PU */ +#define ARIZONA_RSTB_PU_WIDTH 1 /* RSTB_PU */ + +/* + * R3105 (0xC21) - Misc Pad Ctrl 2 + */ +#define ARIZONA_MCLK1_PD 0x1000 /* MCLK1_PD */ +#define ARIZONA_MCLK1_PD_MASK 0x1000 /* MCLK1_PD */ +#define ARIZONA_MCLK1_PD_SHIFT 12 /* MCLK1_PD */ +#define ARIZONA_MCLK1_PD_WIDTH 1 /* MCLK1_PD */ +#define ARIZONA_MICD_PD 0x0100 /* MICD_PD */ +#define ARIZONA_MICD_PD_MASK 0x0100 /* MICD_PD */ +#define ARIZONA_MICD_PD_SHIFT 8 /* MICD_PD */ +#define ARIZONA_MICD_PD_WIDTH 1 /* MICD_PD */ +#define ARIZONA_ADDR_PD 0x0001 /* ADDR_PD */ +#define ARIZONA_ADDR_PD_MASK 0x0001 /* ADDR_PD */ +#define ARIZONA_ADDR_PD_SHIFT 0 /* ADDR_PD */ +#define ARIZONA_ADDR_PD_WIDTH 1 /* ADDR_PD */ + +/* + * R3106 (0xC22) - Misc Pad Ctrl 3 + */ +#define ARIZONA_DMICDAT4_PD 0x0008 /* DMICDAT4_PD */ +#define ARIZONA_DMICDAT4_PD_MASK 0x0008 /* DMICDAT4_PD */ +#define ARIZONA_DMICDAT4_PD_SHIFT 3 /* DMICDAT4_PD */ +#define ARIZONA_DMICDAT4_PD_WIDTH 1 /* DMICDAT4_PD */ +#define ARIZONA_DMICDAT3_PD 0x0004 /* DMICDAT3_PD */ +#define ARIZONA_DMICDAT3_PD_MASK 0x0004 /* DMICDAT3_PD */ +#define ARIZONA_DMICDAT3_PD_SHIFT 2 /* DMICDAT3_PD */ +#define ARIZONA_DMICDAT3_PD_WIDTH 1 /* DMICDAT3_PD */ +#define ARIZONA_DMICDAT2_PD 0x0002 /* DMICDAT2_PD */ +#define ARIZONA_DMICDAT2_PD_MASK 0x0002 /* DMICDAT2_PD */ +#define ARIZONA_DMICDAT2_PD_SHIFT 1 /* DMICDAT2_PD */ +#define ARIZONA_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */ +#define ARIZONA_DMICDAT1_PD 0x0001 /* DMICDAT1_PD */ +#define ARIZONA_DMICDAT1_PD_MASK 0x0001 /* DMICDAT1_PD */ +#define ARIZONA_DMICDAT1_PD_SHIFT 0 /* DMICDAT1_PD */ +#define ARIZONA_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */ + +/* + * R3107 (0xC23) - Misc Pad Ctrl 4 + */ +#define ARIZONA_AIF1RXLRCLK_PU 0x0020 /* AIF1RXLRCLK_PU */ +#define ARIZONA_AIF1RXLRCLK_PU_MASK 0x0020 /* AIF1RXLRCLK_PU */ +#define ARIZONA_AIF1RXLRCLK_PU_SHIFT 5 /* AIF1RXLRCLK_PU */ +#define ARIZONA_AIF1RXLRCLK_PU_WIDTH 1 /* AIF1RXLRCLK_PU */ +#define ARIZONA_AIF1RXLRCLK_PD 0x0010 /* AIF1RXLRCLK_PD */ +#define ARIZONA_AIF1RXLRCLK_PD_MASK 0x0010 /* AIF1RXLRCLK_PD */ +#define ARIZONA_AIF1RXLRCLK_PD_SHIFT 4 /* AIF1RXLRCLK_PD */ +#define ARIZONA_AIF1RXLRCLK_PD_WIDTH 1 /* AIF1RXLRCLK_PD */ +#define ARIZONA_AIF1BCLK_PU 0x0008 /* AIF1BCLK_PU */ +#define ARIZONA_AIF1BCLK_PU_MASK 0x0008 /* AIF1BCLK_PU */ +#define ARIZONA_AIF1BCLK_PU_SHIFT 3 /* AIF1BCLK_PU */ +#define ARIZONA_AIF1BCLK_PU_WIDTH 1 /* AIF1BCLK_PU */ +#define ARIZONA_AIF1BCLK_PD 0x0004 /* AIF1BCLK_PD */ +#define ARIZONA_AIF1BCLK_PD_MASK 0x0004 /* AIF1BCLK_PD */ +#define ARIZONA_AIF1BCLK_PD_SHIFT 2 /* AIF1BCLK_PD */ +#define ARIZONA_AIF1BCLK_PD_WIDTH 1 /* AIF1BCLK_PD */ +#define ARIZONA_AIF1RXDAT_PU 0x0002 /* AIF1RXDAT_PU */ +#define ARIZONA_AIF1RXDAT_PU_MASK 0x0002 /* AIF1RXDAT_PU */ +#define ARIZONA_AIF1RXDAT_PU_SHIFT 1 /* AIF1RXDAT_PU */ +#define ARIZONA_AIF1RXDAT_PU_WIDTH 1 /* AIF1RXDAT_PU */ +#define ARIZONA_AIF1RXDAT_PD 0x0001 /* AIF1RXDAT_PD */ +#define ARIZONA_AIF1RXDAT_PD_MASK 0x0001 /* AIF1RXDAT_PD */ +#define ARIZONA_AIF1RXDAT_PD_SHIFT 0 /* AIF1RXDAT_PD */ +#define ARIZONA_AIF1RXDAT_PD_WIDTH 1 /* AIF1RXDAT_PD */ + +/* + * R3108 (0xC24) - Misc Pad Ctrl 5 + */ +#define ARIZONA_AIF2RXLRCLK_PU 0x0020 /* AIF2RXLRCLK_PU */ +#define ARIZONA_AIF2RXLRCLK_PU_MASK 0x0020 /* AIF2RXLRCLK_PU */ +#define ARIZONA_AIF2RXLRCLK_PU_SHIFT 5 /* AIF2RXLRCLK_PU */ +#define ARIZONA_AIF2RXLRCLK_PU_WIDTH 1 /* AIF2RXLRCLK_PU */ +#define ARIZONA_AIF2RXLRCLK_PD 0x0010 /* AIF2RXLRCLK_PD */ +#define ARIZONA_AIF2RXLRCLK_PD_MASK 0x0010 /* AIF2RXLRCLK_PD */ +#define ARIZONA_AIF2RXLRCLK_PD_SHIFT 4 /* AIF2RXLRCLK_PD */ +#define ARIZONA_AIF2RXLRCLK_PD_WIDTH 1 /* AIF2RXLRCLK_PD */ +#define ARIZONA_AIF2BCLK_PU 0x0008 /* AIF2BCLK_PU */ +#define ARIZONA_AIF2BCLK_PU_MASK 0x0008 /* AIF2BCLK_PU */ +#define ARIZONA_AIF2BCLK_PU_SHIFT 3 /* AIF2BCLK_PU */ +#define ARIZONA_AIF2BCLK_PU_WIDTH 1 /* AIF2BCLK_PU */ +#define ARIZONA_AIF2BCLK_PD 0x0004 /* AIF2BCLK_PD */ +#define ARIZONA_AIF2BCLK_PD_MASK 0x0004 /* AIF2BCLK_PD */ +#define ARIZONA_AIF2BCLK_PD_SHIFT 2 /* AIF2BCLK_PD */ +#define ARIZONA_AIF2BCLK_PD_WIDTH 1 /* AIF2BCLK_PD */ +#define ARIZONA_AIF2RXDAT_PU 0x0002 /* AIF2RXDAT_PU */ +#define ARIZONA_AIF2RXDAT_PU_MASK 0x0002 /* AIF2RXDAT_PU */ +#define ARIZONA_AIF2RXDAT_PU_SHIFT 1 /* AIF2RXDAT_PU */ +#define ARIZONA_AIF2RXDAT_PU_WIDTH 1 /* AIF2RXDAT_PU */ +#define ARIZONA_AIF2RXDAT_PD 0x0001 /* AIF2RXDAT_PD */ +#define ARIZONA_AIF2RXDAT_PD_MASK 0x0001 /* AIF2RXDAT_PD */ +#define ARIZONA_AIF2RXDAT_PD_SHIFT 0 /* AIF2RXDAT_PD */ +#define ARIZONA_AIF2RXDAT_PD_WIDTH 1 /* AIF2RXDAT_PD */ + +/* + * R3109 (0xC25) - Misc Pad Ctrl 6 + */ +#define ARIZONA_AIF3RXLRCLK_PU 0x0020 /* AIF3RXLRCLK_PU */ +#define ARIZONA_AIF3RXLRCLK_PU_MASK 0x0020 /* AIF3RXLRCLK_PU */ +#define ARIZONA_AIF3RXLRCLK_PU_SHIFT 5 /* AIF3RXLRCLK_PU */ +#define ARIZONA_AIF3RXLRCLK_PU_WIDTH 1 /* AIF3RXLRCLK_PU */ +#define ARIZONA_AIF3RXLRCLK_PD 0x0010 /* AIF3RXLRCLK_PD */ +#define ARIZONA_AIF3RXLRCLK_PD_MASK 0x0010 /* AIF3RXLRCLK_PD */ +#define ARIZONA_AIF3RXLRCLK_PD_SHIFT 4 /* AIF3RXLRCLK_PD */ +#define ARIZONA_AIF3RXLRCLK_PD_WIDTH 1 /* AIF3RXLRCLK_PD */ +#define ARIZONA_AIF3BCLK_PU 0x0008 /* AIF3BCLK_PU */ +#define ARIZONA_AIF3BCLK_PU_MASK 0x0008 /* AIF3BCLK_PU */ +#define ARIZONA_AIF3BCLK_PU_SHIFT 3 /* AIF3BCLK_PU */ +#define ARIZONA_AIF3BCLK_PU_WIDTH 1 /* AIF3BCLK_PU */ +#define ARIZONA_AIF3BCLK_PD 0x0004 /* AIF3BCLK_PD */ +#define ARIZONA_AIF3BCLK_PD_MASK 0x0004 /* AIF3BCLK_PD */ +#define ARIZONA_AIF3BCLK_PD_SHIFT 2 /* AIF3BCLK_PD */ +#define ARIZONA_AIF3BCLK_PD_WIDTH 1 /* AIF3BCLK_PD */ +#define ARIZONA_AIF3RXDAT_PU 0x0002 /* AIF3RXDAT_PU */ +#define ARIZONA_AIF3RXDAT_PU_MASK 0x0002 /* AIF3RXDAT_PU */ +#define ARIZONA_AIF3RXDAT_PU_SHIFT 1 /* AIF3RXDAT_PU */ +#define ARIZONA_AIF3RXDAT_PU_WIDTH 1 /* AIF3RXDAT_PU */ +#define ARIZONA_AIF3RXDAT_PD 0x0001 /* AIF3RXDAT_PD */ +#define ARIZONA_AIF3RXDAT_PD_MASK 0x0001 /* AIF3RXDAT_PD */ +#define ARIZONA_AIF3RXDAT_PD_SHIFT 0 /* AIF3RXDAT_PD */ +#define ARIZONA_AIF3RXDAT_PD_WIDTH 1 /* AIF3RXDAT_PD */ + +/* + * R3328 (0xD00) - Interrupt Status 1 + */ +#define ARIZONA_GP4_EINT1 0x0008 /* GP4_EINT1 */ +#define ARIZONA_GP4_EINT1_MASK 0x0008 /* GP4_EINT1 */ +#define ARIZONA_GP4_EINT1_SHIFT 3 /* GP4_EINT1 */ +#define ARIZONA_GP4_EINT1_WIDTH 1 /* GP4_EINT1 */ +#define ARIZONA_GP3_EINT1 0x0004 /* GP3_EINT1 */ +#define ARIZONA_GP3_EINT1_MASK 0x0004 /* GP3_EINT1 */ +#define ARIZONA_GP3_EINT1_SHIFT 2 /* GP3_EINT1 */ +#define ARIZONA_GP3_EINT1_WIDTH 1 /* GP3_EINT1 */ +#define ARIZONA_GP2_EINT1 0x0002 /* GP2_EINT1 */ +#define ARIZONA_GP2_EINT1_MASK 0x0002 /* GP2_EINT1 */ +#define ARIZONA_GP2_EINT1_SHIFT 1 /* GP2_EINT1 */ +#define ARIZONA_GP2_EINT1_WIDTH 1 /* GP2_EINT1 */ +#define ARIZONA_GP1_EINT1 0x0001 /* GP1_EINT1 */ +#define ARIZONA_GP1_EINT1_MASK 0x0001 /* GP1_EINT1 */ +#define ARIZONA_GP1_EINT1_SHIFT 0 /* GP1_EINT1 */ +#define ARIZONA_GP1_EINT1_WIDTH 1 /* GP1_EINT1 */ + +/* + * R3329 (0xD01) - Interrupt Status 2 + */ +#define ARIZONA_DSP1_RAM_RDY_EINT1 0x0100 /* DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_DSP1_RAM_RDY_EINT1_MASK 0x0100 /* DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_DSP1_RAM_RDY_EINT1_SHIFT 8 /* DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_DSP1_RAM_RDY_EINT1_WIDTH 1 /* DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_DSP_IRQ2_EINT1 0x0002 /* DSP_IRQ2_EINT1 */ +#define ARIZONA_DSP_IRQ2_EINT1_MASK 0x0002 /* DSP_IRQ2_EINT1 */ +#define ARIZONA_DSP_IRQ2_EINT1_SHIFT 1 /* DSP_IRQ2_EINT1 */ +#define ARIZONA_DSP_IRQ2_EINT1_WIDTH 1 /* DSP_IRQ2_EINT1 */ +#define ARIZONA_DSP_IRQ1_EINT1 0x0001 /* DSP_IRQ1_EINT1 */ +#define ARIZONA_DSP_IRQ1_EINT1_MASK 0x0001 /* DSP_IRQ1_EINT1 */ +#define ARIZONA_DSP_IRQ1_EINT1_SHIFT 0 /* DSP_IRQ1_EINT1 */ +#define ARIZONA_DSP_IRQ1_EINT1_WIDTH 1 /* DSP_IRQ1_EINT1 */ + +/* + * R3330 (0xD02) - Interrupt Status 3 + */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT1 0x8000 /* SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT1_MASK 0x8000 /* SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT1_SHIFT 15 /* SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT1_WIDTH 1 /* SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_SPK_SHUTDOWN_EINT1 0x4000 /* SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_SPK_SHUTDOWN_EINT1_MASK 0x4000 /* SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_SPK_SHUTDOWN_EINT1_SHIFT 14 /* SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_SPK_SHUTDOWN_EINT1_WIDTH 1 /* SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_HPDET_EINT1 0x2000 /* HPDET_EINT1 */ +#define ARIZONA_HPDET_EINT1_MASK 0x2000 /* HPDET_EINT1 */ +#define ARIZONA_HPDET_EINT1_SHIFT 13 /* HPDET_EINT1 */ +#define ARIZONA_HPDET_EINT1_WIDTH 1 /* HPDET_EINT1 */ +#define ARIZONA_MICDET_EINT1 0x1000 /* MICDET_EINT1 */ +#define ARIZONA_MICDET_EINT1_MASK 0x1000 /* MICDET_EINT1 */ +#define ARIZONA_MICDET_EINT1_SHIFT 12 /* MICDET_EINT1 */ +#define ARIZONA_MICDET_EINT1_WIDTH 1 /* MICDET_EINT1 */ +#define ARIZONA_WSEQ_DONE_EINT1 0x0800 /* WSEQ_DONE_EINT1 */ +#define ARIZONA_WSEQ_DONE_EINT1_MASK 0x0800 /* WSEQ_DONE_EINT1 */ +#define ARIZONA_WSEQ_DONE_EINT1_SHIFT 11 /* WSEQ_DONE_EINT1 */ +#define ARIZONA_WSEQ_DONE_EINT1_WIDTH 1 /* WSEQ_DONE_EINT1 */ +#define ARIZONA_DRC2_SIG_DET_EINT1 0x0400 /* DRC2_SIG_DET_EINT1 */ +#define ARIZONA_DRC2_SIG_DET_EINT1_MASK 0x0400 /* DRC2_SIG_DET_EINT1 */ +#define ARIZONA_DRC2_SIG_DET_EINT1_SHIFT 10 /* DRC2_SIG_DET_EINT1 */ +#define ARIZONA_DRC2_SIG_DET_EINT1_WIDTH 1 /* DRC2_SIG_DET_EINT1 */ +#define ARIZONA_DRC1_SIG_DET_EINT1 0x0200 /* DRC1_SIG_DET_EINT1 */ +#define ARIZONA_DRC1_SIG_DET_EINT1_MASK 0x0200 /* DRC1_SIG_DET_EINT1 */ +#define ARIZONA_DRC1_SIG_DET_EINT1_SHIFT 9 /* DRC1_SIG_DET_EINT1 */ +#define ARIZONA_DRC1_SIG_DET_EINT1_WIDTH 1 /* DRC1_SIG_DET_EINT1 */ +#define ARIZONA_ASRC2_LOCK_EINT1 0x0100 /* ASRC2_LOCK_EINT1 */ +#define ARIZONA_ASRC2_LOCK_EINT1_MASK 0x0100 /* ASRC2_LOCK_EINT1 */ +#define ARIZONA_ASRC2_LOCK_EINT1_SHIFT 8 /* ASRC2_LOCK_EINT1 */ +#define ARIZONA_ASRC2_LOCK_EINT1_WIDTH 1 /* ASRC2_LOCK_EINT1 */ +#define ARIZONA_ASRC1_LOCK_EINT1 0x0080 /* ASRC1_LOCK_EINT1 */ +#define ARIZONA_ASRC1_LOCK_EINT1_MASK 0x0080 /* ASRC1_LOCK_EINT1 */ +#define ARIZONA_ASRC1_LOCK_EINT1_SHIFT 7 /* ASRC1_LOCK_EINT1 */ +#define ARIZONA_ASRC1_LOCK_EINT1_WIDTH 1 /* ASRC1_LOCK_EINT1 */ +#define ARIZONA_UNDERCLOCKED_EINT1 0x0040 /* UNDERCLOCKED_EINT1 */ +#define ARIZONA_UNDERCLOCKED_EINT1_MASK 0x0040 /* UNDERCLOCKED_EINT1 */ +#define ARIZONA_UNDERCLOCKED_EINT1_SHIFT 6 /* UNDERCLOCKED_EINT1 */ +#define ARIZONA_UNDERCLOCKED_EINT1_WIDTH 1 /* UNDERCLOCKED_EINT1 */ +#define ARIZONA_OVERCLOCKED_EINT1 0x0020 /* OVERCLOCKED_EINT1 */ +#define ARIZONA_OVERCLOCKED_EINT1_MASK 0x0020 /* OVERCLOCKED_EINT1 */ +#define ARIZONA_OVERCLOCKED_EINT1_SHIFT 5 /* OVERCLOCKED_EINT1 */ +#define ARIZONA_OVERCLOCKED_EINT1_WIDTH 1 /* OVERCLOCKED_EINT1 */ +#define ARIZONA_FLL2_LOCK_EINT1 0x0008 /* FLL2_LOCK_EINT1 */ +#define ARIZONA_FLL2_LOCK_EINT1_MASK 0x0008 /* FLL2_LOCK_EINT1 */ +#define ARIZONA_FLL2_LOCK_EINT1_SHIFT 3 /* FLL2_LOCK_EINT1 */ +#define ARIZONA_FLL2_LOCK_EINT1_WIDTH 1 /* FLL2_LOCK_EINT1 */ +#define ARIZONA_FLL1_LOCK_EINT1 0x0004 /* FLL1_LOCK_EINT1 */ +#define ARIZONA_FLL1_LOCK_EINT1_MASK 0x0004 /* FLL1_LOCK_EINT1 */ +#define ARIZONA_FLL1_LOCK_EINT1_SHIFT 2 /* FLL1_LOCK_EINT1 */ +#define ARIZONA_FLL1_LOCK_EINT1_WIDTH 1 /* FLL1_LOCK_EINT1 */ +#define ARIZONA_CLKGEN_ERR_EINT1 0x0002 /* CLKGEN_ERR_EINT1 */ +#define ARIZONA_CLKGEN_ERR_EINT1_MASK 0x0002 /* CLKGEN_ERR_EINT1 */ +#define ARIZONA_CLKGEN_ERR_EINT1_SHIFT 1 /* CLKGEN_ERR_EINT1 */ +#define ARIZONA_CLKGEN_ERR_EINT1_WIDTH 1 /* CLKGEN_ERR_EINT1 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT1 0x0001 /* CLKGEN_ERR_ASYNC_EINT1 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT1_MASK 0x0001 /* CLKGEN_ERR_ASYNC_EINT1 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT1_SHIFT 0 /* CLKGEN_ERR_ASYNC_EINT1 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT1_WIDTH 1 /* CLKGEN_ERR_ASYNC_EINT1 */ + +/* + * R3331 (0xD03) - Interrupt Status 4 + */ +#define ARIZONA_ASRC_CFG_ERR_EINT1 0x8000 /* ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_ASRC_CFG_ERR_EINT1_MASK 0x8000 /* ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_ASRC_CFG_ERR_EINT1_SHIFT 15 /* ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_ASRC_CFG_ERR_EINT1_WIDTH 1 /* ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_AIF3_ERR_EINT1 0x4000 /* AIF3_ERR_EINT1 */ +#define ARIZONA_AIF3_ERR_EINT1_MASK 0x4000 /* AIF3_ERR_EINT1 */ +#define ARIZONA_AIF3_ERR_EINT1_SHIFT 14 /* AIF3_ERR_EINT1 */ +#define ARIZONA_AIF3_ERR_EINT1_WIDTH 1 /* AIF3_ERR_EINT1 */ +#define ARIZONA_AIF2_ERR_EINT1 0x2000 /* AIF2_ERR_EINT1 */ +#define ARIZONA_AIF2_ERR_EINT1_MASK 0x2000 /* AIF2_ERR_EINT1 */ +#define ARIZONA_AIF2_ERR_EINT1_SHIFT 13 /* AIF2_ERR_EINT1 */ +#define ARIZONA_AIF2_ERR_EINT1_WIDTH 1 /* AIF2_ERR_EINT1 */ +#define ARIZONA_AIF1_ERR_EINT1 0x1000 /* AIF1_ERR_EINT1 */ +#define ARIZONA_AIF1_ERR_EINT1_MASK 0x1000 /* AIF1_ERR_EINT1 */ +#define ARIZONA_AIF1_ERR_EINT1_SHIFT 12 /* AIF1_ERR_EINT1 */ +#define ARIZONA_AIF1_ERR_EINT1_WIDTH 1 /* AIF1_ERR_EINT1 */ +#define ARIZONA_CTRLIF_ERR_EINT1 0x0800 /* CTRLIF_ERR_EINT1 */ +#define ARIZONA_CTRLIF_ERR_EINT1_MASK 0x0800 /* CTRLIF_ERR_EINT1 */ +#define ARIZONA_CTRLIF_ERR_EINT1_SHIFT 11 /* CTRLIF_ERR_EINT1 */ +#define ARIZONA_CTRLIF_ERR_EINT1_WIDTH 1 /* CTRLIF_ERR_EINT1 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT1 0x0400 /* MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT1_MASK 0x0400 /* MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT1_SHIFT 10 /* MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT1_WIDTH 1 /* MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT1 0x0200 /* ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT1_MASK 0x0200 /* ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT1_SHIFT 9 /* ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT1_WIDTH 1 /* ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT1 0x0100 /* SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT1_MASK 0x0100 /* SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT1_SHIFT 8 /* SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT1_WIDTH 1 /* SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT1 0x0080 /* ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT1_MASK 0x0080 /* ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT1_SHIFT 7 /* ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT1_WIDTH 1 /* ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT1 0x0040 /* ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT1_MASK 0x0040 /* ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT1_SHIFT 6 /* ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT1_WIDTH 1 /* ISRC2_CFG_ERR_EINT1 */ + +/* + * R3332 (0xD04) - Interrupt Status 5 + */ +#define ARIZONA_BOOT_DONE_EINT1 0x0100 /* BOOT_DONE_EINT1 */ +#define ARIZONA_BOOT_DONE_EINT1_MASK 0x0100 /* BOOT_DONE_EINT1 */ +#define ARIZONA_BOOT_DONE_EINT1_SHIFT 8 /* BOOT_DONE_EINT1 */ +#define ARIZONA_BOOT_DONE_EINT1_WIDTH 1 /* BOOT_DONE_EINT1 */ +#define ARIZONA_DCS_DAC_DONE_EINT1 0x0080 /* DCS_DAC_DONE_EINT1 */ +#define ARIZONA_DCS_DAC_DONE_EINT1_MASK 0x0080 /* DCS_DAC_DONE_EINT1 */ +#define ARIZONA_DCS_DAC_DONE_EINT1_SHIFT 7 /* DCS_DAC_DONE_EINT1 */ +#define ARIZONA_DCS_DAC_DONE_EINT1_WIDTH 1 /* DCS_DAC_DONE_EINT1 */ +#define ARIZONA_DCS_HP_DONE_EINT1 0x0040 /* DCS_HP_DONE_EINT1 */ +#define ARIZONA_DCS_HP_DONE_EINT1_MASK 0x0040 /* DCS_HP_DONE_EINT1 */ +#define ARIZONA_DCS_HP_DONE_EINT1_SHIFT 6 /* DCS_HP_DONE_EINT1 */ +#define ARIZONA_DCS_HP_DONE_EINT1_WIDTH 1 /* DCS_HP_DONE_EINT1 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT1 0x0002 /* FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT1_MASK 0x0002 /* FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT1_SHIFT 1 /* FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT1_WIDTH 1 /* FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT1 0x0001 /* FLL1_CLOCK_OK_EINT1 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT1_MASK 0x0001 /* FLL1_CLOCK_OK_EINT1 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT1_SHIFT 0 /* FLL1_CLOCK_OK_EINT1 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT1_WIDTH 1 /* FLL1_CLOCK_OK_EINT1 */ + +/* + * R3336 (0xD08) - Interrupt Status 1 Mask + */ +#define ARIZONA_IM_GP4_EINT1 0x0008 /* IM_GP4_EINT1 */ +#define ARIZONA_IM_GP4_EINT1_MASK 0x0008 /* IM_GP4_EINT1 */ +#define ARIZONA_IM_GP4_EINT1_SHIFT 3 /* IM_GP4_EINT1 */ +#define ARIZONA_IM_GP4_EINT1_WIDTH 1 /* IM_GP4_EINT1 */ +#define ARIZONA_IM_GP3_EINT1 0x0004 /* IM_GP3_EINT1 */ +#define ARIZONA_IM_GP3_EINT1_MASK 0x0004 /* IM_GP3_EINT1 */ +#define ARIZONA_IM_GP3_EINT1_SHIFT 2 /* IM_GP3_EINT1 */ +#define ARIZONA_IM_GP3_EINT1_WIDTH 1 /* IM_GP3_EINT1 */ +#define ARIZONA_IM_GP2_EINT1 0x0002 /* IM_GP2_EINT1 */ +#define ARIZONA_IM_GP2_EINT1_MASK 0x0002 /* IM_GP2_EINT1 */ +#define ARIZONA_IM_GP2_EINT1_SHIFT 1 /* IM_GP2_EINT1 */ +#define ARIZONA_IM_GP2_EINT1_WIDTH 1 /* IM_GP2_EINT1 */ +#define ARIZONA_IM_GP1_EINT1 0x0001 /* IM_GP1_EINT1 */ +#define ARIZONA_IM_GP1_EINT1_MASK 0x0001 /* IM_GP1_EINT1 */ +#define ARIZONA_IM_GP1_EINT1_SHIFT 0 /* IM_GP1_EINT1 */ +#define ARIZONA_IM_GP1_EINT1_WIDTH 1 /* IM_GP1_EINT1 */ + +/* + * R3337 (0xD09) - Interrupt Status 2 Mask + */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT1 0x0100 /* IM_DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT1_MASK 0x0100 /* IM_DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT1_SHIFT 8 /* IM_DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT1_WIDTH 1 /* IM_DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_IM_DSP_IRQ2_EINT1 0x0002 /* IM_DSP_IRQ2_EINT1 */ +#define ARIZONA_IM_DSP_IRQ2_EINT1_MASK 0x0002 /* IM_DSP_IRQ2_EINT1 */ +#define ARIZONA_IM_DSP_IRQ2_EINT1_SHIFT 1 /* IM_DSP_IRQ2_EINT1 */ +#define ARIZONA_IM_DSP_IRQ2_EINT1_WIDTH 1 /* IM_DSP_IRQ2_EINT1 */ +#define ARIZONA_IM_DSP_IRQ1_EINT1 0x0001 /* IM_DSP_IRQ1_EINT1 */ +#define ARIZONA_IM_DSP_IRQ1_EINT1_MASK 0x0001 /* IM_DSP_IRQ1_EINT1 */ +#define ARIZONA_IM_DSP_IRQ1_EINT1_SHIFT 0 /* IM_DSP_IRQ1_EINT1 */ +#define ARIZONA_IM_DSP_IRQ1_EINT1_WIDTH 1 /* IM_DSP_IRQ1_EINT1 */ + +/* + * R3338 (0xD0A) - Interrupt Status 3 Mask + */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT1 0x8000 /* IM_SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT1_MASK 0x8000 /* IM_SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT1_SHIFT 15 /* IM_SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT1_WIDTH 1 /* IM_SPK_SHUTDOWN_WARN_EINT1 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT1 0x4000 /* IM_SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT1_MASK 0x4000 /* IM_SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT1_SHIFT 14 /* IM_SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT1_WIDTH 1 /* IM_SPK_SHUTDOWN_EINT1 */ +#define ARIZONA_IM_HPDET_EINT1 0x2000 /* IM_HPDET_EINT1 */ +#define ARIZONA_IM_HPDET_EINT1_MASK 0x2000 /* IM_HPDET_EINT1 */ +#define ARIZONA_IM_HPDET_EINT1_SHIFT 13 /* IM_HPDET_EINT1 */ +#define ARIZONA_IM_HPDET_EINT1_WIDTH 1 /* IM_HPDET_EINT1 */ +#define ARIZONA_IM_MICDET_EINT1 0x1000 /* IM_MICDET_EINT1 */ +#define ARIZONA_IM_MICDET_EINT1_MASK 0x1000 /* IM_MICDET_EINT1 */ +#define ARIZONA_IM_MICDET_EINT1_SHIFT 12 /* IM_MICDET_EINT1 */ +#define ARIZONA_IM_MICDET_EINT1_WIDTH 1 /* IM_MICDET_EINT1 */ +#define ARIZONA_IM_WSEQ_DONE_EINT1 0x0800 /* IM_WSEQ_DONE_EINT1 */ +#define ARIZONA_IM_WSEQ_DONE_EINT1_MASK 0x0800 /* IM_WSEQ_DONE_EINT1 */ +#define ARIZONA_IM_WSEQ_DONE_EINT1_SHIFT 11 /* IM_WSEQ_DONE_EINT1 */ +#define ARIZONA_IM_WSEQ_DONE_EINT1_WIDTH 1 /* IM_WSEQ_DONE_EINT1 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT1 0x0400 /* IM_DRC2_SIG_DET_EINT1 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT1_MASK 0x0400 /* IM_DRC2_SIG_DET_EINT1 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT1_SHIFT 10 /* IM_DRC2_SIG_DET_EINT1 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT1_WIDTH 1 /* IM_DRC2_SIG_DET_EINT1 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT1 0x0200 /* IM_DRC1_SIG_DET_EINT1 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT1_MASK 0x0200 /* IM_DRC1_SIG_DET_EINT1 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT1_SHIFT 9 /* IM_DRC1_SIG_DET_EINT1 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT1_WIDTH 1 /* IM_DRC1_SIG_DET_EINT1 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT1 0x0100 /* IM_ASRC2_LOCK_EINT1 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT1_MASK 0x0100 /* IM_ASRC2_LOCK_EINT1 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT1_SHIFT 8 /* IM_ASRC2_LOCK_EINT1 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT1_WIDTH 1 /* IM_ASRC2_LOCK_EINT1 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT1 0x0080 /* IM_ASRC1_LOCK_EINT1 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT1_MASK 0x0080 /* IM_ASRC1_LOCK_EINT1 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT1_SHIFT 7 /* IM_ASRC1_LOCK_EINT1 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT1_WIDTH 1 /* IM_ASRC1_LOCK_EINT1 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT1 0x0040 /* IM_UNDERCLOCKED_EINT1 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT1_MASK 0x0040 /* IM_UNDERCLOCKED_EINT1 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT1_SHIFT 6 /* IM_UNDERCLOCKED_EINT1 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT1_WIDTH 1 /* IM_UNDERCLOCKED_EINT1 */ +#define ARIZONA_IM_OVERCLOCKED_EINT1 0x0020 /* IM_OVERCLOCKED_EINT1 */ +#define ARIZONA_IM_OVERCLOCKED_EINT1_MASK 0x0020 /* IM_OVERCLOCKED_EINT1 */ +#define ARIZONA_IM_OVERCLOCKED_EINT1_SHIFT 5 /* IM_OVERCLOCKED_EINT1 */ +#define ARIZONA_IM_OVERCLOCKED_EINT1_WIDTH 1 /* IM_OVERCLOCKED_EINT1 */ +#define ARIZONA_IM_FLL2_LOCK_EINT1 0x0008 /* IM_FLL2_LOCK_EINT1 */ +#define ARIZONA_IM_FLL2_LOCK_EINT1_MASK 0x0008 /* IM_FLL2_LOCK_EINT1 */ +#define ARIZONA_IM_FLL2_LOCK_EINT1_SHIFT 3 /* IM_FLL2_LOCK_EINT1 */ +#define ARIZONA_IM_FLL2_LOCK_EINT1_WIDTH 1 /* IM_FLL2_LOCK_EINT1 */ +#define ARIZONA_IM_FLL1_LOCK_EINT1 0x0004 /* IM_FLL1_LOCK_EINT1 */ +#define ARIZONA_IM_FLL1_LOCK_EINT1_MASK 0x0004 /* IM_FLL1_LOCK_EINT1 */ +#define ARIZONA_IM_FLL1_LOCK_EINT1_SHIFT 2 /* IM_FLL1_LOCK_EINT1 */ +#define ARIZONA_IM_FLL1_LOCK_EINT1_WIDTH 1 /* IM_FLL1_LOCK_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT1 0x0002 /* IM_CLKGEN_ERR_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT1_MASK 0x0002 /* IM_CLKGEN_ERR_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT1_SHIFT 1 /* IM_CLKGEN_ERR_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT1_WIDTH 1 /* IM_CLKGEN_ERR_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT1 0x0001 /* IM_CLKGEN_ERR_ASYNC_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT1_MASK 0x0001 /* IM_CLKGEN_ERR_ASYNC_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT1_SHIFT 0 /* IM_CLKGEN_ERR_ASYNC_EINT1 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT1_WIDTH 1 /* IM_CLKGEN_ERR_ASYNC_EINT1 */ + +/* + * R3339 (0xD0B) - Interrupt Status 4 Mask + */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT1 0x8000 /* IM_ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT1_MASK 0x8000 /* IM_ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT1_SHIFT 15 /* IM_ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT1_WIDTH 1 /* IM_ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_IM_AIF3_ERR_EINT1 0x4000 /* IM_AIF3_ERR_EINT1 */ +#define ARIZONA_IM_AIF3_ERR_EINT1_MASK 0x4000 /* IM_AIF3_ERR_EINT1 */ +#define ARIZONA_IM_AIF3_ERR_EINT1_SHIFT 14 /* IM_AIF3_ERR_EINT1 */ +#define ARIZONA_IM_AIF3_ERR_EINT1_WIDTH 1 /* IM_AIF3_ERR_EINT1 */ +#define ARIZONA_IM_AIF2_ERR_EINT1 0x2000 /* IM_AIF2_ERR_EINT1 */ +#define ARIZONA_IM_AIF2_ERR_EINT1_MASK 0x2000 /* IM_AIF2_ERR_EINT1 */ +#define ARIZONA_IM_AIF2_ERR_EINT1_SHIFT 13 /* IM_AIF2_ERR_EINT1 */ +#define ARIZONA_IM_AIF2_ERR_EINT1_WIDTH 1 /* IM_AIF2_ERR_EINT1 */ +#define ARIZONA_IM_AIF1_ERR_EINT1 0x1000 /* IM_AIF1_ERR_EINT1 */ +#define ARIZONA_IM_AIF1_ERR_EINT1_MASK 0x1000 /* IM_AIF1_ERR_EINT1 */ +#define ARIZONA_IM_AIF1_ERR_EINT1_SHIFT 12 /* IM_AIF1_ERR_EINT1 */ +#define ARIZONA_IM_AIF1_ERR_EINT1_WIDTH 1 /* IM_AIF1_ERR_EINT1 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT1 0x0800 /* IM_CTRLIF_ERR_EINT1 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT1_MASK 0x0800 /* IM_CTRLIF_ERR_EINT1 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT1_SHIFT 11 /* IM_CTRLIF_ERR_EINT1 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT1_WIDTH 1 /* IM_CTRLIF_ERR_EINT1 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT1 0x0400 /* IM_MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT1_MASK 0x0400 /* IM_MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT1_SHIFT 10 /* IM_MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT1_WIDTH 1 /* IM_MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT1 0x0200 /* IM_ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT1_MASK 0x0200 /* IM_ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT1_SHIFT 9 /* IM_ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT1_WIDTH 1 /* IM_ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT1 0x0100 /* IM_SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT1_MASK 0x0100 /* IM_SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT1_SHIFT 8 /* IM_SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT1_WIDTH 1 /* IM_SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT1 0x0080 /* IM_ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT1_MASK 0x0080 /* IM_ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT1_SHIFT 7 /* IM_ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT1_WIDTH 1 /* IM_ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT1 0x0040 /* IM_ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT1_MASK 0x0040 /* IM_ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT1_SHIFT 6 /* IM_ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT1_WIDTH 1 /* IM_ISRC2_CFG_ERR_EINT1 */ + +/* + * R3340 (0xD0C) - Interrupt Status 5 Mask + */ +#define ARIZONA_IM_BOOT_DONE_EINT1 0x0100 /* IM_BOOT_DONE_EINT1 */ +#define ARIZONA_IM_BOOT_DONE_EINT1_MASK 0x0100 /* IM_BOOT_DONE_EINT1 */ +#define ARIZONA_IM_BOOT_DONE_EINT1_SHIFT 8 /* IM_BOOT_DONE_EINT1 */ +#define ARIZONA_IM_BOOT_DONE_EINT1_WIDTH 1 /* IM_BOOT_DONE_EINT1 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT1 0x0080 /* IM_DCS_DAC_DONE_EINT1 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT1_MASK 0x0080 /* IM_DCS_DAC_DONE_EINT1 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT1_SHIFT 7 /* IM_DCS_DAC_DONE_EINT1 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT1_WIDTH 1 /* IM_DCS_DAC_DONE_EINT1 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT1 0x0040 /* IM_DCS_HP_DONE_EINT1 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT1_MASK 0x0040 /* IM_DCS_HP_DONE_EINT1 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT1_SHIFT 6 /* IM_DCS_HP_DONE_EINT1 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT1_WIDTH 1 /* IM_DCS_HP_DONE_EINT1 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT1 0x0002 /* IM_FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT1_MASK 0x0002 /* IM_FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT1_SHIFT 1 /* IM_FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT1_WIDTH 1 /* IM_FLL2_CLOCK_OK_EINT1 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT1 0x0001 /* IM_FLL1_CLOCK_OK_EINT1 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT1_MASK 0x0001 /* IM_FLL1_CLOCK_OK_EINT1 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT1_SHIFT 0 /* IM_FLL1_CLOCK_OK_EINT1 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT1_WIDTH 1 /* IM_FLL1_CLOCK_OK_EINT1 */ + +/* + * R3343 (0xD0F) - Interrupt Control + */ +#define ARIZONA_IM_IRQ1 0x0001 /* IM_IRQ1 */ +#define ARIZONA_IM_IRQ1_MASK 0x0001 /* IM_IRQ1 */ +#define ARIZONA_IM_IRQ1_SHIFT 0 /* IM_IRQ1 */ +#define ARIZONA_IM_IRQ1_WIDTH 1 /* IM_IRQ1 */ + +/* + * R3344 (0xD10) - IRQ2 Status 1 + */ +#define ARIZONA_GP4_EINT2 0x0008 /* GP4_EINT2 */ +#define ARIZONA_GP4_EINT2_MASK 0x0008 /* GP4_EINT2 */ +#define ARIZONA_GP4_EINT2_SHIFT 3 /* GP4_EINT2 */ +#define ARIZONA_GP4_EINT2_WIDTH 1 /* GP4_EINT2 */ +#define ARIZONA_GP3_EINT2 0x0004 /* GP3_EINT2 */ +#define ARIZONA_GP3_EINT2_MASK 0x0004 /* GP3_EINT2 */ +#define ARIZONA_GP3_EINT2_SHIFT 2 /* GP3_EINT2 */ +#define ARIZONA_GP3_EINT2_WIDTH 1 /* GP3_EINT2 */ +#define ARIZONA_GP2_EINT2 0x0002 /* GP2_EINT2 */ +#define ARIZONA_GP2_EINT2_MASK 0x0002 /* GP2_EINT2 */ +#define ARIZONA_GP2_EINT2_SHIFT 1 /* GP2_EINT2 */ +#define ARIZONA_GP2_EINT2_WIDTH 1 /* GP2_EINT2 */ +#define ARIZONA_GP1_EINT2 0x0001 /* GP1_EINT2 */ +#define ARIZONA_GP1_EINT2_MASK 0x0001 /* GP1_EINT2 */ +#define ARIZONA_GP1_EINT2_SHIFT 0 /* GP1_EINT2 */ +#define ARIZONA_GP1_EINT2_WIDTH 1 /* GP1_EINT2 */ + +/* + * R3345 (0xD11) - IRQ2 Status 2 + */ +#define ARIZONA_DSP1_RAM_RDY_EINT2 0x0100 /* DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_DSP1_RAM_RDY_EINT2_MASK 0x0100 /* DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_DSP1_RAM_RDY_EINT2_SHIFT 8 /* DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_DSP1_RAM_RDY_EINT2_WIDTH 1 /* DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_DSP_IRQ2_EINT2 0x0002 /* DSP_IRQ2_EINT2 */ +#define ARIZONA_DSP_IRQ2_EINT2_MASK 0x0002 /* DSP_IRQ2_EINT2 */ +#define ARIZONA_DSP_IRQ2_EINT2_SHIFT 1 /* DSP_IRQ2_EINT2 */ +#define ARIZONA_DSP_IRQ2_EINT2_WIDTH 1 /* DSP_IRQ2_EINT2 */ +#define ARIZONA_DSP_IRQ1_EINT2 0x0001 /* DSP_IRQ1_EINT2 */ +#define ARIZONA_DSP_IRQ1_EINT2_MASK 0x0001 /* DSP_IRQ1_EINT2 */ +#define ARIZONA_DSP_IRQ1_EINT2_SHIFT 0 /* DSP_IRQ1_EINT2 */ +#define ARIZONA_DSP_IRQ1_EINT2_WIDTH 1 /* DSP_IRQ1_EINT2 */ + +/* + * R3346 (0xD12) - IRQ2 Status 3 + */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT2 0x8000 /* SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT2_MASK 0x8000 /* SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT2_SHIFT 15 /* SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_SPK_SHUTDOWN_WARN_EINT2_WIDTH 1 /* SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_SPK_SHUTDOWN_EINT2 0x4000 /* SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_SPK_SHUTDOWN_EINT2_MASK 0x4000 /* SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_SPK_SHUTDOWN_EINT2_SHIFT 14 /* SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_SPK_SHUTDOWN_EINT2_WIDTH 1 /* SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_HPDET_EINT2 0x2000 /* HPDET_EINT2 */ +#define ARIZONA_HPDET_EINT2_MASK 0x2000 /* HPDET_EINT2 */ +#define ARIZONA_HPDET_EINT2_SHIFT 13 /* HPDET_EINT2 */ +#define ARIZONA_HPDET_EINT2_WIDTH 1 /* HPDET_EINT2 */ +#define ARIZONA_MICDET_EINT2 0x1000 /* MICDET_EINT2 */ +#define ARIZONA_MICDET_EINT2_MASK 0x1000 /* MICDET_EINT2 */ +#define ARIZONA_MICDET_EINT2_SHIFT 12 /* MICDET_EINT2 */ +#define ARIZONA_MICDET_EINT2_WIDTH 1 /* MICDET_EINT2 */ +#define ARIZONA_WSEQ_DONE_EINT2 0x0800 /* WSEQ_DONE_EINT2 */ +#define ARIZONA_WSEQ_DONE_EINT2_MASK 0x0800 /* WSEQ_DONE_EINT2 */ +#define ARIZONA_WSEQ_DONE_EINT2_SHIFT 11 /* WSEQ_DONE_EINT2 */ +#define ARIZONA_WSEQ_DONE_EINT2_WIDTH 1 /* WSEQ_DONE_EINT2 */ +#define ARIZONA_DRC2_SIG_DET_EINT2 0x0400 /* DRC2_SIG_DET_EINT2 */ +#define ARIZONA_DRC2_SIG_DET_EINT2_MASK 0x0400 /* DRC2_SIG_DET_EINT2 */ +#define ARIZONA_DRC2_SIG_DET_EINT2_SHIFT 10 /* DRC2_SIG_DET_EINT2 */ +#define ARIZONA_DRC2_SIG_DET_EINT2_WIDTH 1 /* DRC2_SIG_DET_EINT2 */ +#define ARIZONA_DRC1_SIG_DET_EINT2 0x0200 /* DRC1_SIG_DET_EINT2 */ +#define ARIZONA_DRC1_SIG_DET_EINT2_MASK 0x0200 /* DRC1_SIG_DET_EINT2 */ +#define ARIZONA_DRC1_SIG_DET_EINT2_SHIFT 9 /* DRC1_SIG_DET_EINT2 */ +#define ARIZONA_DRC1_SIG_DET_EINT2_WIDTH 1 /* DRC1_SIG_DET_EINT2 */ +#define ARIZONA_ASRC2_LOCK_EINT2 0x0100 /* ASRC2_LOCK_EINT2 */ +#define ARIZONA_ASRC2_LOCK_EINT2_MASK 0x0100 /* ASRC2_LOCK_EINT2 */ +#define ARIZONA_ASRC2_LOCK_EINT2_SHIFT 8 /* ASRC2_LOCK_EINT2 */ +#define ARIZONA_ASRC2_LOCK_EINT2_WIDTH 1 /* ASRC2_LOCK_EINT2 */ +#define ARIZONA_ASRC1_LOCK_EINT2 0x0080 /* ASRC1_LOCK_EINT2 */ +#define ARIZONA_ASRC1_LOCK_EINT2_MASK 0x0080 /* ASRC1_LOCK_EINT2 */ +#define ARIZONA_ASRC1_LOCK_EINT2_SHIFT 7 /* ASRC1_LOCK_EINT2 */ +#define ARIZONA_ASRC1_LOCK_EINT2_WIDTH 1 /* ASRC1_LOCK_EINT2 */ +#define ARIZONA_UNDERCLOCKED_EINT2 0x0040 /* UNDERCLOCKED_EINT2 */ +#define ARIZONA_UNDERCLOCKED_EINT2_MASK 0x0040 /* UNDERCLOCKED_EINT2 */ +#define ARIZONA_UNDERCLOCKED_EINT2_SHIFT 6 /* UNDERCLOCKED_EINT2 */ +#define ARIZONA_UNDERCLOCKED_EINT2_WIDTH 1 /* UNDERCLOCKED_EINT2 */ +#define ARIZONA_OVERCLOCKED_EINT2 0x0020 /* OVERCLOCKED_EINT2 */ +#define ARIZONA_OVERCLOCKED_EINT2_MASK 0x0020 /* OVERCLOCKED_EINT2 */ +#define ARIZONA_OVERCLOCKED_EINT2_SHIFT 5 /* OVERCLOCKED_EINT2 */ +#define ARIZONA_OVERCLOCKED_EINT2_WIDTH 1 /* OVERCLOCKED_EINT2 */ +#define ARIZONA_FLL2_LOCK_EINT2 0x0008 /* FLL2_LOCK_EINT2 */ +#define ARIZONA_FLL2_LOCK_EINT2_MASK 0x0008 /* FLL2_LOCK_EINT2 */ +#define ARIZONA_FLL2_LOCK_EINT2_SHIFT 3 /* FLL2_LOCK_EINT2 */ +#define ARIZONA_FLL2_LOCK_EINT2_WIDTH 1 /* FLL2_LOCK_EINT2 */ +#define ARIZONA_FLL1_LOCK_EINT2 0x0004 /* FLL1_LOCK_EINT2 */ +#define ARIZONA_FLL1_LOCK_EINT2_MASK 0x0004 /* FLL1_LOCK_EINT2 */ +#define ARIZONA_FLL1_LOCK_EINT2_SHIFT 2 /* FLL1_LOCK_EINT2 */ +#define ARIZONA_FLL1_LOCK_EINT2_WIDTH 1 /* FLL1_LOCK_EINT2 */ +#define ARIZONA_CLKGEN_ERR_EINT2 0x0002 /* CLKGEN_ERR_EINT2 */ +#define ARIZONA_CLKGEN_ERR_EINT2_MASK 0x0002 /* CLKGEN_ERR_EINT2 */ +#define ARIZONA_CLKGEN_ERR_EINT2_SHIFT 1 /* CLKGEN_ERR_EINT2 */ +#define ARIZONA_CLKGEN_ERR_EINT2_WIDTH 1 /* CLKGEN_ERR_EINT2 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT2 0x0001 /* CLKGEN_ERR_ASYNC_EINT2 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT2_MASK 0x0001 /* CLKGEN_ERR_ASYNC_EINT2 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT2_SHIFT 0 /* CLKGEN_ERR_ASYNC_EINT2 */ +#define ARIZONA_CLKGEN_ERR_ASYNC_EINT2_WIDTH 1 /* CLKGEN_ERR_ASYNC_EINT2 */ + +/* + * R3347 (0xD13) - IRQ2 Status 4 + */ +#define ARIZONA_ASRC_CFG_ERR_EINT2 0x8000 /* ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_ASRC_CFG_ERR_EINT2_MASK 0x8000 /* ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_ASRC_CFG_ERR_EINT2_SHIFT 15 /* ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_ASRC_CFG_ERR_EINT2_WIDTH 1 /* ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_AIF3_ERR_EINT2 0x4000 /* AIF3_ERR_EINT2 */ +#define ARIZONA_AIF3_ERR_EINT2_MASK 0x4000 /* AIF3_ERR_EINT2 */ +#define ARIZONA_AIF3_ERR_EINT2_SHIFT 14 /* AIF3_ERR_EINT2 */ +#define ARIZONA_AIF3_ERR_EINT2_WIDTH 1 /* AIF3_ERR_EINT2 */ +#define ARIZONA_AIF2_ERR_EINT2 0x2000 /* AIF2_ERR_EINT2 */ +#define ARIZONA_AIF2_ERR_EINT2_MASK 0x2000 /* AIF2_ERR_EINT2 */ +#define ARIZONA_AIF2_ERR_EINT2_SHIFT 13 /* AIF2_ERR_EINT2 */ +#define ARIZONA_AIF2_ERR_EINT2_WIDTH 1 /* AIF2_ERR_EINT2 */ +#define ARIZONA_AIF1_ERR_EINT2 0x1000 /* AIF1_ERR_EINT2 */ +#define ARIZONA_AIF1_ERR_EINT2_MASK 0x1000 /* AIF1_ERR_EINT2 */ +#define ARIZONA_AIF1_ERR_EINT2_SHIFT 12 /* AIF1_ERR_EINT2 */ +#define ARIZONA_AIF1_ERR_EINT2_WIDTH 1 /* AIF1_ERR_EINT2 */ +#define ARIZONA_CTRLIF_ERR_EINT2 0x0800 /* CTRLIF_ERR_EINT2 */ +#define ARIZONA_CTRLIF_ERR_EINT2_MASK 0x0800 /* CTRLIF_ERR_EINT2 */ +#define ARIZONA_CTRLIF_ERR_EINT2_SHIFT 11 /* CTRLIF_ERR_EINT2 */ +#define ARIZONA_CTRLIF_ERR_EINT2_WIDTH 1 /* CTRLIF_ERR_EINT2 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT2 0x0400 /* MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT2_MASK 0x0400 /* MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT2_SHIFT 10 /* MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_EINT2_WIDTH 1 /* MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT2 0x0200 /* ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT2_MASK 0x0200 /* ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT2_SHIFT 9 /* ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_EINT2_WIDTH 1 /* ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT2 0x0100 /* SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT2_MASK 0x0100 /* SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT2_SHIFT 8 /* SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_SYSCLK_ENA_LOW_EINT2_WIDTH 1 /* SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT2 0x0080 /* ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT2_MASK 0x0080 /* ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT2_SHIFT 7 /* ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_ISRC1_CFG_ERR_EINT2_WIDTH 1 /* ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT2 0x0040 /* ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT2_MASK 0x0040 /* ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT2_SHIFT 6 /* ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_ISRC2_CFG_ERR_EINT2_WIDTH 1 /* ISRC2_CFG_ERR_EINT2 */ + +/* + * R3348 (0xD14) - IRQ2 Status 5 + */ +#define ARIZONA_BOOT_DONE_EINT2 0x0100 /* BOOT_DONE_EINT2 */ +#define ARIZONA_BOOT_DONE_EINT2_MASK 0x0100 /* BOOT_DONE_EINT2 */ +#define ARIZONA_BOOT_DONE_EINT2_SHIFT 8 /* BOOT_DONE_EINT2 */ +#define ARIZONA_BOOT_DONE_EINT2_WIDTH 1 /* BOOT_DONE_EINT2 */ +#define ARIZONA_DCS_DAC_DONE_EINT2 0x0080 /* DCS_DAC_DONE_EINT2 */ +#define ARIZONA_DCS_DAC_DONE_EINT2_MASK 0x0080 /* DCS_DAC_DONE_EINT2 */ +#define ARIZONA_DCS_DAC_DONE_EINT2_SHIFT 7 /* DCS_DAC_DONE_EINT2 */ +#define ARIZONA_DCS_DAC_DONE_EINT2_WIDTH 1 /* DCS_DAC_DONE_EINT2 */ +#define ARIZONA_DCS_HP_DONE_EINT2 0x0040 /* DCS_HP_DONE_EINT2 */ +#define ARIZONA_DCS_HP_DONE_EINT2_MASK 0x0040 /* DCS_HP_DONE_EINT2 */ +#define ARIZONA_DCS_HP_DONE_EINT2_SHIFT 6 /* DCS_HP_DONE_EINT2 */ +#define ARIZONA_DCS_HP_DONE_EINT2_WIDTH 1 /* DCS_HP_DONE_EINT2 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT2 0x0002 /* FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT2_MASK 0x0002 /* FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT2_SHIFT 1 /* FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_FLL2_CLOCK_OK_EINT2_WIDTH 1 /* FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT2 0x0001 /* FLL1_CLOCK_OK_EINT2 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT2_MASK 0x0001 /* FLL1_CLOCK_OK_EINT2 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT2_SHIFT 0 /* FLL1_CLOCK_OK_EINT2 */ +#define ARIZONA_FLL1_CLOCK_OK_EINT2_WIDTH 1 /* FLL1_CLOCK_OK_EINT2 */ + +/* + * R3352 (0xD18) - IRQ2 Status 1 Mask + */ +#define ARIZONA_IM_GP4_EINT2 0x0008 /* IM_GP4_EINT2 */ +#define ARIZONA_IM_GP4_EINT2_MASK 0x0008 /* IM_GP4_EINT2 */ +#define ARIZONA_IM_GP4_EINT2_SHIFT 3 /* IM_GP4_EINT2 */ +#define ARIZONA_IM_GP4_EINT2_WIDTH 1 /* IM_GP4_EINT2 */ +#define ARIZONA_IM_GP3_EINT2 0x0004 /* IM_GP3_EINT2 */ +#define ARIZONA_IM_GP3_EINT2_MASK 0x0004 /* IM_GP3_EINT2 */ +#define ARIZONA_IM_GP3_EINT2_SHIFT 2 /* IM_GP3_EINT2 */ +#define ARIZONA_IM_GP3_EINT2_WIDTH 1 /* IM_GP3_EINT2 */ +#define ARIZONA_IM_GP2_EINT2 0x0002 /* IM_GP2_EINT2 */ +#define ARIZONA_IM_GP2_EINT2_MASK 0x0002 /* IM_GP2_EINT2 */ +#define ARIZONA_IM_GP2_EINT2_SHIFT 1 /* IM_GP2_EINT2 */ +#define ARIZONA_IM_GP2_EINT2_WIDTH 1 /* IM_GP2_EINT2 */ +#define ARIZONA_IM_GP1_EINT2 0x0001 /* IM_GP1_EINT2 */ +#define ARIZONA_IM_GP1_EINT2_MASK 0x0001 /* IM_GP1_EINT2 */ +#define ARIZONA_IM_GP1_EINT2_SHIFT 0 /* IM_GP1_EINT2 */ +#define ARIZONA_IM_GP1_EINT2_WIDTH 1 /* IM_GP1_EINT2 */ + +/* + * R3353 (0xD19) - IRQ2 Status 2 Mask + */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT2 0x0100 /* IM_DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT2_MASK 0x0100 /* IM_DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT2_SHIFT 8 /* IM_DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_IM_DSP1_RAM_RDY_EINT2_WIDTH 1 /* IM_DSP1_RAM_RDY_EINT2 */ +#define ARIZONA_IM_DSP_IRQ2_EINT2 0x0002 /* IM_DSP_IRQ2_EINT2 */ +#define ARIZONA_IM_DSP_IRQ2_EINT2_MASK 0x0002 /* IM_DSP_IRQ2_EINT2 */ +#define ARIZONA_IM_DSP_IRQ2_EINT2_SHIFT 1 /* IM_DSP_IRQ2_EINT2 */ +#define ARIZONA_IM_DSP_IRQ2_EINT2_WIDTH 1 /* IM_DSP_IRQ2_EINT2 */ +#define ARIZONA_IM_DSP_IRQ1_EINT2 0x0001 /* IM_DSP_IRQ1_EINT2 */ +#define ARIZONA_IM_DSP_IRQ1_EINT2_MASK 0x0001 /* IM_DSP_IRQ1_EINT2 */ +#define ARIZONA_IM_DSP_IRQ1_EINT2_SHIFT 0 /* IM_DSP_IRQ1_EINT2 */ +#define ARIZONA_IM_DSP_IRQ1_EINT2_WIDTH 1 /* IM_DSP_IRQ1_EINT2 */ + +/* + * R3354 (0xD1A) - IRQ2 Status 3 Mask + */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT2 0x8000 /* IM_SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT2_MASK 0x8000 /* IM_SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT2_SHIFT 15 /* IM_SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_IM_SPK_SHUTDOWN_WARN_EINT2_WIDTH 1 /* IM_SPK_SHUTDOWN_WARN_EINT2 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT2 0x4000 /* IM_SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT2_MASK 0x4000 /* IM_SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT2_SHIFT 14 /* IM_SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_IM_SPK_SHUTDOWN_EINT2_WIDTH 1 /* IM_SPK_SHUTDOWN_EINT2 */ +#define ARIZONA_IM_HPDET_EINT2 0x2000 /* IM_HPDET_EINT2 */ +#define ARIZONA_IM_HPDET_EINT2_MASK 0x2000 /* IM_HPDET_EINT2 */ +#define ARIZONA_IM_HPDET_EINT2_SHIFT 13 /* IM_HPDET_EINT2 */ +#define ARIZONA_IM_HPDET_EINT2_WIDTH 1 /* IM_HPDET_EINT2 */ +#define ARIZONA_IM_MICDET_EINT2 0x1000 /* IM_MICDET_EINT2 */ +#define ARIZONA_IM_MICDET_EINT2_MASK 0x1000 /* IM_MICDET_EINT2 */ +#define ARIZONA_IM_MICDET_EINT2_SHIFT 12 /* IM_MICDET_EINT2 */ +#define ARIZONA_IM_MICDET_EINT2_WIDTH 1 /* IM_MICDET_EINT2 */ +#define ARIZONA_IM_WSEQ_DONE_EINT2 0x0800 /* IM_WSEQ_DONE_EINT2 */ +#define ARIZONA_IM_WSEQ_DONE_EINT2_MASK 0x0800 /* IM_WSEQ_DONE_EINT2 */ +#define ARIZONA_IM_WSEQ_DONE_EINT2_SHIFT 11 /* IM_WSEQ_DONE_EINT2 */ +#define ARIZONA_IM_WSEQ_DONE_EINT2_WIDTH 1 /* IM_WSEQ_DONE_EINT2 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT2 0x0400 /* IM_DRC2_SIG_DET_EINT2 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT2_MASK 0x0400 /* IM_DRC2_SIG_DET_EINT2 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT2_SHIFT 10 /* IM_DRC2_SIG_DET_EINT2 */ +#define ARIZONA_IM_DRC2_SIG_DET_EINT2_WIDTH 1 /* IM_DRC2_SIG_DET_EINT2 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT2 0x0200 /* IM_DRC1_SIG_DET_EINT2 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT2_MASK 0x0200 /* IM_DRC1_SIG_DET_EINT2 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT2_SHIFT 9 /* IM_DRC1_SIG_DET_EINT2 */ +#define ARIZONA_IM_DRC1_SIG_DET_EINT2_WIDTH 1 /* IM_DRC1_SIG_DET_EINT2 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT2 0x0100 /* IM_ASRC2_LOCK_EINT2 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT2_MASK 0x0100 /* IM_ASRC2_LOCK_EINT2 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT2_SHIFT 8 /* IM_ASRC2_LOCK_EINT2 */ +#define ARIZONA_IM_ASRC2_LOCK_EINT2_WIDTH 1 /* IM_ASRC2_LOCK_EINT2 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT2 0x0080 /* IM_ASRC1_LOCK_EINT2 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT2_MASK 0x0080 /* IM_ASRC1_LOCK_EINT2 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT2_SHIFT 7 /* IM_ASRC1_LOCK_EINT2 */ +#define ARIZONA_IM_ASRC1_LOCK_EINT2_WIDTH 1 /* IM_ASRC1_LOCK_EINT2 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT2 0x0040 /* IM_UNDERCLOCKED_EINT2 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT2_MASK 0x0040 /* IM_UNDERCLOCKED_EINT2 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT2_SHIFT 6 /* IM_UNDERCLOCKED_EINT2 */ +#define ARIZONA_IM_UNDERCLOCKED_EINT2_WIDTH 1 /* IM_UNDERCLOCKED_EINT2 */ +#define ARIZONA_IM_OVERCLOCKED_EINT2 0x0020 /* IM_OVERCLOCKED_EINT2 */ +#define ARIZONA_IM_OVERCLOCKED_EINT2_MASK 0x0020 /* IM_OVERCLOCKED_EINT2 */ +#define ARIZONA_IM_OVERCLOCKED_EINT2_SHIFT 5 /* IM_OVERCLOCKED_EINT2 */ +#define ARIZONA_IM_OVERCLOCKED_EINT2_WIDTH 1 /* IM_OVERCLOCKED_EINT2 */ +#define ARIZONA_IM_FLL2_LOCK_EINT2 0x0008 /* IM_FLL2_LOCK_EINT2 */ +#define ARIZONA_IM_FLL2_LOCK_EINT2_MASK 0x0008 /* IM_FLL2_LOCK_EINT2 */ +#define ARIZONA_IM_FLL2_LOCK_EINT2_SHIFT 3 /* IM_FLL2_LOCK_EINT2 */ +#define ARIZONA_IM_FLL2_LOCK_EINT2_WIDTH 1 /* IM_FLL2_LOCK_EINT2 */ +#define ARIZONA_IM_FLL1_LOCK_EINT2 0x0004 /* IM_FLL1_LOCK_EINT2 */ +#define ARIZONA_IM_FLL1_LOCK_EINT2_MASK 0x0004 /* IM_FLL1_LOCK_EINT2 */ +#define ARIZONA_IM_FLL1_LOCK_EINT2_SHIFT 2 /* IM_FLL1_LOCK_EINT2 */ +#define ARIZONA_IM_FLL1_LOCK_EINT2_WIDTH 1 /* IM_FLL1_LOCK_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT2 0x0002 /* IM_CLKGEN_ERR_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT2_MASK 0x0002 /* IM_CLKGEN_ERR_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT2_SHIFT 1 /* IM_CLKGEN_ERR_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_EINT2_WIDTH 1 /* IM_CLKGEN_ERR_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT2 0x0001 /* IM_CLKGEN_ERR_ASYNC_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT2_MASK 0x0001 /* IM_CLKGEN_ERR_ASYNC_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT2_SHIFT 0 /* IM_CLKGEN_ERR_ASYNC_EINT2 */ +#define ARIZONA_IM_CLKGEN_ERR_ASYNC_EINT2_WIDTH 1 /* IM_CLKGEN_ERR_ASYNC_EINT2 */ + +/* + * R3355 (0xD1B) - IRQ2 Status 4 Mask + */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT2 0x8000 /* IM_ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT2_MASK 0x8000 /* IM_ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT2_SHIFT 15 /* IM_ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ASRC_CFG_ERR_EINT2_WIDTH 1 /* IM_ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_IM_AIF3_ERR_EINT2 0x4000 /* IM_AIF3_ERR_EINT2 */ +#define ARIZONA_IM_AIF3_ERR_EINT2_MASK 0x4000 /* IM_AIF3_ERR_EINT2 */ +#define ARIZONA_IM_AIF3_ERR_EINT2_SHIFT 14 /* IM_AIF3_ERR_EINT2 */ +#define ARIZONA_IM_AIF3_ERR_EINT2_WIDTH 1 /* IM_AIF3_ERR_EINT2 */ +#define ARIZONA_IM_AIF2_ERR_EINT2 0x2000 /* IM_AIF2_ERR_EINT2 */ +#define ARIZONA_IM_AIF2_ERR_EINT2_MASK 0x2000 /* IM_AIF2_ERR_EINT2 */ +#define ARIZONA_IM_AIF2_ERR_EINT2_SHIFT 13 /* IM_AIF2_ERR_EINT2 */ +#define ARIZONA_IM_AIF2_ERR_EINT2_WIDTH 1 /* IM_AIF2_ERR_EINT2 */ +#define ARIZONA_IM_AIF1_ERR_EINT2 0x1000 /* IM_AIF1_ERR_EINT2 */ +#define ARIZONA_IM_AIF1_ERR_EINT2_MASK 0x1000 /* IM_AIF1_ERR_EINT2 */ +#define ARIZONA_IM_AIF1_ERR_EINT2_SHIFT 12 /* IM_AIF1_ERR_EINT2 */ +#define ARIZONA_IM_AIF1_ERR_EINT2_WIDTH 1 /* IM_AIF1_ERR_EINT2 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT2 0x0800 /* IM_CTRLIF_ERR_EINT2 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT2_MASK 0x0800 /* IM_CTRLIF_ERR_EINT2 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT2_SHIFT 11 /* IM_CTRLIF_ERR_EINT2 */ +#define ARIZONA_IM_CTRLIF_ERR_EINT2_WIDTH 1 /* IM_CTRLIF_ERR_EINT2 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT2 0x0400 /* IM_MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT2_MASK 0x0400 /* IM_MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT2_SHIFT 10 /* IM_MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_IM_MIXER_DROPPED_SAMPLE_EINT2_WIDTH 1 /* IM_MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT2 0x0200 /* IM_ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT2_MASK 0x0200 /* IM_ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT2_SHIFT 9 /* IM_ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_ASYNC_CLK_ENA_LOW_EINT2_WIDTH 1 /* IM_ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT2 0x0100 /* IM_SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT2_MASK 0x0100 /* IM_SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT2_SHIFT 8 /* IM_SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_SYSCLK_ENA_LOW_EINT2_WIDTH 1 /* IM_SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT2 0x0080 /* IM_ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT2_MASK 0x0080 /* IM_ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT2_SHIFT 7 /* IM_ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ISRC1_CFG_ERR_EINT2_WIDTH 1 /* IM_ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT2 0x0040 /* IM_ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT2_MASK 0x0040 /* IM_ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT2_SHIFT 6 /* IM_ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_IM_ISRC2_CFG_ERR_EINT2_WIDTH 1 /* IM_ISRC2_CFG_ERR_EINT2 */ + +/* + * R3356 (0xD1C) - IRQ2 Status 5 Mask + */ + +#define ARIZONA_IM_BOOT_DONE_EINT2 0x0100 /* IM_BOOT_DONE_EINT2 */ +#define ARIZONA_IM_BOOT_DONE_EINT2_MASK 0x0100 /* IM_BOOT_DONE_EINT2 */ +#define ARIZONA_IM_BOOT_DONE_EINT2_SHIFT 8 /* IM_BOOT_DONE_EINT2 */ +#define ARIZONA_IM_BOOT_DONE_EINT2_WIDTH 1 /* IM_BOOT_DONE_EINT2 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT2 0x0080 /* IM_DCS_DAC_DONE_EINT2 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT2_MASK 0x0080 /* IM_DCS_DAC_DONE_EINT2 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT2_SHIFT 7 /* IM_DCS_DAC_DONE_EINT2 */ +#define ARIZONA_IM_DCS_DAC_DONE_EINT2_WIDTH 1 /* IM_DCS_DAC_DONE_EINT2 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT2 0x0040 /* IM_DCS_HP_DONE_EINT2 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT2_MASK 0x0040 /* IM_DCS_HP_DONE_EINT2 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT2_SHIFT 6 /* IM_DCS_HP_DONE_EINT2 */ +#define ARIZONA_IM_DCS_HP_DONE_EINT2_WIDTH 1 /* IM_DCS_HP_DONE_EINT2 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT2 0x0002 /* IM_FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT2_MASK 0x0002 /* IM_FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT2_SHIFT 1 /* IM_FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_IM_FLL2_CLOCK_OK_EINT2_WIDTH 1 /* IM_FLL2_CLOCK_OK_EINT2 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT2 0x0001 /* IM_FLL1_CLOCK_OK_EINT2 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT2_MASK 0x0001 /* IM_FLL1_CLOCK_OK_EINT2 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT2_SHIFT 0 /* IM_FLL1_CLOCK_OK_EINT2 */ +#define ARIZONA_IM_FLL1_CLOCK_OK_EINT2_WIDTH 1 /* IM_FLL1_CLOCK_OK_EINT2 */ + +/* + * R3359 (0xD1F) - IRQ2 Control + */ +#define ARIZONA_IM_IRQ2 0x0001 /* IM_IRQ2 */ +#define ARIZONA_IM_IRQ2_MASK 0x0001 /* IM_IRQ2 */ +#define ARIZONA_IM_IRQ2_SHIFT 0 /* IM_IRQ2 */ +#define ARIZONA_IM_IRQ2_WIDTH 1 /* IM_IRQ2 */ + +/* + * R3360 (0xD20) - Interrupt Raw Status 2 + */ +#define ARIZONA_DSP1_RAM_RDY_STS 0x0100 /* DSP1_RAM_RDY_STS */ +#define ARIZONA_DSP1_RAM_RDY_STS_MASK 0x0100 /* DSP1_RAM_RDY_STS */ +#define ARIZONA_DSP1_RAM_RDY_STS_SHIFT 8 /* DSP1_RAM_RDY_STS */ +#define ARIZONA_DSP1_RAM_RDY_STS_WIDTH 1 /* DSP1_RAM_RDY_STS */ +#define ARIZONA_DSP_IRQ2_STS 0x0002 /* DSP_IRQ2_STS */ +#define ARIZONA_DSP_IRQ2_STS_MASK 0x0002 /* DSP_IRQ2_STS */ +#define ARIZONA_DSP_IRQ2_STS_SHIFT 1 /* DSP_IRQ2_STS */ +#define ARIZONA_DSP_IRQ2_STS_WIDTH 1 /* DSP_IRQ2_STS */ +#define ARIZONA_DSP_IRQ1_STS 0x0001 /* DSP_IRQ1_STS */ +#define ARIZONA_DSP_IRQ1_STS_MASK 0x0001 /* DSP_IRQ1_STS */ +#define ARIZONA_DSP_IRQ1_STS_SHIFT 0 /* DSP_IRQ1_STS */ +#define ARIZONA_DSP_IRQ1_STS_WIDTH 1 /* DSP_IRQ1_STS */ + +/* + * R3361 (0xD21) - Interrupt Raw Status 3 + */ +#define ARIZONA_SPK_SHUTDOWN_WARN_STS 0x8000 /* SPK_SHUTDOWN_WARN_STS */ +#define ARIZONA_SPK_SHUTDOWN_WARN_STS_MASK 0x8000 /* SPK_SHUTDOWN_WARN_STS */ +#define ARIZONA_SPK_SHUTDOWN_WARN_STS_SHIFT 15 /* SPK_SHUTDOWN_WARN_STS */ +#define ARIZONA_SPK_SHUTDOWN_WARN_STS_WIDTH 1 /* SPK_SHUTDOWN_WARN_STS */ +#define ARIZONA_SPK_SHUTDOWN_STS 0x4000 /* SPK_SHUTDOWN_STS */ +#define ARIZONA_SPK_SHUTDOWN_STS_MASK 0x4000 /* SPK_SHUTDOWN_STS */ +#define ARIZONA_SPK_SHUTDOWN_STS_SHIFT 14 /* SPK_SHUTDOWN_STS */ +#define ARIZONA_SPK_SHUTDOWN_STS_WIDTH 1 /* SPK_SHUTDOWN_STS */ +#define ARIZONA_HPDET_STS 0x2000 /* HPDET_STS */ +#define ARIZONA_HPDET_STS_MASK 0x2000 /* HPDET_STS */ +#define ARIZONA_HPDET_STS_SHIFT 13 /* HPDET_STS */ +#define ARIZONA_HPDET_STS_WIDTH 1 /* HPDET_STS */ +#define ARIZONA_MICDET_STS 0x1000 /* MICDET_STS */ +#define ARIZONA_MICDET_STS_MASK 0x1000 /* MICDET_STS */ +#define ARIZONA_MICDET_STS_SHIFT 12 /* MICDET_STS */ +#define ARIZONA_MICDET_STS_WIDTH 1 /* MICDET_STS */ +#define ARIZONA_WSEQ_DONE_STS 0x0800 /* WSEQ_DONE_STS */ +#define ARIZONA_WSEQ_DONE_STS_MASK 0x0800 /* WSEQ_DONE_STS */ +#define ARIZONA_WSEQ_DONE_STS_SHIFT 11 /* WSEQ_DONE_STS */ +#define ARIZONA_WSEQ_DONE_STS_WIDTH 1 /* WSEQ_DONE_STS */ +#define ARIZONA_DRC2_SIG_DET_STS 0x0400 /* DRC2_SIG_DET_STS */ +#define ARIZONA_DRC2_SIG_DET_STS_MASK 0x0400 /* DRC2_SIG_DET_STS */ +#define ARIZONA_DRC2_SIG_DET_STS_SHIFT 10 /* DRC2_SIG_DET_STS */ +#define ARIZONA_DRC2_SIG_DET_STS_WIDTH 1 /* DRC2_SIG_DET_STS */ +#define ARIZONA_DRC1_SIG_DET_STS 0x0200 /* DRC1_SIG_DET_STS */ +#define ARIZONA_DRC1_SIG_DET_STS_MASK 0x0200 /* DRC1_SIG_DET_STS */ +#define ARIZONA_DRC1_SIG_DET_STS_SHIFT 9 /* DRC1_SIG_DET_STS */ +#define ARIZONA_DRC1_SIG_DET_STS_WIDTH 1 /* DRC1_SIG_DET_STS */ +#define ARIZONA_ASRC2_LOCK_STS 0x0100 /* ASRC2_LOCK_STS */ +#define ARIZONA_ASRC2_LOCK_STS_MASK 0x0100 /* ASRC2_LOCK_STS */ +#define ARIZONA_ASRC2_LOCK_STS_SHIFT 8 /* ASRC2_LOCK_STS */ +#define ARIZONA_ASRC2_LOCK_STS_WIDTH 1 /* ASRC2_LOCK_STS */ +#define ARIZONA_ASRC1_LOCK_STS 0x0080 /* ASRC1_LOCK_STS */ +#define ARIZONA_ASRC1_LOCK_STS_MASK 0x0080 /* ASRC1_LOCK_STS */ +#define ARIZONA_ASRC1_LOCK_STS_SHIFT 7 /* ASRC1_LOCK_STS */ +#define ARIZONA_ASRC1_LOCK_STS_WIDTH 1 /* ASRC1_LOCK_STS */ +#define ARIZONA_UNDERCLOCKED_STS 0x0040 /* UNDERCLOCKED_STS */ +#define ARIZONA_UNDERCLOCKED_STS_MASK 0x0040 /* UNDERCLOCKED_STS */ +#define ARIZONA_UNDERCLOCKED_STS_SHIFT 6 /* UNDERCLOCKED_STS */ +#define ARIZONA_UNDERCLOCKED_STS_WIDTH 1 /* UNDERCLOCKED_STS */ +#define ARIZONA_OVERCLOCKED_STS 0x0020 /* OVERCLOCKED_STS */ +#define ARIZONA_OVERCLOCKED_STS_MASK 0x0020 /* OVERCLOCKED_STS */ +#define ARIZONA_OVERCLOCKED_STS_SHIFT 5 /* OVERCLOCKED_STS */ +#define ARIZONA_OVERCLOCKED_STS_WIDTH 1 /* OVERCLOCKED_STS */ +#define ARIZONA_FLL2_LOCK_STS 0x0008 /* FLL2_LOCK_STS */ +#define ARIZONA_FLL2_LOCK_STS_MASK 0x0008 /* FLL2_LOCK_STS */ +#define ARIZONA_FLL2_LOCK_STS_SHIFT 3 /* FLL2_LOCK_STS */ +#define ARIZONA_FLL2_LOCK_STS_WIDTH 1 /* FLL2_LOCK_STS */ +#define ARIZONA_FLL1_LOCK_STS 0x0004 /* FLL1_LOCK_STS */ +#define ARIZONA_FLL1_LOCK_STS_MASK 0x0004 /* FLL1_LOCK_STS */ +#define ARIZONA_FLL1_LOCK_STS_SHIFT 2 /* FLL1_LOCK_STS */ +#define ARIZONA_FLL1_LOCK_STS_WIDTH 1 /* FLL1_LOCK_STS */ +#define ARIZONA_CLKGEN_ERR_STS 0x0002 /* CLKGEN_ERR_STS */ +#define ARIZONA_CLKGEN_ERR_STS_MASK 0x0002 /* CLKGEN_ERR_STS */ +#define ARIZONA_CLKGEN_ERR_STS_SHIFT 1 /* CLKGEN_ERR_STS */ +#define ARIZONA_CLKGEN_ERR_STS_WIDTH 1 /* CLKGEN_ERR_STS */ +#define ARIZONA_CLKGEN_ERR_ASYNC_STS 0x0001 /* CLKGEN_ERR_ASYNC_STS */ +#define ARIZONA_CLKGEN_ERR_ASYNC_STS_MASK 0x0001 /* CLKGEN_ERR_ASYNC_STS */ +#define ARIZONA_CLKGEN_ERR_ASYNC_STS_SHIFT 0 /* CLKGEN_ERR_ASYNC_STS */ +#define ARIZONA_CLKGEN_ERR_ASYNC_STS_WIDTH 1 /* CLKGEN_ERR_ASYNC_STS */ + +/* + * R3362 (0xD22) - Interrupt Raw Status 4 + */ +#define ARIZONA_ASRC_CFG_ERR_STS 0x8000 /* ASRC_CFG_ERR_STS */ +#define ARIZONA_ASRC_CFG_ERR_STS_MASK 0x8000 /* ASRC_CFG_ERR_STS */ +#define ARIZONA_ASRC_CFG_ERR_STS_SHIFT 15 /* ASRC_CFG_ERR_STS */ +#define ARIZONA_ASRC_CFG_ERR_STS_WIDTH 1 /* ASRC_CFG_ERR_STS */ +#define ARIZONA_AIF3_ERR_STS 0x4000 /* AIF3_ERR_STS */ +#define ARIZONA_AIF3_ERR_STS_MASK 0x4000 /* AIF3_ERR_STS */ +#define ARIZONA_AIF3_ERR_STS_SHIFT 14 /* AIF3_ERR_STS */ +#define ARIZONA_AIF3_ERR_STS_WIDTH 1 /* AIF3_ERR_STS */ +#define ARIZONA_AIF2_ERR_STS 0x2000 /* AIF2_ERR_STS */ +#define ARIZONA_AIF2_ERR_STS_MASK 0x2000 /* AIF2_ERR_STS */ +#define ARIZONA_AIF2_ERR_STS_SHIFT 13 /* AIF2_ERR_STS */ +#define ARIZONA_AIF2_ERR_STS_WIDTH 1 /* AIF2_ERR_STS */ +#define ARIZONA_AIF1_ERR_STS 0x1000 /* AIF1_ERR_STS */ +#define ARIZONA_AIF1_ERR_STS_MASK 0x1000 /* AIF1_ERR_STS */ +#define ARIZONA_AIF1_ERR_STS_SHIFT 12 /* AIF1_ERR_STS */ +#define ARIZONA_AIF1_ERR_STS_WIDTH 1 /* AIF1_ERR_STS */ +#define ARIZONA_CTRLIF_ERR_STS 0x0800 /* CTRLIF_ERR_STS */ +#define ARIZONA_CTRLIF_ERR_STS_MASK 0x0800 /* CTRLIF_ERR_STS */ +#define ARIZONA_CTRLIF_ERR_STS_SHIFT 11 /* CTRLIF_ERR_STS */ +#define ARIZONA_CTRLIF_ERR_STS_WIDTH 1 /* CTRLIF_ERR_STS */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_STS 0x0400 /* MIXER_DROPPED_SAMPLE_STS */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_STS_MASK 0x0400 /* MIXER_DROPPED_SAMPLE_STS */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_STS_SHIFT 10 /* MIXER_DROPPED_SAMPLE_STS */ +#define ARIZONA_MIXER_DROPPED_SAMPLE_STS_WIDTH 1 /* MIXER_DROPPED_SAMPLE_STS */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_STS 0x0200 /* ASYNC_CLK_ENA_LOW_STS */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_STS_MASK 0x0200 /* ASYNC_CLK_ENA_LOW_STS */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_STS_SHIFT 9 /* ASYNC_CLK_ENA_LOW_STS */ +#define ARIZONA_ASYNC_CLK_ENA_LOW_STS_WIDTH 1 /* ASYNC_CLK_ENA_LOW_STS */ +#define ARIZONA_SYSCLK_ENA_LOW_STS 0x0100 /* SYSCLK_ENA_LOW_STS */ +#define ARIZONA_SYSCLK_ENA_LOW_STS_MASK 0x0100 /* SYSCLK_ENA_LOW_STS */ +#define ARIZONA_SYSCLK_ENA_LOW_STS_SHIFT 8 /* SYSCLK_ENA_LOW_STS */ +#define ARIZONA_SYSCLK_ENA_LOW_STS_WIDTH 1 /* SYSCLK_ENA_LOW_STS */ +#define ARIZONA_ISRC1_CFG_ERR_STS 0x0080 /* ISRC1_CFG_ERR_STS */ +#define ARIZONA_ISRC1_CFG_ERR_STS_MASK 0x0080 /* ISRC1_CFG_ERR_STS */ +#define ARIZONA_ISRC1_CFG_ERR_STS_SHIFT 7 /* ISRC1_CFG_ERR_STS */ +#define ARIZONA_ISRC1_CFG_ERR_STS_WIDTH 1 /* ISRC1_CFG_ERR_STS */ +#define ARIZONA_ISRC2_CFG_ERR_STS 0x0040 /* ISRC2_CFG_ERR_STS */ +#define ARIZONA_ISRC2_CFG_ERR_STS_MASK 0x0040 /* ISRC2_CFG_ERR_STS */ +#define ARIZONA_ISRC2_CFG_ERR_STS_SHIFT 6 /* ISRC2_CFG_ERR_STS */ +#define ARIZONA_ISRC2_CFG_ERR_STS_WIDTH 1 /* ISRC2_CFG_ERR_STS */ + +/* + * R3363 (0xD23) - Interrupt Raw Status 5 + */ +#define ARIZONA_BOOT_DONE_STS 0x0100 /* BOOT_DONE_STS */ +#define ARIZONA_BOOT_DONE_STS_MASK 0x0100 /* BOOT_DONE_STS */ +#define ARIZONA_BOOT_DONE_STS_SHIFT 8 /* BOOT_DONE_STS */ +#define ARIZONA_BOOT_DONE_STS_WIDTH 1 /* BOOT_DONE_STS */ +#define ARIZONA_DCS_DAC_DONE_STS 0x0080 /* DCS_DAC_DONE_STS */ +#define ARIZONA_DCS_DAC_DONE_STS_MASK 0x0080 /* DCS_DAC_DONE_STS */ +#define ARIZONA_DCS_DAC_DONE_STS_SHIFT 7 /* DCS_DAC_DONE_STS */ +#define ARIZONA_DCS_DAC_DONE_STS_WIDTH 1 /* DCS_DAC_DONE_STS */ +#define ARIZONA_DCS_HP_DONE_STS 0x0040 /* DCS_HP_DONE_STS */ +#define ARIZONA_DCS_HP_DONE_STS_MASK 0x0040 /* DCS_HP_DONE_STS */ +#define ARIZONA_DCS_HP_DONE_STS_SHIFT 6 /* DCS_HP_DONE_STS */ +#define ARIZONA_DCS_HP_DONE_STS_WIDTH 1 /* DCS_HP_DONE_STS */ +#define ARIZONA_FLL2_CLOCK_OK_STS 0x0002 /* FLL2_CLOCK_OK_STS */ +#define ARIZONA_FLL2_CLOCK_OK_STS_MASK 0x0002 /* FLL2_CLOCK_OK_STS */ +#define ARIZONA_FLL2_CLOCK_OK_STS_SHIFT 1 /* FLL2_CLOCK_OK_STS */ +#define ARIZONA_FLL2_CLOCK_OK_STS_WIDTH 1 /* FLL2_CLOCK_OK_STS */ +#define ARIZONA_FLL1_CLOCK_OK_STS 0x0001 /* FLL1_CLOCK_OK_STS */ +#define ARIZONA_FLL1_CLOCK_OK_STS_MASK 0x0001 /* FLL1_CLOCK_OK_STS */ +#define ARIZONA_FLL1_CLOCK_OK_STS_SHIFT 0 /* FLL1_CLOCK_OK_STS */ +#define ARIZONA_FLL1_CLOCK_OK_STS_WIDTH 1 /* FLL1_CLOCK_OK_STS */ + +/* + * R3364 (0xD24) - Interrupt Raw Status 6 + */ +#define ARIZONA_PWM_OVERCLOCKED_STS 0x2000 /* PWM_OVERCLOCKED_STS */ +#define ARIZONA_PWM_OVERCLOCKED_STS_MASK 0x2000 /* PWM_OVERCLOCKED_STS */ +#define ARIZONA_PWM_OVERCLOCKED_STS_SHIFT 13 /* PWM_OVERCLOCKED_STS */ +#define ARIZONA_PWM_OVERCLOCKED_STS_WIDTH 1 /* PWM_OVERCLOCKED_STS */ +#define ARIZONA_FX_CORE_OVERCLOCKED_STS 0x1000 /* FX_CORE_OVERCLOCKED_STS */ +#define ARIZONA_FX_CORE_OVERCLOCKED_STS_MASK 0x1000 /* FX_CORE_OVERCLOCKED_STS */ +#define ARIZONA_FX_CORE_OVERCLOCKED_STS_SHIFT 12 /* FX_CORE_OVERCLOCKED_STS */ +#define ARIZONA_FX_CORE_OVERCLOCKED_STS_WIDTH 1 /* FX_CORE_OVERCLOCKED_STS */ +#define ARIZONA_DAC_SYS_OVERCLOCKED_STS 0x0400 /* DAC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_DAC_SYS_OVERCLOCKED_STS_MASK 0x0400 /* DAC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_DAC_SYS_OVERCLOCKED_STS_SHIFT 10 /* DAC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_DAC_SYS_OVERCLOCKED_STS_WIDTH 1 /* DAC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_DAC_WARP_OVERCLOCKED_STS 0x0200 /* DAC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_DAC_WARP_OVERCLOCKED_STS_MASK 0x0200 /* DAC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_DAC_WARP_OVERCLOCKED_STS_SHIFT 9 /* DAC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_DAC_WARP_OVERCLOCKED_STS_WIDTH 1 /* DAC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ADC_OVERCLOCKED_STS 0x0100 /* ADC_OVERCLOCKED_STS */ +#define ARIZONA_ADC_OVERCLOCKED_STS_MASK 0x0100 /* ADC_OVERCLOCKED_STS */ +#define ARIZONA_ADC_OVERCLOCKED_STS_SHIFT 8 /* ADC_OVERCLOCKED_STS */ +#define ARIZONA_ADC_OVERCLOCKED_STS_WIDTH 1 /* ADC_OVERCLOCKED_STS */ +#define ARIZONA_MIXER_OVERCLOCKED_STS 0x0080 /* MIXER_OVERCLOCKED_STS */ +#define ARIZONA_MIXER_OVERCLOCKED_STS_MASK 0x0080 /* MIXER_OVERCLOCKED_STS */ +#define ARIZONA_MIXER_OVERCLOCKED_STS_SHIFT 7 /* MIXER_OVERCLOCKED_STS */ +#define ARIZONA_MIXER_OVERCLOCKED_STS_WIDTH 1 /* MIXER_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_ASYNC_OVERCLOCKED_STS 0x0040 /* AIF3_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_ASYNC_OVERCLOCKED_STS_MASK 0x0040 /* AIF3_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_ASYNC_OVERCLOCKED_STS_SHIFT 6 /* AIF3_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_ASYNC_OVERCLOCKED_STS_WIDTH 1 /* AIF3_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_ASYNC_OVERCLOCKED_STS 0x0020 /* AIF2_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_ASYNC_OVERCLOCKED_STS_MASK 0x0020 /* AIF2_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_ASYNC_OVERCLOCKED_STS_SHIFT 5 /* AIF2_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_ASYNC_OVERCLOCKED_STS_WIDTH 1 /* AIF2_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_ASYNC_OVERCLOCKED_STS 0x0010 /* AIF1_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_ASYNC_OVERCLOCKED_STS_MASK 0x0010 /* AIF1_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_ASYNC_OVERCLOCKED_STS_SHIFT 4 /* AIF1_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_ASYNC_OVERCLOCKED_STS_WIDTH 1 /* AIF1_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_SYNC_OVERCLOCKED_STS 0x0008 /* AIF3_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_SYNC_OVERCLOCKED_STS_MASK 0x0008 /* AIF3_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_SYNC_OVERCLOCKED_STS_SHIFT 3 /* AIF3_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF3_SYNC_OVERCLOCKED_STS_WIDTH 1 /* AIF3_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_SYNC_OVERCLOCKED_STS 0x0004 /* AIF2_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_SYNC_OVERCLOCKED_STS_MASK 0x0004 /* AIF2_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_SYNC_OVERCLOCKED_STS_SHIFT 2 /* AIF2_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF2_SYNC_OVERCLOCKED_STS_WIDTH 1 /* AIF2_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_SYNC_OVERCLOCKED_STS 0x0002 /* AIF1_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_SYNC_OVERCLOCKED_STS_MASK 0x0002 /* AIF1_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_SYNC_OVERCLOCKED_STS_SHIFT 1 /* AIF1_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_AIF1_SYNC_OVERCLOCKED_STS_WIDTH 1 /* AIF1_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_PAD_CTRL_OVERCLOCKED_STS 0x0001 /* PAD_CTRL_OVERCLOCKED_STS */ +#define ARIZONA_PAD_CTRL_OVERCLOCKED_STS_MASK 0x0001 /* PAD_CTRL_OVERCLOCKED_STS */ +#define ARIZONA_PAD_CTRL_OVERCLOCKED_STS_SHIFT 0 /* PAD_CTRL_OVERCLOCKED_STS */ +#define ARIZONA_PAD_CTRL_OVERCLOCKED_STS_WIDTH 1 /* PAD_CTRL_OVERCLOCKED_STS */ + +/* + * R3365 (0xD25) - Interrupt Raw Status 7 + */ +#define ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS 0x8000 /* SLIMBUS_SUBSYS_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS_MASK 0x8000 /* SLIMBUS_SUBSYS_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS_SHIFT 15 /* SLIMBUS_SUBSYS_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS_WIDTH 1 /* SLIMBUS_SUBSYS_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS 0x4000 /* SLIMBUS_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS_MASK 0x4000 /* SLIMBUS_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS_SHIFT 14 /* SLIMBUS_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS_WIDTH 1 /* SLIMBUS_ASYNC_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS 0x2000 /* SLIMBUS_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS_MASK 0x2000 /* SLIMBUS_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS_SHIFT 13 /* SLIMBUS_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS_WIDTH 1 /* SLIMBUS_SYNC_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS 0x1000 /* ASRC_ASYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS_MASK 0x1000 /* ASRC_ASYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS_SHIFT 12 /* ASRC_ASYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS_WIDTH 1 /* ASRC_ASYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS 0x0800 /* ASRC_ASYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS_MASK 0x0800 /* ASRC_ASYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS_SHIFT 11 /* ASRC_ASYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS_WIDTH 1 /* ASRC_ASYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS 0x0400 /* ASRC_SYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS_MASK 0x0400 /* ASRC_SYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS_SHIFT 10 /* ASRC_SYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS_WIDTH 1 /* ASRC_SYNC_SYS_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS 0x0200 /* ASRC_SYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS_MASK 0x0200 /* ASRC_SYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS_SHIFT 9 /* ASRC_SYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS_WIDTH 1 /* ASRC_SYNC_WARP_OVERCLOCKED_STS */ +#define ARIZONA_ADSP2_1_OVERCLOCKED_STS 0x0008 /* ADSP2_1_OVERCLOCKED_STS */ +#define ARIZONA_ADSP2_1_OVERCLOCKED_STS_MASK 0x0008 /* ADSP2_1_OVERCLOCKED_STS */ +#define ARIZONA_ADSP2_1_OVERCLOCKED_STS_SHIFT 3 /* ADSP2_1_OVERCLOCKED_STS */ +#define ARIZONA_ADSP2_1_OVERCLOCKED_STS_WIDTH 1 /* ADSP2_1_OVERCLOCKED_STS */ +#define ARIZONA_ISRC2_OVERCLOCKED_STS 0x0002 /* ISRC2_OVERCLOCKED_STS */ +#define ARIZONA_ISRC2_OVERCLOCKED_STS_MASK 0x0002 /* ISRC2_OVERCLOCKED_STS */ +#define ARIZONA_ISRC2_OVERCLOCKED_STS_SHIFT 1 /* ISRC2_OVERCLOCKED_STS */ +#define ARIZONA_ISRC2_OVERCLOCKED_STS_WIDTH 1 /* ISRC2_OVERCLOCKED_STS */ +#define ARIZONA_ISRC1_OVERCLOCKED_STS 0x0001 /* ISRC1_OVERCLOCKED_STS */ +#define ARIZONA_ISRC1_OVERCLOCKED_STS_MASK 0x0001 /* ISRC1_OVERCLOCKED_STS */ +#define ARIZONA_ISRC1_OVERCLOCKED_STS_SHIFT 0 /* ISRC1_OVERCLOCKED_STS */ +#define ARIZONA_ISRC1_OVERCLOCKED_STS_WIDTH 1 /* ISRC1_OVERCLOCKED_STS */ + +/* + * R3366 (0xD26) - Interrupt Raw Status 8 + */ +#define ARIZONA_AIF3_UNDERCLOCKED_STS 0x0400 /* AIF3_UNDERCLOCKED_STS */ +#define ARIZONA_AIF3_UNDERCLOCKED_STS_MASK 0x0400 /* AIF3_UNDERCLOCKED_STS */ +#define ARIZONA_AIF3_UNDERCLOCKED_STS_SHIFT 10 /* AIF3_UNDERCLOCKED_STS */ +#define ARIZONA_AIF3_UNDERCLOCKED_STS_WIDTH 1 /* AIF3_UNDERCLOCKED_STS */ +#define ARIZONA_AIF2_UNDERCLOCKED_STS 0x0200 /* AIF2_UNDERCLOCKED_STS */ +#define ARIZONA_AIF2_UNDERCLOCKED_STS_MASK 0x0200 /* AIF2_UNDERCLOCKED_STS */ +#define ARIZONA_AIF2_UNDERCLOCKED_STS_SHIFT 9 /* AIF2_UNDERCLOCKED_STS */ +#define ARIZONA_AIF2_UNDERCLOCKED_STS_WIDTH 1 /* AIF2_UNDERCLOCKED_STS */ +#define ARIZONA_AIF1_UNDERCLOCKED_STS 0x0100 /* AIF1_UNDERCLOCKED_STS */ +#define ARIZONA_AIF1_UNDERCLOCKED_STS_MASK 0x0100 /* AIF1_UNDERCLOCKED_STS */ +#define ARIZONA_AIF1_UNDERCLOCKED_STS_SHIFT 8 /* AIF1_UNDERCLOCKED_STS */ +#define ARIZONA_AIF1_UNDERCLOCKED_STS_WIDTH 1 /* AIF1_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC2_UNDERCLOCKED_STS 0x0040 /* ISRC2_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC2_UNDERCLOCKED_STS_MASK 0x0040 /* ISRC2_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC2_UNDERCLOCKED_STS_SHIFT 6 /* ISRC2_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC2_UNDERCLOCKED_STS_WIDTH 1 /* ISRC2_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC1_UNDERCLOCKED_STS 0x0020 /* ISRC1_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC1_UNDERCLOCKED_STS_MASK 0x0020 /* ISRC1_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC1_UNDERCLOCKED_STS_SHIFT 5 /* ISRC1_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC1_UNDERCLOCKED_STS_WIDTH 1 /* ISRC1_UNDERCLOCKED_STS */ +#define ARIZONA_FX_UNDERCLOCKED_STS 0x0010 /* FX_UNDERCLOCKED_STS */ +#define ARIZONA_FX_UNDERCLOCKED_STS_MASK 0x0010 /* FX_UNDERCLOCKED_STS */ +#define ARIZONA_FX_UNDERCLOCKED_STS_SHIFT 4 /* FX_UNDERCLOCKED_STS */ +#define ARIZONA_FX_UNDERCLOCKED_STS_WIDTH 1 /* FX_UNDERCLOCKED_STS */ +#define ARIZONA_ASRC_UNDERCLOCKED_STS 0x0008 /* ASRC_UNDERCLOCKED_STS */ +#define ARIZONA_ASRC_UNDERCLOCKED_STS_MASK 0x0008 /* ASRC_UNDERCLOCKED_STS */ +#define ARIZONA_ASRC_UNDERCLOCKED_STS_SHIFT 3 /* ASRC_UNDERCLOCKED_STS */ +#define ARIZONA_ASRC_UNDERCLOCKED_STS_WIDTH 1 /* ASRC_UNDERCLOCKED_STS */ +#define ARIZONA_DAC_UNDERCLOCKED_STS 0x0004 /* DAC_UNDERCLOCKED_STS */ +#define ARIZONA_DAC_UNDERCLOCKED_STS_MASK 0x0004 /* DAC_UNDERCLOCKED_STS */ +#define ARIZONA_DAC_UNDERCLOCKED_STS_SHIFT 2 /* DAC_UNDERCLOCKED_STS */ +#define ARIZONA_DAC_UNDERCLOCKED_STS_WIDTH 1 /* DAC_UNDERCLOCKED_STS */ +#define ARIZONA_ADC_UNDERCLOCKED_STS 0x0002 /* ADC_UNDERCLOCKED_STS */ +#define ARIZONA_ADC_UNDERCLOCKED_STS_MASK 0x0002 /* ADC_UNDERCLOCKED_STS */ +#define ARIZONA_ADC_UNDERCLOCKED_STS_SHIFT 1 /* ADC_UNDERCLOCKED_STS */ +#define ARIZONA_ADC_UNDERCLOCKED_STS_WIDTH 1 /* ADC_UNDERCLOCKED_STS */ +#define ARIZONA_MIXER_UNDERCLOCKED_STS 0x0001 /* MIXER_UNDERCLOCKED_STS */ +#define ARIZONA_MIXER_UNDERCLOCKED_STS_MASK 0x0001 /* MIXER_UNDERCLOCKED_STS */ +#define ARIZONA_MIXER_UNDERCLOCKED_STS_SHIFT 0 /* MIXER_UNDERCLOCKED_STS */ +#define ARIZONA_MIXER_UNDERCLOCKED_STS_WIDTH 1 /* MIXER_UNDERCLOCKED_STS */ + +/* + * R3392 (0xD40) - IRQ Pin Status + */ +#define ARIZONA_IRQ2_STS 0x0002 /* IRQ2_STS */ +#define ARIZONA_IRQ2_STS_MASK 0x0002 /* IRQ2_STS */ +#define ARIZONA_IRQ2_STS_SHIFT 1 /* IRQ2_STS */ +#define ARIZONA_IRQ2_STS_WIDTH 1 /* IRQ2_STS */ +#define ARIZONA_IRQ1_STS 0x0001 /* IRQ1_STS */ +#define ARIZONA_IRQ1_STS_MASK 0x0001 /* IRQ1_STS */ +#define ARIZONA_IRQ1_STS_SHIFT 0 /* IRQ1_STS */ +#define ARIZONA_IRQ1_STS_WIDTH 1 /* IRQ1_STS */ + +/* + * R3393 (0xD41) - ADSP2 IRQ0 + */ +#define ARIZONA_DSP_IRQ2 0x0002 /* DSP_IRQ2 */ +#define ARIZONA_DSP_IRQ2_MASK 0x0002 /* DSP_IRQ2 */ +#define ARIZONA_DSP_IRQ2_SHIFT 1 /* DSP_IRQ2 */ +#define ARIZONA_DSP_IRQ2_WIDTH 1 /* DSP_IRQ2 */ +#define ARIZONA_DSP_IRQ1 0x0001 /* DSP_IRQ1 */ +#define ARIZONA_DSP_IRQ1_MASK 0x0001 /* DSP_IRQ1 */ +#define ARIZONA_DSP_IRQ1_SHIFT 0 /* DSP_IRQ1 */ +#define ARIZONA_DSP_IRQ1_WIDTH 1 /* DSP_IRQ1 */ + +/* + * R3408 (0xD50) - AOD wkup and trig + */ +#define ARIZONA_GP5_FALL_TRIG_STS 0x0020 /* GP5_FALL_TRIG_STS */ +#define ARIZONA_GP5_FALL_TRIG_STS_MASK 0x0020 /* GP5_FALL_TRIG_STS */ +#define ARIZONA_GP5_FALL_TRIG_STS_SHIFT 5 /* GP5_FALL_TRIG_STS */ +#define ARIZONA_GP5_FALL_TRIG_STS_WIDTH 1 /* GP5_FALL_TRIG_STS */ +#define ARIZONA_GP5_RISE_TRIG_STS 0x0010 /* GP5_RISE_TRIG_STS */ +#define ARIZONA_GP5_RISE_TRIG_STS_MASK 0x0010 /* GP5_RISE_TRIG_STS */ +#define ARIZONA_GP5_RISE_TRIG_STS_SHIFT 4 /* GP5_RISE_TRIG_STS */ +#define ARIZONA_GP5_RISE_TRIG_STS_WIDTH 1 /* GP5_RISE_TRIG_STS */ +#define ARIZONA_JD1_FALL_TRIG_STS 0x0008 /* JD1_FALL_TRIG_STS */ +#define ARIZONA_JD1_FALL_TRIG_STS_MASK 0x0008 /* JD1_FALL_TRIG_STS */ +#define ARIZONA_JD1_FALL_TRIG_STS_SHIFT 3 /* JD1_FALL_TRIG_STS */ +#define ARIZONA_JD1_FALL_TRIG_STS_WIDTH 1 /* JD1_FALL_TRIG_STS */ +#define ARIZONA_JD1_RISE_TRIG_STS 0x0004 /* JD1_RISE_TRIG_STS */ +#define ARIZONA_JD1_RISE_TRIG_STS_MASK 0x0004 /* JD1_RISE_TRIG_STS */ +#define ARIZONA_JD1_RISE_TRIG_STS_SHIFT 2 /* JD1_RISE_TRIG_STS */ +#define ARIZONA_JD1_RISE_TRIG_STS_WIDTH 1 /* JD1_RISE_TRIG_STS */ +#define ARIZONA_JD2_FALL_TRIG_STS 0x0002 /* JD2_FALL_TRIG_STS */ +#define ARIZONA_JD2_FALL_TRIG_STS_MASK 0x0002 /* JD2_FALL_TRIG_STS */ +#define ARIZONA_JD2_FALL_TRIG_STS_SHIFT 1 /* JD2_FALL_TRIG_STS */ +#define ARIZONA_JD2_FALL_TRIG_STS_WIDTH 1 /* JD2_FALL_TRIG_STS */ +#define ARIZONA_JD2_RISE_TRIG_STS 0x0001 /* JD2_RISE_TRIG_STS */ +#define ARIZONA_JD2_RISE_TRIG_STS_MASK 0x0001 /* JD2_RISE_TRIG_STS */ +#define ARIZONA_JD2_RISE_TRIG_STS_SHIFT 0 /* JD2_RISE_TRIG_STS */ +#define ARIZONA_JD2_RISE_TRIG_STS_WIDTH 1 /* JD2_RISE_TRIG_STS */ + +/* + * R3409 (0xD51) - AOD IRQ1 + */ +#define ARIZONA_GP5_FALL_EINT1 0x0020 /* GP5_FALL_EINT1 */ +#define ARIZONA_GP5_FALL_EINT1_MASK 0x0020 /* GP5_FALL_EINT1 */ +#define ARIZONA_GP5_FALL_EINT1_SHIFT 5 /* GP5_FALL_EINT1 */ +#define ARIZONA_GP5_FALL_EINT1_WIDTH 1 /* GP5_FALL_EINT1 */ +#define ARIZONA_GP5_RISE_EINT1 0x0010 /* GP5_RISE_EINT1 */ +#define ARIZONA_GP5_RISE_EINT1_MASK 0x0010 /* GP5_RISE_EINT1 */ +#define ARIZONA_GP5_RISE_EINT1_SHIFT 4 /* GP5_RISE_EINT1 */ +#define ARIZONA_GP5_RISE_EINT1_WIDTH 1 /* GP5_RISE_EINT1 */ +#define ARIZONA_JD1_FALL_EINT1 0x0008 /* JD1_FALL_EINT1 */ +#define ARIZONA_JD1_FALL_EINT1_MASK 0x0008 /* JD1_FALL_EINT1 */ +#define ARIZONA_JD1_FALL_EINT1_SHIFT 3 /* JD1_FALL_EINT1 */ +#define ARIZONA_JD1_FALL_EINT1_WIDTH 1 /* JD1_FALL_EINT1 */ +#define ARIZONA_JD1_RISE_EINT1 0x0004 /* JD1_RISE_EINT1 */ +#define ARIZONA_JD1_RISE_EINT1_MASK 0x0004 /* JD1_RISE_EINT1 */ +#define ARIZONA_JD1_RISE_EINT1_SHIFT 2 /* JD1_RISE_EINT1 */ +#define ARIZONA_JD1_RISE_EINT1_WIDTH 1 /* JD1_RISE_EINT1 */ +#define ARIZONA_JD2_FALL_EINT1 0x0002 /* JD2_FALL_EINT1 */ +#define ARIZONA_JD2_FALL_EINT1_MASK 0x0002 /* JD2_FALL_EINT1 */ +#define ARIZONA_JD2_FALL_EINT1_SHIFT 1 /* JD2_FALL_EINT1 */ +#define ARIZONA_JD2_FALL_EINT1_WIDTH 1 /* JD2_FALL_EINT1 */ +#define ARIZONA_JD2_RISE_EINT1 0x0001 /* JD2_RISE_EINT1 */ +#define ARIZONA_JD2_RISE_EINT1_MASK 0x0001 /* JD2_RISE_EINT1 */ +#define ARIZONA_JD2_RISE_EINT1_SHIFT 0 /* JD2_RISE_EINT1 */ +#define ARIZONA_JD2_RISE_EINT1_WIDTH 1 /* JD2_RISE_EINT1 */ + +/* + * R3410 (0xD52) - AOD IRQ2 + */ +#define ARIZONA_GP5_FALL_EINT2 0x0020 /* GP5_FALL_EINT2 */ +#define ARIZONA_GP5_FALL_EINT2_MASK 0x0020 /* GP5_FALL_EINT2 */ +#define ARIZONA_GP5_FALL_EINT2_SHIFT 5 /* GP5_FALL_EINT2 */ +#define ARIZONA_GP5_FALL_EINT2_WIDTH 1 /* GP5_FALL_EINT2 */ +#define ARIZONA_GP5_RISE_EINT2 0x0010 /* GP5_RISE_EINT2 */ +#define ARIZONA_GP5_RISE_EINT2_MASK 0x0010 /* GP5_RISE_EINT2 */ +#define ARIZONA_GP5_RISE_EINT2_SHIFT 4 /* GP5_RISE_EINT2 */ +#define ARIZONA_GP5_RISE_EINT2_WIDTH 1 /* GP5_RISE_EINT2 */ +#define ARIZONA_JD1_FALL_EINT2 0x0008 /* JD1_FALL_EINT2 */ +#define ARIZONA_JD1_FALL_EINT2_MASK 0x0008 /* JD1_FALL_EINT2 */ +#define ARIZONA_JD1_FALL_EINT2_SHIFT 3 /* JD1_FALL_EINT2 */ +#define ARIZONA_JD1_FALL_EINT2_WIDTH 1 /* JD1_FALL_EINT2 */ +#define ARIZONA_JD1_RISE_EINT2 0x0004 /* JD1_RISE_EINT2 */ +#define ARIZONA_JD1_RISE_EINT2_MASK 0x0004 /* JD1_RISE_EINT2 */ +#define ARIZONA_JD1_RISE_EINT2_SHIFT 2 /* JD1_RISE_EINT2 */ +#define ARIZONA_JD1_RISE_EINT2_WIDTH 1 /* JD1_RISE_EINT2 */ +#define ARIZONA_JD2_FALL_EINT2 0x0002 /* JD2_FALL_EINT2 */ +#define ARIZONA_JD2_FALL_EINT2_MASK 0x0002 /* JD2_FALL_EINT2 */ +#define ARIZONA_JD2_FALL_EINT2_SHIFT 1 /* JD2_FALL_EINT2 */ +#define ARIZONA_JD2_FALL_EINT2_WIDTH 1 /* JD2_FALL_EINT2 */ +#define ARIZONA_JD2_RISE_EINT2 0x0001 /* JD2_RISE_EINT2 */ +#define ARIZONA_JD2_RISE_EINT2_MASK 0x0001 /* JD2_RISE_EINT2 */ +#define ARIZONA_JD2_RISE_EINT2_SHIFT 0 /* JD2_RISE_EINT2 */ +#define ARIZONA_JD2_RISE_EINT2_WIDTH 1 /* JD2_RISE_EINT2 */ + +/* + * R3411 (0xD53) - AOD IRQ Mask IRQ1 + */ +#define ARIZONA_IM_GP5_FALL_EINT1 0x0020 /* IM_GP5_FALL_EINT1 */ +#define ARIZONA_IM_GP5_FALL_EINT1_MASK 0x0020 /* IM_GP5_FALL_EINT1 */ +#define ARIZONA_IM_GP5_FALL_EINT1_SHIFT 5 /* IM_GP5_FALL_EINT1 */ +#define ARIZONA_IM_GP5_FALL_EINT1_WIDTH 1 /* IM_GP5_FALL_EINT1 */ +#define ARIZONA_IM_GP5_RISE_EINT1 0x0010 /* IM_GP5_RISE_EINT1 */ +#define ARIZONA_IM_GP5_RISE_EINT1_MASK 0x0010 /* IM_GP5_RISE_EINT1 */ +#define ARIZONA_IM_GP5_RISE_EINT1_SHIFT 4 /* IM_GP5_RISE_EINT1 */ +#define ARIZONA_IM_GP5_RISE_EINT1_WIDTH 1 /* IM_GP5_RISE_EINT1 */ +#define ARIZONA_IM_JD1_FALL_EINT1 0x0008 /* IM_JD1_FALL_EINT1 */ +#define ARIZONA_IM_JD1_FALL_EINT1_MASK 0x0008 /* IM_JD1_FALL_EINT1 */ +#define ARIZONA_IM_JD1_FALL_EINT1_SHIFT 3 /* IM_JD1_FALL_EINT1 */ +#define ARIZONA_IM_JD1_FALL_EINT1_WIDTH 1 /* IM_JD1_FALL_EINT1 */ +#define ARIZONA_IM_JD1_RISE_EINT1 0x0004 /* IM_JD1_RISE_EINT1 */ +#define ARIZONA_IM_JD1_RISE_EINT1_MASK 0x0004 /* IM_JD1_RISE_EINT1 */ +#define ARIZONA_IM_JD1_RISE_EINT1_SHIFT 2 /* IM_JD1_RISE_EINT1 */ +#define ARIZONA_IM_JD1_RISE_EINT1_WIDTH 1 /* IM_JD1_RISE_EINT1 */ +#define ARIZONA_IM_JD2_FALL_EINT1 0x0002 /* IM_JD2_FALL_EINT1 */ +#define ARIZONA_IM_JD2_FALL_EINT1_MASK 0x0002 /* IM_JD2_FALL_EINT1 */ +#define ARIZONA_IM_JD2_FALL_EINT1_SHIFT 1 /* IM_JD2_FALL_EINT1 */ +#define ARIZONA_IM_JD2_FALL_EINT1_WIDTH 1 /* IM_JD2_FALL_EINT1 */ +#define ARIZONA_IM_JD2_RISE_EINT1 0x0001 /* IM_JD2_RISE_EINT1 */ +#define ARIZONA_IM_JD2_RISE_EINT1_MASK 0x0001 /* IM_JD2_RISE_EINT1 */ +#define ARIZONA_IM_JD2_RISE_EINT1_SHIFT 0 /* IM_JD2_RISE_EINT1 */ +#define ARIZONA_IM_JD2_RISE_EINT1_WIDTH 1 /* IM_JD2_RISE_EINT1 */ + +/* + * R3412 (0xD54) - AOD IRQ Mask IRQ2 + */ +#define ARIZONA_IM_GP5_FALL_EINT2 0x0020 /* IM_GP5_FALL_EINT2 */ +#define ARIZONA_IM_GP5_FALL_EINT2_MASK 0x0020 /* IM_GP5_FALL_EINT2 */ +#define ARIZONA_IM_GP5_FALL_EINT2_SHIFT 5 /* IM_GP5_FALL_EINT2 */ +#define ARIZONA_IM_GP5_FALL_EINT2_WIDTH 1 /* IM_GP5_FALL_EINT2 */ +#define ARIZONA_IM_GP5_RISE_EINT2 0x0010 /* IM_GP5_RISE_EINT2 */ +#define ARIZONA_IM_GP5_RISE_EINT2_MASK 0x0010 /* IM_GP5_RISE_EINT2 */ +#define ARIZONA_IM_GP5_RISE_EINT2_SHIFT 4 /* IM_GP5_RISE_EINT2 */ +#define ARIZONA_IM_GP5_RISE_EINT2_WIDTH 1 /* IM_GP5_RISE_EINT2 */ +#define ARIZONA_IM_JD1_FALL_EINT2 0x0008 /* IM_JD1_FALL_EINT2 */ +#define ARIZONA_IM_JD1_FALL_EINT2_MASK 0x0008 /* IM_JD1_FALL_EINT2 */ +#define ARIZONA_IM_JD1_FALL_EINT2_SHIFT 3 /* IM_JD1_FALL_EINT2 */ +#define ARIZONA_IM_JD1_FALL_EINT2_WIDTH 1 /* IM_JD1_FALL_EINT2 */ +#define ARIZONA_IM_JD1_RISE_EINT2 0x0004 /* IM_JD1_RISE_EINT2 */ +#define ARIZONA_IM_JD1_RISE_EINT2_MASK 0x0004 /* IM_JD1_RISE_EINT2 */ +#define ARIZONA_IM_JD1_RISE_EINT2_SHIFT 2 /* IM_JD1_RISE_EINT2 */ +#define ARIZONA_IM_JD1_RISE_EINT2_WIDTH 1 /* IM_JD1_RISE_EINT2 */ +#define ARIZONA_IM_JD2_FALL_EINT2 0x0002 /* IM_JD2_FALL_EINT2 */ +#define ARIZONA_IM_JD2_FALL_EINT2_MASK 0x0002 /* IM_JD2_FALL_EINT2 */ +#define ARIZONA_IM_JD2_FALL_EINT2_SHIFT 1 /* IM_JD2_FALL_EINT2 */ +#define ARIZONA_IM_JD2_FALL_EINT2_WIDTH 1 /* IM_JD2_FALL_EINT2 */ +#define ARIZONA_IM_JD2_RISE_EINT2 0x0001 /* IM_JD2_RISE_EINT2 */ +#define ARIZONA_IM_JD2_RISE_EINT2_MASK 0x0001 /* IM_JD2_RISE_EINT2 */ +#define ARIZONA_IM_JD2_RISE_EINT2_SHIFT 0 /* IM_JD2_RISE_EINT2 */ +#define ARIZONA_IM_JD2_RISE_EINT2_WIDTH 1 /* IM_JD2_RISE_EINT2 */ + +/* + * R3413 (0xD55) - AOD IRQ Raw Status + */ +#define ARIZONA_GP5_STS 0x0004 /* GP5_STS */ +#define ARIZONA_GP5_STS_MASK 0x0004 /* GP5_STS */ +#define ARIZONA_GP5_STS_SHIFT 2 /* GP5_STS */ +#define ARIZONA_GP5_STS_WIDTH 1 /* GP5_STS */ +#define ARIZONA_JD2_STS 0x0002 /* JD2_STS */ +#define ARIZONA_JD2_STS_MASK 0x0002 /* JD2_STS */ +#define ARIZONA_JD2_STS_SHIFT 1 /* JD2_STS */ +#define ARIZONA_JD2_STS_WIDTH 1 /* JD2_STS */ +#define ARIZONA_JD1_STS 0x0001 /* JD1_STS */ +#define ARIZONA_JD1_STS_MASK 0x0001 /* JD1_STS */ +#define ARIZONA_JD1_STS_SHIFT 0 /* JD1_STS */ +#define ARIZONA_JD1_STS_WIDTH 1 /* JD1_STS */ + +/* + * R3414 (0xD56) - Jack detect debounce + */ +#define ARIZONA_JD2_DB 0x0002 /* JD2_DB */ +#define ARIZONA_JD2_DB_MASK 0x0002 /* JD2_DB */ +#define ARIZONA_JD2_DB_SHIFT 1 /* JD2_DB */ +#define ARIZONA_JD2_DB_WIDTH 1 /* JD2_DB */ +#define ARIZONA_JD1_DB 0x0001 /* JD1_DB */ +#define ARIZONA_JD1_DB_MASK 0x0001 /* JD1_DB */ +#define ARIZONA_JD1_DB_SHIFT 0 /* JD1_DB */ +#define ARIZONA_JD1_DB_WIDTH 1 /* JD1_DB */ + +/* + * R3584 (0xE00) - FX_Ctrl1 + */ +#define ARIZONA_FX_RATE_MASK 0x7800 /* FX_RATE - [14:11] */ +#define ARIZONA_FX_RATE_SHIFT 11 /* FX_RATE - [14:11] */ +#define ARIZONA_FX_RATE_WIDTH 4 /* FX_RATE - [14:11] */ + +/* + * R3585 (0xE01) - FX_Ctrl2 + */ +#define ARIZONA_FX_STS_MASK 0xFFF0 /* FX_STS - [15:4] */ +#define ARIZONA_FX_STS_SHIFT 4 /* FX_STS - [15:4] */ +#define ARIZONA_FX_STS_WIDTH 12 /* FX_STS - [15:4] */ + +/* + * R3600 (0xE10) - EQ1_1 + */ +#define ARIZONA_EQ1_B1_GAIN_MASK 0xF800 /* EQ1_B1_GAIN - [15:11] */ +#define ARIZONA_EQ1_B1_GAIN_SHIFT 11 /* EQ1_B1_GAIN - [15:11] */ +#define ARIZONA_EQ1_B1_GAIN_WIDTH 5 /* EQ1_B1_GAIN - [15:11] */ +#define ARIZONA_EQ1_B2_GAIN_MASK 0x07C0 /* EQ1_B2_GAIN - [10:6] */ +#define ARIZONA_EQ1_B2_GAIN_SHIFT 6 /* EQ1_B2_GAIN - [10:6] */ +#define ARIZONA_EQ1_B2_GAIN_WIDTH 5 /* EQ1_B2_GAIN - [10:6] */ +#define ARIZONA_EQ1_B3_GAIN_MASK 0x003E /* EQ1_B3_GAIN - [5:1] */ +#define ARIZONA_EQ1_B3_GAIN_SHIFT 1 /* EQ1_B3_GAIN - [5:1] */ +#define ARIZONA_EQ1_B3_GAIN_WIDTH 5 /* EQ1_B3_GAIN - [5:1] */ +#define ARIZONA_EQ1_ENA 0x0001 /* EQ1_ENA */ +#define ARIZONA_EQ1_ENA_MASK 0x0001 /* EQ1_ENA */ +#define ARIZONA_EQ1_ENA_SHIFT 0 /* EQ1_ENA */ +#define ARIZONA_EQ1_ENA_WIDTH 1 /* EQ1_ENA */ + +/* + * R3601 (0xE11) - EQ1_2 + */ +#define ARIZONA_EQ1_B4_GAIN_MASK 0xF800 /* EQ1_B4_GAIN - [15:11] */ +#define ARIZONA_EQ1_B4_GAIN_SHIFT 11 /* EQ1_B4_GAIN - [15:11] */ +#define ARIZONA_EQ1_B4_GAIN_WIDTH 5 /* EQ1_B4_GAIN - [15:11] */ +#define ARIZONA_EQ1_B5_GAIN_MASK 0x07C0 /* EQ1_B5_GAIN - [10:6] */ +#define ARIZONA_EQ1_B5_GAIN_SHIFT 6 /* EQ1_B5_GAIN - [10:6] */ +#define ARIZONA_EQ1_B5_GAIN_WIDTH 5 /* EQ1_B5_GAIN - [10:6] */ +#define ARIZONA_EQ1_B1_MODE 0x0001 /* EQ1_B1_MODE */ +#define ARIZONA_EQ1_B1_MODE_MASK 0x0001 /* EQ1_B1_MODE */ +#define ARIZONA_EQ1_B1_MODE_SHIFT 0 /* EQ1_B1_MODE */ +#define ARIZONA_EQ1_B1_MODE_WIDTH 1 /* EQ1_B1_MODE */ + +/* + * R3602 (0xE12) - EQ1_3 + */ +#define ARIZONA_EQ1_B1_A_MASK 0xFFFF /* EQ1_B1_A - [15:0] */ +#define ARIZONA_EQ1_B1_A_SHIFT 0 /* EQ1_B1_A - [15:0] */ +#define ARIZONA_EQ1_B1_A_WIDTH 16 /* EQ1_B1_A - [15:0] */ + +/* + * R3603 (0xE13) - EQ1_4 + */ +#define ARIZONA_EQ1_B1_B_MASK 0xFFFF /* EQ1_B1_B - [15:0] */ +#define ARIZONA_EQ1_B1_B_SHIFT 0 /* EQ1_B1_B - [15:0] */ +#define ARIZONA_EQ1_B1_B_WIDTH 16 /* EQ1_B1_B - [15:0] */ + +/* + * R3604 (0xE14) - EQ1_5 + */ +#define ARIZONA_EQ1_B1_PG_MASK 0xFFFF /* EQ1_B1_PG - [15:0] */ +#define ARIZONA_EQ1_B1_PG_SHIFT 0 /* EQ1_B1_PG - [15:0] */ +#define ARIZONA_EQ1_B1_PG_WIDTH 16 /* EQ1_B1_PG - [15:0] */ + +/* + * R3605 (0xE15) - EQ1_6 + */ +#define ARIZONA_EQ1_B2_A_MASK 0xFFFF /* EQ1_B2_A - [15:0] */ +#define ARIZONA_EQ1_B2_A_SHIFT 0 /* EQ1_B2_A - [15:0] */ +#define ARIZONA_EQ1_B2_A_WIDTH 16 /* EQ1_B2_A - [15:0] */ + +/* + * R3606 (0xE16) - EQ1_7 + */ +#define ARIZONA_EQ1_B2_B_MASK 0xFFFF /* EQ1_B2_B - [15:0] */ +#define ARIZONA_EQ1_B2_B_SHIFT 0 /* EQ1_B2_B - [15:0] */ +#define ARIZONA_EQ1_B2_B_WIDTH 16 /* EQ1_B2_B - [15:0] */ + +/* + * R3607 (0xE17) - EQ1_8 + */ +#define ARIZONA_EQ1_B2_C_MASK 0xFFFF /* EQ1_B2_C - [15:0] */ +#define ARIZONA_EQ1_B2_C_SHIFT 0 /* EQ1_B2_C - [15:0] */ +#define ARIZONA_EQ1_B2_C_WIDTH 16 /* EQ1_B2_C - [15:0] */ + +/* + * R3608 (0xE18) - EQ1_9 + */ +#define ARIZONA_EQ1_B2_PG_MASK 0xFFFF /* EQ1_B2_PG - [15:0] */ +#define ARIZONA_EQ1_B2_PG_SHIFT 0 /* EQ1_B2_PG - [15:0] */ +#define ARIZONA_EQ1_B2_PG_WIDTH 16 /* EQ1_B2_PG - [15:0] */ + +/* + * R3609 (0xE19) - EQ1_10 + */ +#define ARIZONA_EQ1_B3_A_MASK 0xFFFF /* EQ1_B3_A - [15:0] */ +#define ARIZONA_EQ1_B3_A_SHIFT 0 /* EQ1_B3_A - [15:0] */ +#define ARIZONA_EQ1_B3_A_WIDTH 16 /* EQ1_B3_A - [15:0] */ + +/* + * R3610 (0xE1A) - EQ1_11 + */ +#define ARIZONA_EQ1_B3_B_MASK 0xFFFF /* EQ1_B3_B - [15:0] */ +#define ARIZONA_EQ1_B3_B_SHIFT 0 /* EQ1_B3_B - [15:0] */ +#define ARIZONA_EQ1_B3_B_WIDTH 16 /* EQ1_B3_B - [15:0] */ + +/* + * R3611 (0xE1B) - EQ1_12 + */ +#define ARIZONA_EQ1_B3_C_MASK 0xFFFF /* EQ1_B3_C - [15:0] */ +#define ARIZONA_EQ1_B3_C_SHIFT 0 /* EQ1_B3_C - [15:0] */ +#define ARIZONA_EQ1_B3_C_WIDTH 16 /* EQ1_B3_C - [15:0] */ + +/* + * R3612 (0xE1C) - EQ1_13 + */ +#define ARIZONA_EQ1_B3_PG_MASK 0xFFFF /* EQ1_B3_PG - [15:0] */ +#define ARIZONA_EQ1_B3_PG_SHIFT 0 /* EQ1_B3_PG - [15:0] */ +#define ARIZONA_EQ1_B3_PG_WIDTH 16 /* EQ1_B3_PG - [15:0] */ + +/* + * R3613 (0xE1D) - EQ1_14 + */ +#define ARIZONA_EQ1_B4_A_MASK 0xFFFF /* EQ1_B4_A - [15:0] */ +#define ARIZONA_EQ1_B4_A_SHIFT 0 /* EQ1_B4_A - [15:0] */ +#define ARIZONA_EQ1_B4_A_WIDTH 16 /* EQ1_B4_A - [15:0] */ + +/* + * R3614 (0xE1E) - EQ1_15 + */ +#define ARIZONA_EQ1_B4_B_MASK 0xFFFF /* EQ1_B4_B - [15:0] */ +#define ARIZONA_EQ1_B4_B_SHIFT 0 /* EQ1_B4_B - [15:0] */ +#define ARIZONA_EQ1_B4_B_WIDTH 16 /* EQ1_B4_B - [15:0] */ + +/* + * R3615 (0xE1F) - EQ1_16 + */ +#define ARIZONA_EQ1_B4_C_MASK 0xFFFF /* EQ1_B4_C - [15:0] */ +#define ARIZONA_EQ1_B4_C_SHIFT 0 /* EQ1_B4_C - [15:0] */ +#define ARIZONA_EQ1_B4_C_WIDTH 16 /* EQ1_B4_C - [15:0] */ + +/* + * R3616 (0xE20) - EQ1_17 + */ +#define ARIZONA_EQ1_B4_PG_MASK 0xFFFF /* EQ1_B4_PG - [15:0] */ +#define ARIZONA_EQ1_B4_PG_SHIFT 0 /* EQ1_B4_PG - [15:0] */ +#define ARIZONA_EQ1_B4_PG_WIDTH 16 /* EQ1_B4_PG - [15:0] */ + +/* + * R3617 (0xE21) - EQ1_18 + */ +#define ARIZONA_EQ1_B5_A_MASK 0xFFFF /* EQ1_B5_A - [15:0] */ +#define ARIZONA_EQ1_B5_A_SHIFT 0 /* EQ1_B5_A - [15:0] */ +#define ARIZONA_EQ1_B5_A_WIDTH 16 /* EQ1_B5_A - [15:0] */ + +/* + * R3618 (0xE22) - EQ1_19 + */ +#define ARIZONA_EQ1_B5_B_MASK 0xFFFF /* EQ1_B5_B - [15:0] */ +#define ARIZONA_EQ1_B5_B_SHIFT 0 /* EQ1_B5_B - [15:0] */ +#define ARIZONA_EQ1_B5_B_WIDTH 16 /* EQ1_B5_B - [15:0] */ + +/* + * R3619 (0xE23) - EQ1_20 + */ +#define ARIZONA_EQ1_B5_PG_MASK 0xFFFF /* EQ1_B5_PG - [15:0] */ +#define ARIZONA_EQ1_B5_PG_SHIFT 0 /* EQ1_B5_PG - [15:0] */ +#define ARIZONA_EQ1_B5_PG_WIDTH 16 /* EQ1_B5_PG - [15:0] */ + +/* + * R3620 (0xE24) - EQ1_21 + */ +#define ARIZONA_EQ1_B1_C_MASK 0xFFFF /* EQ1_B1_C - [15:0] */ +#define ARIZONA_EQ1_B1_C_SHIFT 0 /* EQ1_B1_C - [15:0] */ +#define ARIZONA_EQ1_B1_C_WIDTH 16 /* EQ1_B1_C - [15:0] */ + +/* + * R3622 (0xE26) - EQ2_1 + */ +#define ARIZONA_EQ2_B1_GAIN_MASK 0xF800 /* EQ2_B1_GAIN - [15:11] */ +#define ARIZONA_EQ2_B1_GAIN_SHIFT 11 /* EQ2_B1_GAIN - [15:11] */ +#define ARIZONA_EQ2_B1_GAIN_WIDTH 5 /* EQ2_B1_GAIN - [15:11] */ +#define ARIZONA_EQ2_B2_GAIN_MASK 0x07C0 /* EQ2_B2_GAIN - [10:6] */ +#define ARIZONA_EQ2_B2_GAIN_SHIFT 6 /* EQ2_B2_GAIN - [10:6] */ +#define ARIZONA_EQ2_B2_GAIN_WIDTH 5 /* EQ2_B2_GAIN - [10:6] */ +#define ARIZONA_EQ2_B3_GAIN_MASK 0x003E /* EQ2_B3_GAIN - [5:1] */ +#define ARIZONA_EQ2_B3_GAIN_SHIFT 1 /* EQ2_B3_GAIN - [5:1] */ +#define ARIZONA_EQ2_B3_GAIN_WIDTH 5 /* EQ2_B3_GAIN - [5:1] */ +#define ARIZONA_EQ2_ENA 0x0001 /* EQ2_ENA */ +#define ARIZONA_EQ2_ENA_MASK 0x0001 /* EQ2_ENA */ +#define ARIZONA_EQ2_ENA_SHIFT 0 /* EQ2_ENA */ +#define ARIZONA_EQ2_ENA_WIDTH 1 /* EQ2_ENA */ + +/* + * R3623 (0xE27) - EQ2_2 + */ +#define ARIZONA_EQ2_B4_GAIN_MASK 0xF800 /* EQ2_B4_GAIN - [15:11] */ +#define ARIZONA_EQ2_B4_GAIN_SHIFT 11 /* EQ2_B4_GAIN - [15:11] */ +#define ARIZONA_EQ2_B4_GAIN_WIDTH 5 /* EQ2_B4_GAIN - [15:11] */ +#define ARIZONA_EQ2_B5_GAIN_MASK 0x07C0 /* EQ2_B5_GAIN - [10:6] */ +#define ARIZONA_EQ2_B5_GAIN_SHIFT 6 /* EQ2_B5_GAIN - [10:6] */ +#define ARIZONA_EQ2_B5_GAIN_WIDTH 5 /* EQ2_B5_GAIN - [10:6] */ +#define ARIZONA_EQ2_B1_MODE 0x0001 /* EQ2_B1_MODE */ +#define ARIZONA_EQ2_B1_MODE_MASK 0x0001 /* EQ2_B1_MODE */ +#define ARIZONA_EQ2_B1_MODE_SHIFT 0 /* EQ2_B1_MODE */ +#define ARIZONA_EQ2_B1_MODE_WIDTH 1 /* EQ2_B1_MODE */ + +/* + * R3624 (0xE28) - EQ2_3 + */ +#define ARIZONA_EQ2_B1_A_MASK 0xFFFF /* EQ2_B1_A - [15:0] */ +#define ARIZONA_EQ2_B1_A_SHIFT 0 /* EQ2_B1_A - [15:0] */ +#define ARIZONA_EQ2_B1_A_WIDTH 16 /* EQ2_B1_A - [15:0] */ + +/* + * R3625 (0xE29) - EQ2_4 + */ +#define ARIZONA_EQ2_B1_B_MASK 0xFFFF /* EQ2_B1_B - [15:0] */ +#define ARIZONA_EQ2_B1_B_SHIFT 0 /* EQ2_B1_B - [15:0] */ +#define ARIZONA_EQ2_B1_B_WIDTH 16 /* EQ2_B1_B - [15:0] */ + +/* + * R3626 (0xE2A) - EQ2_5 + */ +#define ARIZONA_EQ2_B1_PG_MASK 0xFFFF /* EQ2_B1_PG - [15:0] */ +#define ARIZONA_EQ2_B1_PG_SHIFT 0 /* EQ2_B1_PG - [15:0] */ +#define ARIZONA_EQ2_B1_PG_WIDTH 16 /* EQ2_B1_PG - [15:0] */ + +/* + * R3627 (0xE2B) - EQ2_6 + */ +#define ARIZONA_EQ2_B2_A_MASK 0xFFFF /* EQ2_B2_A - [15:0] */ +#define ARIZONA_EQ2_B2_A_SHIFT 0 /* EQ2_B2_A - [15:0] */ +#define ARIZONA_EQ2_B2_A_WIDTH 16 /* EQ2_B2_A - [15:0] */ + +/* + * R3628 (0xE2C) - EQ2_7 + */ +#define ARIZONA_EQ2_B2_B_MASK 0xFFFF /* EQ2_B2_B - [15:0] */ +#define ARIZONA_EQ2_B2_B_SHIFT 0 /* EQ2_B2_B - [15:0] */ +#define ARIZONA_EQ2_B2_B_WIDTH 16 /* EQ2_B2_B - [15:0] */ + +/* + * R3629 (0xE2D) - EQ2_8 + */ +#define ARIZONA_EQ2_B2_C_MASK 0xFFFF /* EQ2_B2_C - [15:0] */ +#define ARIZONA_EQ2_B2_C_SHIFT 0 /* EQ2_B2_C - [15:0] */ +#define ARIZONA_EQ2_B2_C_WIDTH 16 /* EQ2_B2_C - [15:0] */ + +/* + * R3630 (0xE2E) - EQ2_9 + */ +#define ARIZONA_EQ2_B2_PG_MASK 0xFFFF /* EQ2_B2_PG - [15:0] */ +#define ARIZONA_EQ2_B2_PG_SHIFT 0 /* EQ2_B2_PG - [15:0] */ +#define ARIZONA_EQ2_B2_PG_WIDTH 16 /* EQ2_B2_PG - [15:0] */ + +/* + * R3631 (0xE2F) - EQ2_10 + */ +#define ARIZONA_EQ2_B3_A_MASK 0xFFFF /* EQ2_B3_A - [15:0] */ +#define ARIZONA_EQ2_B3_A_SHIFT 0 /* EQ2_B3_A - [15:0] */ +#define ARIZONA_EQ2_B3_A_WIDTH 16 /* EQ2_B3_A - [15:0] */ + +/* + * R3632 (0xE30) - EQ2_11 + */ +#define ARIZONA_EQ2_B3_B_MASK 0xFFFF /* EQ2_B3_B - [15:0] */ +#define ARIZONA_EQ2_B3_B_SHIFT 0 /* EQ2_B3_B - [15:0] */ +#define ARIZONA_EQ2_B3_B_WIDTH 16 /* EQ2_B3_B - [15:0] */ + +/* + * R3633 (0xE31) - EQ2_12 + */ +#define ARIZONA_EQ2_B3_C_MASK 0xFFFF /* EQ2_B3_C - [15:0] */ +#define ARIZONA_EQ2_B3_C_SHIFT 0 /* EQ2_B3_C - [15:0] */ +#define ARIZONA_EQ2_B3_C_WIDTH 16 /* EQ2_B3_C - [15:0] */ + +/* + * R3634 (0xE32) - EQ2_13 + */ +#define ARIZONA_EQ2_B3_PG_MASK 0xFFFF /* EQ2_B3_PG - [15:0] */ +#define ARIZONA_EQ2_B3_PG_SHIFT 0 /* EQ2_B3_PG - [15:0] */ +#define ARIZONA_EQ2_B3_PG_WIDTH 16 /* EQ2_B3_PG - [15:0] */ + +/* + * R3635 (0xE33) - EQ2_14 + */ +#define ARIZONA_EQ2_B4_A_MASK 0xFFFF /* EQ2_B4_A - [15:0] */ +#define ARIZONA_EQ2_B4_A_SHIFT 0 /* EQ2_B4_A - [15:0] */ +#define ARIZONA_EQ2_B4_A_WIDTH 16 /* EQ2_B4_A - [15:0] */ + +/* + * R3636 (0xE34) - EQ2_15 + */ +#define ARIZONA_EQ2_B4_B_MASK 0xFFFF /* EQ2_B4_B - [15:0] */ +#define ARIZONA_EQ2_B4_B_SHIFT 0 /* EQ2_B4_B - [15:0] */ +#define ARIZONA_EQ2_B4_B_WIDTH 16 /* EQ2_B4_B - [15:0] */ + +/* + * R3637 (0xE35) - EQ2_16 + */ +#define ARIZONA_EQ2_B4_C_MASK 0xFFFF /* EQ2_B4_C - [15:0] */ +#define ARIZONA_EQ2_B4_C_SHIFT 0 /* EQ2_B4_C - [15:0] */ +#define ARIZONA_EQ2_B4_C_WIDTH 16 /* EQ2_B4_C - [15:0] */ + +/* + * R3638 (0xE36) - EQ2_17 + */ +#define ARIZONA_EQ2_B4_PG_MASK 0xFFFF /* EQ2_B4_PG - [15:0] */ +#define ARIZONA_EQ2_B4_PG_SHIFT 0 /* EQ2_B4_PG - [15:0] */ +#define ARIZONA_EQ2_B4_PG_WIDTH 16 /* EQ2_B4_PG - [15:0] */ + +/* + * R3639 (0xE37) - EQ2_18 + */ +#define ARIZONA_EQ2_B5_A_MASK 0xFFFF /* EQ2_B5_A - [15:0] */ +#define ARIZONA_EQ2_B5_A_SHIFT 0 /* EQ2_B5_A - [15:0] */ +#define ARIZONA_EQ2_B5_A_WIDTH 16 /* EQ2_B5_A - [15:0] */ + +/* + * R3640 (0xE38) - EQ2_19 + */ +#define ARIZONA_EQ2_B5_B_MASK 0xFFFF /* EQ2_B5_B - [15:0] */ +#define ARIZONA_EQ2_B5_B_SHIFT 0 /* EQ2_B5_B - [15:0] */ +#define ARIZONA_EQ2_B5_B_WIDTH 16 /* EQ2_B5_B - [15:0] */ + +/* + * R3641 (0xE39) - EQ2_20 + */ +#define ARIZONA_EQ2_B5_PG_MASK 0xFFFF /* EQ2_B5_PG - [15:0] */ +#define ARIZONA_EQ2_B5_PG_SHIFT 0 /* EQ2_B5_PG - [15:0] */ +#define ARIZONA_EQ2_B5_PG_WIDTH 16 /* EQ2_B5_PG - [15:0] */ + +/* + * R3642 (0xE3A) - EQ2_21 + */ +#define ARIZONA_EQ2_B1_C_MASK 0xFFFF /* EQ2_B1_C - [15:0] */ +#define ARIZONA_EQ2_B1_C_SHIFT 0 /* EQ2_B1_C - [15:0] */ +#define ARIZONA_EQ2_B1_C_WIDTH 16 /* EQ2_B1_C - [15:0] */ + +/* + * R3644 (0xE3C) - EQ3_1 + */ +#define ARIZONA_EQ3_B1_GAIN_MASK 0xF800 /* EQ3_B1_GAIN - [15:11] */ +#define ARIZONA_EQ3_B1_GAIN_SHIFT 11 /* EQ3_B1_GAIN - [15:11] */ +#define ARIZONA_EQ3_B1_GAIN_WIDTH 5 /* EQ3_B1_GAIN - [15:11] */ +#define ARIZONA_EQ3_B2_GAIN_MASK 0x07C0 /* EQ3_B2_GAIN - [10:6] */ +#define ARIZONA_EQ3_B2_GAIN_SHIFT 6 /* EQ3_B2_GAIN - [10:6] */ +#define ARIZONA_EQ3_B2_GAIN_WIDTH 5 /* EQ3_B2_GAIN - [10:6] */ +#define ARIZONA_EQ3_B3_GAIN_MASK 0x003E /* EQ3_B3_GAIN - [5:1] */ +#define ARIZONA_EQ3_B3_GAIN_SHIFT 1 /* EQ3_B3_GAIN - [5:1] */ +#define ARIZONA_EQ3_B3_GAIN_WIDTH 5 /* EQ3_B3_GAIN - [5:1] */ +#define ARIZONA_EQ3_ENA 0x0001 /* EQ3_ENA */ +#define ARIZONA_EQ3_ENA_MASK 0x0001 /* EQ3_ENA */ +#define ARIZONA_EQ3_ENA_SHIFT 0 /* EQ3_ENA */ +#define ARIZONA_EQ3_ENA_WIDTH 1 /* EQ3_ENA */ + +/* + * R3645 (0xE3D) - EQ3_2 + */ +#define ARIZONA_EQ3_B4_GAIN_MASK 0xF800 /* EQ3_B4_GAIN - [15:11] */ +#define ARIZONA_EQ3_B4_GAIN_SHIFT 11 /* EQ3_B4_GAIN - [15:11] */ +#define ARIZONA_EQ3_B4_GAIN_WIDTH 5 /* EQ3_B4_GAIN - [15:11] */ +#define ARIZONA_EQ3_B5_GAIN_MASK 0x07C0 /* EQ3_B5_GAIN - [10:6] */ +#define ARIZONA_EQ3_B5_GAIN_SHIFT 6 /* EQ3_B5_GAIN - [10:6] */ +#define ARIZONA_EQ3_B5_GAIN_WIDTH 5 /* EQ3_B5_GAIN - [10:6] */ +#define ARIZONA_EQ3_B1_MODE 0x0001 /* EQ3_B1_MODE */ +#define ARIZONA_EQ3_B1_MODE_MASK 0x0001 /* EQ3_B1_MODE */ +#define ARIZONA_EQ3_B1_MODE_SHIFT 0 /* EQ3_B1_MODE */ +#define ARIZONA_EQ3_B1_MODE_WIDTH 1 /* EQ3_B1_MODE */ + +/* + * R3646 (0xE3E) - EQ3_3 + */ +#define ARIZONA_EQ3_B1_A_MASK 0xFFFF /* EQ3_B1_A - [15:0] */ +#define ARIZONA_EQ3_B1_A_SHIFT 0 /* EQ3_B1_A - [15:0] */ +#define ARIZONA_EQ3_B1_A_WIDTH 16 /* EQ3_B1_A - [15:0] */ + +/* + * R3647 (0xE3F) - EQ3_4 + */ +#define ARIZONA_EQ3_B1_B_MASK 0xFFFF /* EQ3_B1_B - [15:0] */ +#define ARIZONA_EQ3_B1_B_SHIFT 0 /* EQ3_B1_B - [15:0] */ +#define ARIZONA_EQ3_B1_B_WIDTH 16 /* EQ3_B1_B - [15:0] */ + +/* + * R3648 (0xE40) - EQ3_5 + */ +#define ARIZONA_EQ3_B1_PG_MASK 0xFFFF /* EQ3_B1_PG - [15:0] */ +#define ARIZONA_EQ3_B1_PG_SHIFT 0 /* EQ3_B1_PG - [15:0] */ +#define ARIZONA_EQ3_B1_PG_WIDTH 16 /* EQ3_B1_PG - [15:0] */ + +/* + * R3649 (0xE41) - EQ3_6 + */ +#define ARIZONA_EQ3_B2_A_MASK 0xFFFF /* EQ3_B2_A - [15:0] */ +#define ARIZONA_EQ3_B2_A_SHIFT 0 /* EQ3_B2_A - [15:0] */ +#define ARIZONA_EQ3_B2_A_WIDTH 16 /* EQ3_B2_A - [15:0] */ + +/* + * R3650 (0xE42) - EQ3_7 + */ +#define ARIZONA_EQ3_B2_B_MASK 0xFFFF /* EQ3_B2_B - [15:0] */ +#define ARIZONA_EQ3_B2_B_SHIFT 0 /* EQ3_B2_B - [15:0] */ +#define ARIZONA_EQ3_B2_B_WIDTH 16 /* EQ3_B2_B - [15:0] */ + +/* + * R3651 (0xE43) - EQ3_8 + */ +#define ARIZONA_EQ3_B2_C_MASK 0xFFFF /* EQ3_B2_C - [15:0] */ +#define ARIZONA_EQ3_B2_C_SHIFT 0 /* EQ3_B2_C - [15:0] */ +#define ARIZONA_EQ3_B2_C_WIDTH 16 /* EQ3_B2_C - [15:0] */ + +/* + * R3652 (0xE44) - EQ3_9 + */ +#define ARIZONA_EQ3_B2_PG_MASK 0xFFFF /* EQ3_B2_PG - [15:0] */ +#define ARIZONA_EQ3_B2_PG_SHIFT 0 /* EQ3_B2_PG - [15:0] */ +#define ARIZONA_EQ3_B2_PG_WIDTH 16 /* EQ3_B2_PG - [15:0] */ + +/* + * R3653 (0xE45) - EQ3_10 + */ +#define ARIZONA_EQ3_B3_A_MASK 0xFFFF /* EQ3_B3_A - [15:0] */ +#define ARIZONA_EQ3_B3_A_SHIFT 0 /* EQ3_B3_A - [15:0] */ +#define ARIZONA_EQ3_B3_A_WIDTH 16 /* EQ3_B3_A - [15:0] */ + +/* + * R3654 (0xE46) - EQ3_11 + */ +#define ARIZONA_EQ3_B3_B_MASK 0xFFFF /* EQ3_B3_B - [15:0] */ +#define ARIZONA_EQ3_B3_B_SHIFT 0 /* EQ3_B3_B - [15:0] */ +#define ARIZONA_EQ3_B3_B_WIDTH 16 /* EQ3_B3_B - [15:0] */ + +/* + * R3655 (0xE47) - EQ3_12 + */ +#define ARIZONA_EQ3_B3_C_MASK 0xFFFF /* EQ3_B3_C - [15:0] */ +#define ARIZONA_EQ3_B3_C_SHIFT 0 /* EQ3_B3_C - [15:0] */ +#define ARIZONA_EQ3_B3_C_WIDTH 16 /* EQ3_B3_C - [15:0] */ + +/* + * R3656 (0xE48) - EQ3_13 + */ +#define ARIZONA_EQ3_B3_PG_MASK 0xFFFF /* EQ3_B3_PG - [15:0] */ +#define ARIZONA_EQ3_B3_PG_SHIFT 0 /* EQ3_B3_PG - [15:0] */ +#define ARIZONA_EQ3_B3_PG_WIDTH 16 /* EQ3_B3_PG - [15:0] */ + +/* + * R3657 (0xE49) - EQ3_14 + */ +#define ARIZONA_EQ3_B4_A_MASK 0xFFFF /* EQ3_B4_A - [15:0] */ +#define ARIZONA_EQ3_B4_A_SHIFT 0 /* EQ3_B4_A - [15:0] */ +#define ARIZONA_EQ3_B4_A_WIDTH 16 /* EQ3_B4_A - [15:0] */ + +/* + * R3658 (0xE4A) - EQ3_15 + */ +#define ARIZONA_EQ3_B4_B_MASK 0xFFFF /* EQ3_B4_B - [15:0] */ +#define ARIZONA_EQ3_B4_B_SHIFT 0 /* EQ3_B4_B - [15:0] */ +#define ARIZONA_EQ3_B4_B_WIDTH 16 /* EQ3_B4_B - [15:0] */ + +/* + * R3659 (0xE4B) - EQ3_16 + */ +#define ARIZONA_EQ3_B4_C_MASK 0xFFFF /* EQ3_B4_C - [15:0] */ +#define ARIZONA_EQ3_B4_C_SHIFT 0 /* EQ3_B4_C - [15:0] */ +#define ARIZONA_EQ3_B4_C_WIDTH 16 /* EQ3_B4_C - [15:0] */ + +/* + * R3660 (0xE4C) - EQ3_17 + */ +#define ARIZONA_EQ3_B4_PG_MASK 0xFFFF /* EQ3_B4_PG - [15:0] */ +#define ARIZONA_EQ3_B4_PG_SHIFT 0 /* EQ3_B4_PG - [15:0] */ +#define ARIZONA_EQ3_B4_PG_WIDTH 16 /* EQ3_B4_PG - [15:0] */ + +/* + * R3661 (0xE4D) - EQ3_18 + */ +#define ARIZONA_EQ3_B5_A_MASK 0xFFFF /* EQ3_B5_A - [15:0] */ +#define ARIZONA_EQ3_B5_A_SHIFT 0 /* EQ3_B5_A - [15:0] */ +#define ARIZONA_EQ3_B5_A_WIDTH 16 /* EQ3_B5_A - [15:0] */ + +/* + * R3662 (0xE4E) - EQ3_19 + */ +#define ARIZONA_EQ3_B5_B_MASK 0xFFFF /* EQ3_B5_B - [15:0] */ +#define ARIZONA_EQ3_B5_B_SHIFT 0 /* EQ3_B5_B - [15:0] */ +#define ARIZONA_EQ3_B5_B_WIDTH 16 /* EQ3_B5_B - [15:0] */ + +/* + * R3663 (0xE4F) - EQ3_20 + */ +#define ARIZONA_EQ3_B5_PG_MASK 0xFFFF /* EQ3_B5_PG - [15:0] */ +#define ARIZONA_EQ3_B5_PG_SHIFT 0 /* EQ3_B5_PG - [15:0] */ +#define ARIZONA_EQ3_B5_PG_WIDTH 16 /* EQ3_B5_PG - [15:0] */ + +/* + * R3664 (0xE50) - EQ3_21 + */ +#define ARIZONA_EQ3_B1_C_MASK 0xFFFF /* EQ3_B1_C - [15:0] */ +#define ARIZONA_EQ3_B1_C_SHIFT 0 /* EQ3_B1_C - [15:0] */ +#define ARIZONA_EQ3_B1_C_WIDTH 16 /* EQ3_B1_C - [15:0] */ + +/* + * R3666 (0xE52) - EQ4_1 + */ +#define ARIZONA_EQ4_B1_GAIN_MASK 0xF800 /* EQ4_B1_GAIN - [15:11] */ +#define ARIZONA_EQ4_B1_GAIN_SHIFT 11 /* EQ4_B1_GAIN - [15:11] */ +#define ARIZONA_EQ4_B1_GAIN_WIDTH 5 /* EQ4_B1_GAIN - [15:11] */ +#define ARIZONA_EQ4_B2_GAIN_MASK 0x07C0 /* EQ4_B2_GAIN - [10:6] */ +#define ARIZONA_EQ4_B2_GAIN_SHIFT 6 /* EQ4_B2_GAIN - [10:6] */ +#define ARIZONA_EQ4_B2_GAIN_WIDTH 5 /* EQ4_B2_GAIN - [10:6] */ +#define ARIZONA_EQ4_B3_GAIN_MASK 0x003E /* EQ4_B3_GAIN - [5:1] */ +#define ARIZONA_EQ4_B3_GAIN_SHIFT 1 /* EQ4_B3_GAIN - [5:1] */ +#define ARIZONA_EQ4_B3_GAIN_WIDTH 5 /* EQ4_B3_GAIN - [5:1] */ +#define ARIZONA_EQ4_ENA 0x0001 /* EQ4_ENA */ +#define ARIZONA_EQ4_ENA_MASK 0x0001 /* EQ4_ENA */ +#define ARIZONA_EQ4_ENA_SHIFT 0 /* EQ4_ENA */ +#define ARIZONA_EQ4_ENA_WIDTH 1 /* EQ4_ENA */ + +/* + * R3667 (0xE53) - EQ4_2 + */ +#define ARIZONA_EQ4_B4_GAIN_MASK 0xF800 /* EQ4_B4_GAIN - [15:11] */ +#define ARIZONA_EQ4_B4_GAIN_SHIFT 11 /* EQ4_B4_GAIN - [15:11] */ +#define ARIZONA_EQ4_B4_GAIN_WIDTH 5 /* EQ4_B4_GAIN - [15:11] */ +#define ARIZONA_EQ4_B5_GAIN_MASK 0x07C0 /* EQ4_B5_GAIN - [10:6] */ +#define ARIZONA_EQ4_B5_GAIN_SHIFT 6 /* EQ4_B5_GAIN - [10:6] */ +#define ARIZONA_EQ4_B5_GAIN_WIDTH 5 /* EQ4_B5_GAIN - [10:6] */ +#define ARIZONA_EQ4_B1_MODE 0x0001 /* EQ4_B1_MODE */ +#define ARIZONA_EQ4_B1_MODE_MASK 0x0001 /* EQ4_B1_MODE */ +#define ARIZONA_EQ4_B1_MODE_SHIFT 0 /* EQ4_B1_MODE */ +#define ARIZONA_EQ4_B1_MODE_WIDTH 1 /* EQ4_B1_MODE */ + +/* + * R3668 (0xE54) - EQ4_3 + */ +#define ARIZONA_EQ4_B1_A_MASK 0xFFFF /* EQ4_B1_A - [15:0] */ +#define ARIZONA_EQ4_B1_A_SHIFT 0 /* EQ4_B1_A - [15:0] */ +#define ARIZONA_EQ4_B1_A_WIDTH 16 /* EQ4_B1_A - [15:0] */ + +/* + * R3669 (0xE55) - EQ4_4 + */ +#define ARIZONA_EQ4_B1_B_MASK 0xFFFF /* EQ4_B1_B - [15:0] */ +#define ARIZONA_EQ4_B1_B_SHIFT 0 /* EQ4_B1_B - [15:0] */ +#define ARIZONA_EQ4_B1_B_WIDTH 16 /* EQ4_B1_B - [15:0] */ + +/* + * R3670 (0xE56) - EQ4_5 + */ +#define ARIZONA_EQ4_B1_PG_MASK 0xFFFF /* EQ4_B1_PG - [15:0] */ +#define ARIZONA_EQ4_B1_PG_SHIFT 0 /* EQ4_B1_PG - [15:0] */ +#define ARIZONA_EQ4_B1_PG_WIDTH 16 /* EQ4_B1_PG - [15:0] */ + +/* + * R3671 (0xE57) - EQ4_6 + */ +#define ARIZONA_EQ4_B2_A_MASK 0xFFFF /* EQ4_B2_A - [15:0] */ +#define ARIZONA_EQ4_B2_A_SHIFT 0 /* EQ4_B2_A - [15:0] */ +#define ARIZONA_EQ4_B2_A_WIDTH 16 /* EQ4_B2_A - [15:0] */ + +/* + * R3672 (0xE58) - EQ4_7 + */ +#define ARIZONA_EQ4_B2_B_MASK 0xFFFF /* EQ4_B2_B - [15:0] */ +#define ARIZONA_EQ4_B2_B_SHIFT 0 /* EQ4_B2_B - [15:0] */ +#define ARIZONA_EQ4_B2_B_WIDTH 16 /* EQ4_B2_B - [15:0] */ + +/* + * R3673 (0xE59) - EQ4_8 + */ +#define ARIZONA_EQ4_B2_C_MASK 0xFFFF /* EQ4_B2_C - [15:0] */ +#define ARIZONA_EQ4_B2_C_SHIFT 0 /* EQ4_B2_C - [15:0] */ +#define ARIZONA_EQ4_B2_C_WIDTH 16 /* EQ4_B2_C - [15:0] */ + +/* + * R3674 (0xE5A) - EQ4_9 + */ +#define ARIZONA_EQ4_B2_PG_MASK 0xFFFF /* EQ4_B2_PG - [15:0] */ +#define ARIZONA_EQ4_B2_PG_SHIFT 0 /* EQ4_B2_PG - [15:0] */ +#define ARIZONA_EQ4_B2_PG_WIDTH 16 /* EQ4_B2_PG - [15:0] */ + +/* + * R3675 (0xE5B) - EQ4_10 + */ +#define ARIZONA_EQ4_B3_A_MASK 0xFFFF /* EQ4_B3_A - [15:0] */ +#define ARIZONA_EQ4_B3_A_SHIFT 0 /* EQ4_B3_A - [15:0] */ +#define ARIZONA_EQ4_B3_A_WIDTH 16 /* EQ4_B3_A - [15:0] */ + +/* + * R3676 (0xE5C) - EQ4_11 + */ +#define ARIZONA_EQ4_B3_B_MASK 0xFFFF /* EQ4_B3_B - [15:0] */ +#define ARIZONA_EQ4_B3_B_SHIFT 0 /* EQ4_B3_B - [15:0] */ +#define ARIZONA_EQ4_B3_B_WIDTH 16 /* EQ4_B3_B - [15:0] */ + +/* + * R3677 (0xE5D) - EQ4_12 + */ +#define ARIZONA_EQ4_B3_C_MASK 0xFFFF /* EQ4_B3_C - [15:0] */ +#define ARIZONA_EQ4_B3_C_SHIFT 0 /* EQ4_B3_C - [15:0] */ +#define ARIZONA_EQ4_B3_C_WIDTH 16 /* EQ4_B3_C - [15:0] */ + +/* + * R3678 (0xE5E) - EQ4_13 + */ +#define ARIZONA_EQ4_B3_PG_MASK 0xFFFF /* EQ4_B3_PG - [15:0] */ +#define ARIZONA_EQ4_B3_PG_SHIFT 0 /* EQ4_B3_PG - [15:0] */ +#define ARIZONA_EQ4_B3_PG_WIDTH 16 /* EQ4_B3_PG - [15:0] */ + +/* + * R3679 (0xE5F) - EQ4_14 + */ +#define ARIZONA_EQ4_B4_A_MASK 0xFFFF /* EQ4_B4_A - [15:0] */ +#define ARIZONA_EQ4_B4_A_SHIFT 0 /* EQ4_B4_A - [15:0] */ +#define ARIZONA_EQ4_B4_A_WIDTH 16 /* EQ4_B4_A - [15:0] */ + +/* + * R3680 (0xE60) - EQ4_15 + */ +#define ARIZONA_EQ4_B4_B_MASK 0xFFFF /* EQ4_B4_B - [15:0] */ +#define ARIZONA_EQ4_B4_B_SHIFT 0 /* EQ4_B4_B - [15:0] */ +#define ARIZONA_EQ4_B4_B_WIDTH 16 /* EQ4_B4_B - [15:0] */ + +/* + * R3681 (0xE61) - EQ4_16 + */ +#define ARIZONA_EQ4_B4_C_MASK 0xFFFF /* EQ4_B4_C - [15:0] */ +#define ARIZONA_EQ4_B4_C_SHIFT 0 /* EQ4_B4_C - [15:0] */ +#define ARIZONA_EQ4_B4_C_WIDTH 16 /* EQ4_B4_C - [15:0] */ + +/* + * R3682 (0xE62) - EQ4_17 + */ +#define ARIZONA_EQ4_B4_PG_MASK 0xFFFF /* EQ4_B4_PG - [15:0] */ +#define ARIZONA_EQ4_B4_PG_SHIFT 0 /* EQ4_B4_PG - [15:0] */ +#define ARIZONA_EQ4_B4_PG_WIDTH 16 /* EQ4_B4_PG - [15:0] */ + +/* + * R3683 (0xE63) - EQ4_18 + */ +#define ARIZONA_EQ4_B5_A_MASK 0xFFFF /* EQ4_B5_A - [15:0] */ +#define ARIZONA_EQ4_B5_A_SHIFT 0 /* EQ4_B5_A - [15:0] */ +#define ARIZONA_EQ4_B5_A_WIDTH 16 /* EQ4_B5_A - [15:0] */ + +/* + * R3684 (0xE64) - EQ4_19 + */ +#define ARIZONA_EQ4_B5_B_MASK 0xFFFF /* EQ4_B5_B - [15:0] */ +#define ARIZONA_EQ4_B5_B_SHIFT 0 /* EQ4_B5_B - [15:0] */ +#define ARIZONA_EQ4_B5_B_WIDTH 16 /* EQ4_B5_B - [15:0] */ + +/* + * R3685 (0xE65) - EQ4_20 + */ +#define ARIZONA_EQ4_B5_PG_MASK 0xFFFF /* EQ4_B5_PG - [15:0] */ +#define ARIZONA_EQ4_B5_PG_SHIFT 0 /* EQ4_B5_PG - [15:0] */ +#define ARIZONA_EQ4_B5_PG_WIDTH 16 /* EQ4_B5_PG - [15:0] */ + +/* + * R3686 (0xE66) - EQ4_21 + */ +#define ARIZONA_EQ4_B1_C_MASK 0xFFFF /* EQ4_B1_C - [15:0] */ +#define ARIZONA_EQ4_B1_C_SHIFT 0 /* EQ4_B1_C - [15:0] */ +#define ARIZONA_EQ4_B1_C_WIDTH 16 /* EQ4_B1_C - [15:0] */ + +/* + * R3712 (0xE80) - DRC1 ctrl1 + */ +#define ARIZONA_DRC1_SIG_DET_RMS_MASK 0xF800 /* DRC1_SIG_DET_RMS - [15:11] */ +#define ARIZONA_DRC1_SIG_DET_RMS_SHIFT 11 /* DRC1_SIG_DET_RMS - [15:11] */ +#define ARIZONA_DRC1_SIG_DET_RMS_WIDTH 5 /* DRC1_SIG_DET_RMS - [15:11] */ +#define ARIZONA_DRC1_SIG_DET_PK_MASK 0x0600 /* DRC1_SIG_DET_PK - [10:9] */ +#define ARIZONA_DRC1_SIG_DET_PK_SHIFT 9 /* DRC1_SIG_DET_PK - [10:9] */ +#define ARIZONA_DRC1_SIG_DET_PK_WIDTH 2 /* DRC1_SIG_DET_PK - [10:9] */ +#define ARIZONA_DRC1_NG_ENA 0x0100 /* DRC1_NG_ENA */ +#define ARIZONA_DRC1_NG_ENA_MASK 0x0100 /* DRC1_NG_ENA */ +#define ARIZONA_DRC1_NG_ENA_SHIFT 8 /* DRC1_NG_ENA */ +#define ARIZONA_DRC1_NG_ENA_WIDTH 1 /* DRC1_NG_ENA */ +#define ARIZONA_DRC1_SIG_DET_MODE 0x0080 /* DRC1_SIG_DET_MODE */ +#define ARIZONA_DRC1_SIG_DET_MODE_MASK 0x0080 /* DRC1_SIG_DET_MODE */ +#define ARIZONA_DRC1_SIG_DET_MODE_SHIFT 7 /* DRC1_SIG_DET_MODE */ +#define ARIZONA_DRC1_SIG_DET_MODE_WIDTH 1 /* DRC1_SIG_DET_MODE */ +#define ARIZONA_DRC1_SIG_DET 0x0040 /* DRC1_SIG_DET */ +#define ARIZONA_DRC1_SIG_DET_MASK 0x0040 /* DRC1_SIG_DET */ +#define ARIZONA_DRC1_SIG_DET_SHIFT 6 /* DRC1_SIG_DET */ +#define ARIZONA_DRC1_SIG_DET_WIDTH 1 /* DRC1_SIG_DET */ +#define ARIZONA_DRC1_KNEE2_OP_ENA 0x0020 /* DRC1_KNEE2_OP_ENA */ +#define ARIZONA_DRC1_KNEE2_OP_ENA_MASK 0x0020 /* DRC1_KNEE2_OP_ENA */ +#define ARIZONA_DRC1_KNEE2_OP_ENA_SHIFT 5 /* DRC1_KNEE2_OP_ENA */ +#define ARIZONA_DRC1_KNEE2_OP_ENA_WIDTH 1 /* DRC1_KNEE2_OP_ENA */ +#define ARIZONA_DRC1_QR 0x0010 /* DRC1_QR */ +#define ARIZONA_DRC1_QR_MASK 0x0010 /* DRC1_QR */ +#define ARIZONA_DRC1_QR_SHIFT 4 /* DRC1_QR */ +#define ARIZONA_DRC1_QR_WIDTH 1 /* DRC1_QR */ +#define ARIZONA_DRC1_ANTICLIP 0x0008 /* DRC1_ANTICLIP */ +#define ARIZONA_DRC1_ANTICLIP_MASK 0x0008 /* DRC1_ANTICLIP */ +#define ARIZONA_DRC1_ANTICLIP_SHIFT 3 /* DRC1_ANTICLIP */ +#define ARIZONA_DRC1_ANTICLIP_WIDTH 1 /* DRC1_ANTICLIP */ +#define ARIZONA_DRC1L_ENA 0x0002 /* DRC1L_ENA */ +#define ARIZONA_DRC1L_ENA_MASK 0x0002 /* DRC1L_ENA */ +#define ARIZONA_DRC1L_ENA_SHIFT 1 /* DRC1L_ENA */ +#define ARIZONA_DRC1L_ENA_WIDTH 1 /* DRC1L_ENA */ +#define ARIZONA_DRC1R_ENA 0x0001 /* DRC1R_ENA */ +#define ARIZONA_DRC1R_ENA_MASK 0x0001 /* DRC1R_ENA */ +#define ARIZONA_DRC1R_ENA_SHIFT 0 /* DRC1R_ENA */ +#define ARIZONA_DRC1R_ENA_WIDTH 1 /* DRC1R_ENA */ + +/* + * R3713 (0xE81) - DRC1 ctrl2 + */ +#define ARIZONA_DRC1_ATK_MASK 0x1E00 /* DRC1_ATK - [12:9] */ +#define ARIZONA_DRC1_ATK_SHIFT 9 /* DRC1_ATK - [12:9] */ +#define ARIZONA_DRC1_ATK_WIDTH 4 /* DRC1_ATK - [12:9] */ +#define ARIZONA_DRC1_DCY_MASK 0x01E0 /* DRC1_DCY - [8:5] */ +#define ARIZONA_DRC1_DCY_SHIFT 5 /* DRC1_DCY - [8:5] */ +#define ARIZONA_DRC1_DCY_WIDTH 4 /* DRC1_DCY - [8:5] */ +#define ARIZONA_DRC1_MINGAIN_MASK 0x001C /* DRC1_MINGAIN - [4:2] */ +#define ARIZONA_DRC1_MINGAIN_SHIFT 2 /* DRC1_MINGAIN - [4:2] */ +#define ARIZONA_DRC1_MINGAIN_WIDTH 3 /* DRC1_MINGAIN - [4:2] */ +#define ARIZONA_DRC1_MAXGAIN_MASK 0x0003 /* DRC1_MAXGAIN - [1:0] */ +#define ARIZONA_DRC1_MAXGAIN_SHIFT 0 /* DRC1_MAXGAIN - [1:0] */ +#define ARIZONA_DRC1_MAXGAIN_WIDTH 2 /* DRC1_MAXGAIN - [1:0] */ + +/* + * R3714 (0xE82) - DRC1 ctrl3 + */ +#define ARIZONA_DRC1_NG_MINGAIN_MASK 0xF000 /* DRC1_NG_MINGAIN - [15:12] */ +#define ARIZONA_DRC1_NG_MINGAIN_SHIFT 12 /* DRC1_NG_MINGAIN - [15:12] */ +#define ARIZONA_DRC1_NG_MINGAIN_WIDTH 4 /* DRC1_NG_MINGAIN - [15:12] */ +#define ARIZONA_DRC1_NG_EXP_MASK 0x0C00 /* DRC1_NG_EXP - [11:10] */ +#define ARIZONA_DRC1_NG_EXP_SHIFT 10 /* DRC1_NG_EXP - [11:10] */ +#define ARIZONA_DRC1_NG_EXP_WIDTH 2 /* DRC1_NG_EXP - [11:10] */ +#define ARIZONA_DRC1_QR_THR_MASK 0x0300 /* DRC1_QR_THR - [9:8] */ +#define ARIZONA_DRC1_QR_THR_SHIFT 8 /* DRC1_QR_THR - [9:8] */ +#define ARIZONA_DRC1_QR_THR_WIDTH 2 /* DRC1_QR_THR - [9:8] */ +#define ARIZONA_DRC1_QR_DCY_MASK 0x00C0 /* DRC1_QR_DCY - [7:6] */ +#define ARIZONA_DRC1_QR_DCY_SHIFT 6 /* DRC1_QR_DCY - [7:6] */ +#define ARIZONA_DRC1_QR_DCY_WIDTH 2 /* DRC1_QR_DCY - [7:6] */ +#define ARIZONA_DRC1_HI_COMP_MASK 0x0038 /* DRC1_HI_COMP - [5:3] */ +#define ARIZONA_DRC1_HI_COMP_SHIFT 3 /* DRC1_HI_COMP - [5:3] */ +#define ARIZONA_DRC1_HI_COMP_WIDTH 3 /* DRC1_HI_COMP - [5:3] */ +#define ARIZONA_DRC1_LO_COMP_MASK 0x0007 /* DRC1_LO_COMP - [2:0] */ +#define ARIZONA_DRC1_LO_COMP_SHIFT 0 /* DRC1_LO_COMP - [2:0] */ +#define ARIZONA_DRC1_LO_COMP_WIDTH 3 /* DRC1_LO_COMP - [2:0] */ + +/* + * R3715 (0xE83) - DRC1 ctrl4 + */ +#define ARIZONA_DRC1_KNEE_IP_MASK 0x07E0 /* DRC1_KNEE_IP - [10:5] */ +#define ARIZONA_DRC1_KNEE_IP_SHIFT 5 /* DRC1_KNEE_IP - [10:5] */ +#define ARIZONA_DRC1_KNEE_IP_WIDTH 6 /* DRC1_KNEE_IP - [10:5] */ +#define ARIZONA_DRC1_KNEE_OP_MASK 0x001F /* DRC1_KNEE_OP - [4:0] */ +#define ARIZONA_DRC1_KNEE_OP_SHIFT 0 /* DRC1_KNEE_OP - [4:0] */ +#define ARIZONA_DRC1_KNEE_OP_WIDTH 5 /* DRC1_KNEE_OP - [4:0] */ + +/* + * R3716 (0xE84) - DRC1 ctrl5 + */ +#define ARIZONA_DRC1_KNEE2_IP_MASK 0x03E0 /* DRC1_KNEE2_IP - [9:5] */ +#define ARIZONA_DRC1_KNEE2_IP_SHIFT 5 /* DRC1_KNEE2_IP - [9:5] */ +#define ARIZONA_DRC1_KNEE2_IP_WIDTH 5 /* DRC1_KNEE2_IP - [9:5] */ +#define ARIZONA_DRC1_KNEE2_OP_MASK 0x001F /* DRC1_KNEE2_OP - [4:0] */ +#define ARIZONA_DRC1_KNEE2_OP_SHIFT 0 /* DRC1_KNEE2_OP - [4:0] */ +#define ARIZONA_DRC1_KNEE2_OP_WIDTH 5 /* DRC1_KNEE2_OP - [4:0] */ + +/* + * R3721 (0xE89) - DRC2 ctrl1 + */ +#define ARIZONA_DRC2_SIG_DET_RMS_MASK 0xF800 /* DRC2_SIG_DET_RMS - [15:11] */ +#define ARIZONA_DRC2_SIG_DET_RMS_SHIFT 11 /* DRC2_SIG_DET_RMS - [15:11] */ +#define ARIZONA_DRC2_SIG_DET_RMS_WIDTH 5 /* DRC2_SIG_DET_RMS - [15:11] */ +#define ARIZONA_DRC2_SIG_DET_PK_MASK 0x0600 /* DRC2_SIG_DET_PK - [10:9] */ +#define ARIZONA_DRC2_SIG_DET_PK_SHIFT 9 /* DRC2_SIG_DET_PK - [10:9] */ +#define ARIZONA_DRC2_SIG_DET_PK_WIDTH 2 /* DRC2_SIG_DET_PK - [10:9] */ +#define ARIZONA_DRC2_NG_ENA 0x0100 /* DRC2_NG_ENA */ +#define ARIZONA_DRC2_NG_ENA_MASK 0x0100 /* DRC2_NG_ENA */ +#define ARIZONA_DRC2_NG_ENA_SHIFT 8 /* DRC2_NG_ENA */ +#define ARIZONA_DRC2_NG_ENA_WIDTH 1 /* DRC2_NG_ENA */ +#define ARIZONA_DRC2_SIG_DET_MODE 0x0080 /* DRC2_SIG_DET_MODE */ +#define ARIZONA_DRC2_SIG_DET_MODE_MASK 0x0080 /* DRC2_SIG_DET_MODE */ +#define ARIZONA_DRC2_SIG_DET_MODE_SHIFT 7 /* DRC2_SIG_DET_MODE */ +#define ARIZONA_DRC2_SIG_DET_MODE_WIDTH 1 /* DRC2_SIG_DET_MODE */ +#define ARIZONA_DRC2_SIG_DET 0x0040 /* DRC2_SIG_DET */ +#define ARIZONA_DRC2_SIG_DET_MASK 0x0040 /* DRC2_SIG_DET */ +#define ARIZONA_DRC2_SIG_DET_SHIFT 6 /* DRC2_SIG_DET */ +#define ARIZONA_DRC2_SIG_DET_WIDTH 1 /* DRC2_SIG_DET */ +#define ARIZONA_DRC2_KNEE2_OP_ENA 0x0020 /* DRC2_KNEE2_OP_ENA */ +#define ARIZONA_DRC2_KNEE2_OP_ENA_MASK 0x0020 /* DRC2_KNEE2_OP_ENA */ +#define ARIZONA_DRC2_KNEE2_OP_ENA_SHIFT 5 /* DRC2_KNEE2_OP_ENA */ +#define ARIZONA_DRC2_KNEE2_OP_ENA_WIDTH 1 /* DRC2_KNEE2_OP_ENA */ +#define ARIZONA_DRC2_QR 0x0010 /* DRC2_QR */ +#define ARIZONA_DRC2_QR_MASK 0x0010 /* DRC2_QR */ +#define ARIZONA_DRC2_QR_SHIFT 4 /* DRC2_QR */ +#define ARIZONA_DRC2_QR_WIDTH 1 /* DRC2_QR */ +#define ARIZONA_DRC2_ANTICLIP 0x0008 /* DRC2_ANTICLIP */ +#define ARIZONA_DRC2_ANTICLIP_MASK 0x0008 /* DRC2_ANTICLIP */ +#define ARIZONA_DRC2_ANTICLIP_SHIFT 3 /* DRC2_ANTICLIP */ +#define ARIZONA_DRC2_ANTICLIP_WIDTH 1 /* DRC2_ANTICLIP */ +#define ARIZONA_DRC2L_ENA 0x0002 /* DRC2L_ENA */ +#define ARIZONA_DRC2L_ENA_MASK 0x0002 /* DRC2L_ENA */ +#define ARIZONA_DRC2L_ENA_SHIFT 1 /* DRC2L_ENA */ +#define ARIZONA_DRC2L_ENA_WIDTH 1 /* DRC2L_ENA */ +#define ARIZONA_DRC2R_ENA 0x0001 /* DRC2R_ENA */ +#define ARIZONA_DRC2R_ENA_MASK 0x0001 /* DRC2R_ENA */ +#define ARIZONA_DRC2R_ENA_SHIFT 0 /* DRC2R_ENA */ +#define ARIZONA_DRC2R_ENA_WIDTH 1 /* DRC2R_ENA */ + +/* + * R3722 (0xE8A) - DRC2 ctrl2 + */ +#define ARIZONA_DRC2_ATK_MASK 0x1E00 /* DRC2_ATK - [12:9] */ +#define ARIZONA_DRC2_ATK_SHIFT 9 /* DRC2_ATK - [12:9] */ +#define ARIZONA_DRC2_ATK_WIDTH 4 /* DRC2_ATK - [12:9] */ +#define ARIZONA_DRC2_DCY_MASK 0x01E0 /* DRC2_DCY - [8:5] */ +#define ARIZONA_DRC2_DCY_SHIFT 5 /* DRC2_DCY - [8:5] */ +#define ARIZONA_DRC2_DCY_WIDTH 4 /* DRC2_DCY - [8:5] */ +#define ARIZONA_DRC2_MINGAIN_MASK 0x001C /* DRC2_MINGAIN - [4:2] */ +#define ARIZONA_DRC2_MINGAIN_SHIFT 2 /* DRC2_MINGAIN - [4:2] */ +#define ARIZONA_DRC2_MINGAIN_WIDTH 3 /* DRC2_MINGAIN - [4:2] */ +#define ARIZONA_DRC2_MAXGAIN_MASK 0x0003 /* DRC2_MAXGAIN - [1:0] */ +#define ARIZONA_DRC2_MAXGAIN_SHIFT 0 /* DRC2_MAXGAIN - [1:0] */ +#define ARIZONA_DRC2_MAXGAIN_WIDTH 2 /* DRC2_MAXGAIN - [1:0] */ + +/* + * R3723 (0xE8B) - DRC2 ctrl3 + */ +#define ARIZONA_DRC2_NG_MINGAIN_MASK 0xF000 /* DRC2_NG_MINGAIN - [15:12] */ +#define ARIZONA_DRC2_NG_MINGAIN_SHIFT 12 /* DRC2_NG_MINGAIN - [15:12] */ +#define ARIZONA_DRC2_NG_MINGAIN_WIDTH 4 /* DRC2_NG_MINGAIN - [15:12] */ +#define ARIZONA_DRC2_NG_EXP_MASK 0x0C00 /* DRC2_NG_EXP - [11:10] */ +#define ARIZONA_DRC2_NG_EXP_SHIFT 10 /* DRC2_NG_EXP - [11:10] */ +#define ARIZONA_DRC2_NG_EXP_WIDTH 2 /* DRC2_NG_EXP - [11:10] */ +#define ARIZONA_DRC2_QR_THR_MASK 0x0300 /* DRC2_QR_THR - [9:8] */ +#define ARIZONA_DRC2_QR_THR_SHIFT 8 /* DRC2_QR_THR - [9:8] */ +#define ARIZONA_DRC2_QR_THR_WIDTH 2 /* DRC2_QR_THR - [9:8] */ +#define ARIZONA_DRC2_QR_DCY_MASK 0x00C0 /* DRC2_QR_DCY - [7:6] */ +#define ARIZONA_DRC2_QR_DCY_SHIFT 6 /* DRC2_QR_DCY - [7:6] */ +#define ARIZONA_DRC2_QR_DCY_WIDTH 2 /* DRC2_QR_DCY - [7:6] */ +#define ARIZONA_DRC2_HI_COMP_MASK 0x0038 /* DRC2_HI_COMP - [5:3] */ +#define ARIZONA_DRC2_HI_COMP_SHIFT 3 /* DRC2_HI_COMP - [5:3] */ +#define ARIZONA_DRC2_HI_COMP_WIDTH 3 /* DRC2_HI_COMP - [5:3] */ +#define ARIZONA_DRC2_LO_COMP_MASK 0x0007 /* DRC2_LO_COMP - [2:0] */ +#define ARIZONA_DRC2_LO_COMP_SHIFT 0 /* DRC2_LO_COMP - [2:0] */ +#define ARIZONA_DRC2_LO_COMP_WIDTH 3 /* DRC2_LO_COMP - [2:0] */ + +/* + * R3724 (0xE8C) - DRC2 ctrl4 + */ +#define ARIZONA_DRC2_KNEE_IP_MASK 0x07E0 /* DRC2_KNEE_IP - [10:5] */ +#define ARIZONA_DRC2_KNEE_IP_SHIFT 5 /* DRC2_KNEE_IP - [10:5] */ +#define ARIZONA_DRC2_KNEE_IP_WIDTH 6 /* DRC2_KNEE_IP - [10:5] */ +#define ARIZONA_DRC2_KNEE_OP_MASK 0x001F /* DRC2_KNEE_OP - [4:0] */ +#define ARIZONA_DRC2_KNEE_OP_SHIFT 0 /* DRC2_KNEE_OP - [4:0] */ +#define ARIZONA_DRC2_KNEE_OP_WIDTH 5 /* DRC2_KNEE_OP - [4:0] */ + +/* + * R3725 (0xE8D) - DRC2 ctrl5 + */ +#define ARIZONA_DRC2_KNEE2_IP_MASK 0x03E0 /* DRC2_KNEE2_IP - [9:5] */ +#define ARIZONA_DRC2_KNEE2_IP_SHIFT 5 /* DRC2_KNEE2_IP - [9:5] */ +#define ARIZONA_DRC2_KNEE2_IP_WIDTH 5 /* DRC2_KNEE2_IP - [9:5] */ +#define ARIZONA_DRC2_KNEE2_OP_MASK 0x001F /* DRC2_KNEE2_OP - [4:0] */ +#define ARIZONA_DRC2_KNEE2_OP_SHIFT 0 /* DRC2_KNEE2_OP - [4:0] */ +#define ARIZONA_DRC2_KNEE2_OP_WIDTH 5 /* DRC2_KNEE2_OP - [4:0] */ + +/* + * R3776 (0xEC0) - HPLPF1_1 + */ +#define ARIZONA_LHPF1_MODE 0x0002 /* LHPF1_MODE */ +#define ARIZONA_LHPF1_MODE_MASK 0x0002 /* LHPF1_MODE */ +#define ARIZONA_LHPF1_MODE_SHIFT 1 /* LHPF1_MODE */ +#define ARIZONA_LHPF1_MODE_WIDTH 1 /* LHPF1_MODE */ +#define ARIZONA_LHPF1_ENA 0x0001 /* LHPF1_ENA */ +#define ARIZONA_LHPF1_ENA_MASK 0x0001 /* LHPF1_ENA */ +#define ARIZONA_LHPF1_ENA_SHIFT 0 /* LHPF1_ENA */ +#define ARIZONA_LHPF1_ENA_WIDTH 1 /* LHPF1_ENA */ + +/* + * R3777 (0xEC1) - HPLPF1_2 + */ +#define ARIZONA_LHPF1_COEFF_MASK 0xFFFF /* LHPF1_COEFF - [15:0] */ +#define ARIZONA_LHPF1_COEFF_SHIFT 0 /* LHPF1_COEFF - [15:0] */ +#define ARIZONA_LHPF1_COEFF_WIDTH 16 /* LHPF1_COEFF - [15:0] */ + +/* + * R3780 (0xEC4) - HPLPF2_1 + */ +#define ARIZONA_LHPF2_MODE 0x0002 /* LHPF2_MODE */ +#define ARIZONA_LHPF2_MODE_MASK 0x0002 /* LHPF2_MODE */ +#define ARIZONA_LHPF2_MODE_SHIFT 1 /* LHPF2_MODE */ +#define ARIZONA_LHPF2_MODE_WIDTH 1 /* LHPF2_MODE */ +#define ARIZONA_LHPF2_ENA 0x0001 /* LHPF2_ENA */ +#define ARIZONA_LHPF2_ENA_MASK 0x0001 /* LHPF2_ENA */ +#define ARIZONA_LHPF2_ENA_SHIFT 0 /* LHPF2_ENA */ +#define ARIZONA_LHPF2_ENA_WIDTH 1 /* LHPF2_ENA */ + +/* + * R3781 (0xEC5) - HPLPF2_2 + */ +#define ARIZONA_LHPF2_COEFF_MASK 0xFFFF /* LHPF2_COEFF - [15:0] */ +#define ARIZONA_LHPF2_COEFF_SHIFT 0 /* LHPF2_COEFF - [15:0] */ +#define ARIZONA_LHPF2_COEFF_WIDTH 16 /* LHPF2_COEFF - [15:0] */ + +/* + * R3784 (0xEC8) - HPLPF3_1 + */ +#define ARIZONA_LHPF3_MODE 0x0002 /* LHPF3_MODE */ +#define ARIZONA_LHPF3_MODE_MASK 0x0002 /* LHPF3_MODE */ +#define ARIZONA_LHPF3_MODE_SHIFT 1 /* LHPF3_MODE */ +#define ARIZONA_LHPF3_MODE_WIDTH 1 /* LHPF3_MODE */ +#define ARIZONA_LHPF3_ENA 0x0001 /* LHPF3_ENA */ +#define ARIZONA_LHPF3_ENA_MASK 0x0001 /* LHPF3_ENA */ +#define ARIZONA_LHPF3_ENA_SHIFT 0 /* LHPF3_ENA */ +#define ARIZONA_LHPF3_ENA_WIDTH 1 /* LHPF3_ENA */ + +/* + * R3785 (0xEC9) - HPLPF3_2 + */ +#define ARIZONA_LHPF3_COEFF_MASK 0xFFFF /* LHPF3_COEFF - [15:0] */ +#define ARIZONA_LHPF3_COEFF_SHIFT 0 /* LHPF3_COEFF - [15:0] */ +#define ARIZONA_LHPF3_COEFF_WIDTH 16 /* LHPF3_COEFF - [15:0] */ + +/* + * R3788 (0xECC) - HPLPF4_1 + */ +#define ARIZONA_LHPF4_MODE 0x0002 /* LHPF4_MODE */ +#define ARIZONA_LHPF4_MODE_MASK 0x0002 /* LHPF4_MODE */ +#define ARIZONA_LHPF4_MODE_SHIFT 1 /* LHPF4_MODE */ +#define ARIZONA_LHPF4_MODE_WIDTH 1 /* LHPF4_MODE */ +#define ARIZONA_LHPF4_ENA 0x0001 /* LHPF4_ENA */ +#define ARIZONA_LHPF4_ENA_MASK 0x0001 /* LHPF4_ENA */ +#define ARIZONA_LHPF4_ENA_SHIFT 0 /* LHPF4_ENA */ +#define ARIZONA_LHPF4_ENA_WIDTH 1 /* LHPF4_ENA */ + +/* + * R3789 (0xECD) - HPLPF4_2 + */ +#define ARIZONA_LHPF4_COEFF_MASK 0xFFFF /* LHPF4_COEFF - [15:0] */ +#define ARIZONA_LHPF4_COEFF_SHIFT 0 /* LHPF4_COEFF - [15:0] */ +#define ARIZONA_LHPF4_COEFF_WIDTH 16 /* LHPF4_COEFF - [15:0] */ + +/* + * R3808 (0xEE0) - ASRC_ENABLE + */ +#define ARIZONA_ASRC2L_ENA 0x0008 /* ASRC2L_ENA */ +#define ARIZONA_ASRC2L_ENA_MASK 0x0008 /* ASRC2L_ENA */ +#define ARIZONA_ASRC2L_ENA_SHIFT 3 /* ASRC2L_ENA */ +#define ARIZONA_ASRC2L_ENA_WIDTH 1 /* ASRC2L_ENA */ +#define ARIZONA_ASRC2R_ENA 0x0004 /* ASRC2R_ENA */ +#define ARIZONA_ASRC2R_ENA_MASK 0x0004 /* ASRC2R_ENA */ +#define ARIZONA_ASRC2R_ENA_SHIFT 2 /* ASRC2R_ENA */ +#define ARIZONA_ASRC2R_ENA_WIDTH 1 /* ASRC2R_ENA */ +#define ARIZONA_ASRC1L_ENA 0x0002 /* ASRC1L_ENA */ +#define ARIZONA_ASRC1L_ENA_MASK 0x0002 /* ASRC1L_ENA */ +#define ARIZONA_ASRC1L_ENA_SHIFT 1 /* ASRC1L_ENA */ +#define ARIZONA_ASRC1L_ENA_WIDTH 1 /* ASRC1L_ENA */ +#define ARIZONA_ASRC1R_ENA 0x0001 /* ASRC1R_ENA */ +#define ARIZONA_ASRC1R_ENA_MASK 0x0001 /* ASRC1R_ENA */ +#define ARIZONA_ASRC1R_ENA_SHIFT 0 /* ASRC1R_ENA */ +#define ARIZONA_ASRC1R_ENA_WIDTH 1 /* ASRC1R_ENA */ + +/* + * R3810 (0xEE2) - ASRC_RATE1 + */ +#define ARIZONA_ASRC_RATE1_MASK 0x7800 /* ASRC_RATE1 - [14:11] */ +#define ARIZONA_ASRC_RATE1_SHIFT 11 /* ASRC_RATE1 - [14:11] */ +#define ARIZONA_ASRC_RATE1_WIDTH 4 /* ASRC_RATE1 - [14:11] */ + +/* + * R3811 (0xEE3) - ASRC_RATE2 + */ +#define ARIZONA_ASRC_RATE2_MASK 0x7800 /* ASRC_RATE2 - [14:11] */ +#define ARIZONA_ASRC_RATE2_SHIFT 11 /* ASRC_RATE2 - [14:11] */ +#define ARIZONA_ASRC_RATE2_WIDTH 4 /* ASRC_RATE2 - [14:11] */ + +/* + * R3824 (0xEF0) - ISRC 1 CTRL 1 + */ +#define ARIZONA_ISRC1_FSH_MASK 0x7800 /* ISRC1_FSH - [14:11] */ +#define ARIZONA_ISRC1_FSH_SHIFT 11 /* ISRC1_FSH - [14:11] */ +#define ARIZONA_ISRC1_FSH_WIDTH 4 /* ISRC1_FSH - [14:11] */ +#define ARIZONA_ISRC1_CLK_SEL_MASK 0x0700 /* ISRC1_CLK_SEL - [10:8] */ +#define ARIZONA_ISRC1_CLK_SEL_SHIFT 8 /* ISRC1_CLK_SEL - [10:8] */ +#define ARIZONA_ISRC1_CLK_SEL_WIDTH 3 /* ISRC1_CLK_SEL - [10:8] */ + +/* + * R3825 (0xEF1) - ISRC 1 CTRL 2 + */ +#define ARIZONA_ISRC1_FSL_MASK 0x7800 /* ISRC1_FSL - [14:11] */ +#define ARIZONA_ISRC1_FSL_SHIFT 11 /* ISRC1_FSL - [14:11] */ +#define ARIZONA_ISRC1_FSL_WIDTH 4 /* ISRC1_FSL - [14:11] */ + +/* + * R3826 (0xEF2) - ISRC 1 CTRL 3 + */ +#define ARIZONA_ISRC1_INT0_ENA 0x8000 /* ISRC1_INT0_ENA */ +#define ARIZONA_ISRC1_INT0_ENA_MASK 0x8000 /* ISRC1_INT0_ENA */ +#define ARIZONA_ISRC1_INT0_ENA_SHIFT 15 /* ISRC1_INT0_ENA */ +#define ARIZONA_ISRC1_INT0_ENA_WIDTH 1 /* ISRC1_INT0_ENA */ +#define ARIZONA_ISRC1_INT1_ENA 0x4000 /* ISRC1_INT1_ENA */ +#define ARIZONA_ISRC1_INT1_ENA_MASK 0x4000 /* ISRC1_INT1_ENA */ +#define ARIZONA_ISRC1_INT1_ENA_SHIFT 14 /* ISRC1_INT1_ENA */ +#define ARIZONA_ISRC1_INT1_ENA_WIDTH 1 /* ISRC1_INT1_ENA */ +#define ARIZONA_ISRC1_INT2_ENA 0x2000 /* ISRC1_INT2_ENA */ +#define ARIZONA_ISRC1_INT2_ENA_MASK 0x2000 /* ISRC1_INT2_ENA */ +#define ARIZONA_ISRC1_INT2_ENA_SHIFT 13 /* ISRC1_INT2_ENA */ +#define ARIZONA_ISRC1_INT2_ENA_WIDTH 1 /* ISRC1_INT2_ENA */ +#define ARIZONA_ISRC1_INT3_ENA 0x1000 /* ISRC1_INT3_ENA */ +#define ARIZONA_ISRC1_INT3_ENA_MASK 0x1000 /* ISRC1_INT3_ENA */ +#define ARIZONA_ISRC1_INT3_ENA_SHIFT 12 /* ISRC1_INT3_ENA */ +#define ARIZONA_ISRC1_INT3_ENA_WIDTH 1 /* ISRC1_INT3_ENA */ +#define ARIZONA_ISRC1_DEC0_ENA 0x0200 /* ISRC1_DEC0_ENA */ +#define ARIZONA_ISRC1_DEC0_ENA_MASK 0x0200 /* ISRC1_DEC0_ENA */ +#define ARIZONA_ISRC1_DEC0_ENA_SHIFT 9 /* ISRC1_DEC0_ENA */ +#define ARIZONA_ISRC1_DEC0_ENA_WIDTH 1 /* ISRC1_DEC0_ENA */ +#define ARIZONA_ISRC1_DEC1_ENA 0x0100 /* ISRC1_DEC1_ENA */ +#define ARIZONA_ISRC1_DEC1_ENA_MASK 0x0100 /* ISRC1_DEC1_ENA */ +#define ARIZONA_ISRC1_DEC1_ENA_SHIFT 8 /* ISRC1_DEC1_ENA */ +#define ARIZONA_ISRC1_DEC1_ENA_WIDTH 1 /* ISRC1_DEC1_ENA */ +#define ARIZONA_ISRC1_DEC2_ENA 0x0080 /* ISRC1_DEC2_ENA */ +#define ARIZONA_ISRC1_DEC2_ENA_MASK 0x0080 /* ISRC1_DEC2_ENA */ +#define ARIZONA_ISRC1_DEC2_ENA_SHIFT 7 /* ISRC1_DEC2_ENA */ +#define ARIZONA_ISRC1_DEC2_ENA_WIDTH 1 /* ISRC1_DEC2_ENA */ +#define ARIZONA_ISRC1_DEC3_ENA 0x0040 /* ISRC1_DEC3_ENA */ +#define ARIZONA_ISRC1_DEC3_ENA_MASK 0x0040 /* ISRC1_DEC3_ENA */ +#define ARIZONA_ISRC1_DEC3_ENA_SHIFT 6 /* ISRC1_DEC3_ENA */ +#define ARIZONA_ISRC1_DEC3_ENA_WIDTH 1 /* ISRC1_DEC3_ENA */ +#define ARIZONA_ISRC1_NOTCH_ENA 0x0001 /* ISRC1_NOTCH_ENA */ +#define ARIZONA_ISRC1_NOTCH_ENA_MASK 0x0001 /* ISRC1_NOTCH_ENA */ +#define ARIZONA_ISRC1_NOTCH_ENA_SHIFT 0 /* ISRC1_NOTCH_ENA */ +#define ARIZONA_ISRC1_NOTCH_ENA_WIDTH 1 /* ISRC1_NOTCH_ENA */ + +/* + * R3827 (0xEF3) - ISRC 2 CTRL 1 + */ +#define ARIZONA_ISRC2_FSH_MASK 0x7800 /* ISRC2_FSH - [14:11] */ +#define ARIZONA_ISRC2_FSH_SHIFT 11 /* ISRC2_FSH - [14:11] */ +#define ARIZONA_ISRC2_FSH_WIDTH 4 /* ISRC2_FSH - [14:11] */ +#define ARIZONA_ISRC2_CLK_SEL_MASK 0x0700 /* ISRC2_CLK_SEL - [10:8] */ +#define ARIZONA_ISRC2_CLK_SEL_SHIFT 8 /* ISRC2_CLK_SEL - [10:8] */ +#define ARIZONA_ISRC2_CLK_SEL_WIDTH 3 /* ISRC2_CLK_SEL - [10:8] */ + +/* + * R3828 (0xEF4) - ISRC 2 CTRL 2 + */ +#define ARIZONA_ISRC2_FSL_MASK 0x7800 /* ISRC2_FSL - [14:11] */ +#define ARIZONA_ISRC2_FSL_SHIFT 11 /* ISRC2_FSL - [14:11] */ +#define ARIZONA_ISRC2_FSL_WIDTH 4 /* ISRC2_FSL - [14:11] */ + +/* + * R3829 (0xEF5) - ISRC 2 CTRL 3 + */ +#define ARIZONA_ISRC2_INT0_ENA 0x8000 /* ISRC2_INT0_ENA */ +#define ARIZONA_ISRC2_INT0_ENA_MASK 0x8000 /* ISRC2_INT0_ENA */ +#define ARIZONA_ISRC2_INT0_ENA_SHIFT 15 /* ISRC2_INT0_ENA */ +#define ARIZONA_ISRC2_INT0_ENA_WIDTH 1 /* ISRC2_INT0_ENA */ +#define ARIZONA_ISRC2_INT1_ENA 0x4000 /* ISRC2_INT1_ENA */ +#define ARIZONA_ISRC2_INT1_ENA_MASK 0x4000 /* ISRC2_INT1_ENA */ +#define ARIZONA_ISRC2_INT1_ENA_SHIFT 14 /* ISRC2_INT1_ENA */ +#define ARIZONA_ISRC2_INT1_ENA_WIDTH 1 /* ISRC2_INT1_ENA */ +#define ARIZONA_ISRC2_INT2_ENA 0x2000 /* ISRC2_INT2_ENA */ +#define ARIZONA_ISRC2_INT2_ENA_MASK 0x2000 /* ISRC2_INT2_ENA */ +#define ARIZONA_ISRC2_INT2_ENA_SHIFT 13 /* ISRC2_INT2_ENA */ +#define ARIZONA_ISRC2_INT2_ENA_WIDTH 1 /* ISRC2_INT2_ENA */ +#define ARIZONA_ISRC2_INT3_ENA 0x1000 /* ISRC2_INT3_ENA */ +#define ARIZONA_ISRC2_INT3_ENA_MASK 0x1000 /* ISRC2_INT3_ENA */ +#define ARIZONA_ISRC2_INT3_ENA_SHIFT 12 /* ISRC2_INT3_ENA */ +#define ARIZONA_ISRC2_INT3_ENA_WIDTH 1 /* ISRC2_INT3_ENA */ +#define ARIZONA_ISRC2_DEC0_ENA 0x0200 /* ISRC2_DEC0_ENA */ +#define ARIZONA_ISRC2_DEC0_ENA_MASK 0x0200 /* ISRC2_DEC0_ENA */ +#define ARIZONA_ISRC2_DEC0_ENA_SHIFT 9 /* ISRC2_DEC0_ENA */ +#define ARIZONA_ISRC2_DEC0_ENA_WIDTH 1 /* ISRC2_DEC0_ENA */ +#define ARIZONA_ISRC2_DEC1_ENA 0x0100 /* ISRC2_DEC1_ENA */ +#define ARIZONA_ISRC2_DEC1_ENA_MASK 0x0100 /* ISRC2_DEC1_ENA */ +#define ARIZONA_ISRC2_DEC1_ENA_SHIFT 8 /* ISRC2_DEC1_ENA */ +#define ARIZONA_ISRC2_DEC1_ENA_WIDTH 1 /* ISRC2_DEC1_ENA */ +#define ARIZONA_ISRC2_DEC2_ENA 0x0080 /* ISRC2_DEC2_ENA */ +#define ARIZONA_ISRC2_DEC2_ENA_MASK 0x0080 /* ISRC2_DEC2_ENA */ +#define ARIZONA_ISRC2_DEC2_ENA_SHIFT 7 /* ISRC2_DEC2_ENA */ +#define ARIZONA_ISRC2_DEC2_ENA_WIDTH 1 /* ISRC2_DEC2_ENA */ +#define ARIZONA_ISRC2_DEC3_ENA 0x0040 /* ISRC2_DEC3_ENA */ +#define ARIZONA_ISRC2_DEC3_ENA_MASK 0x0040 /* ISRC2_DEC3_ENA */ +#define ARIZONA_ISRC2_DEC3_ENA_SHIFT 6 /* ISRC2_DEC3_ENA */ +#define ARIZONA_ISRC2_DEC3_ENA_WIDTH 1 /* ISRC2_DEC3_ENA */ +#define ARIZONA_ISRC2_NOTCH_ENA 0x0001 /* ISRC2_NOTCH_ENA */ +#define ARIZONA_ISRC2_NOTCH_ENA_MASK 0x0001 /* ISRC2_NOTCH_ENA */ +#define ARIZONA_ISRC2_NOTCH_ENA_SHIFT 0 /* ISRC2_NOTCH_ENA */ +#define ARIZONA_ISRC2_NOTCH_ENA_WIDTH 1 /* ISRC2_NOTCH_ENA */ + +/* + * R3830 (0xEF6) - ISRC 3 CTRL 1 + */ +#define ARIZONA_ISRC3_FSH_MASK 0x7800 /* ISRC3_FSH - [14:11] */ +#define ARIZONA_ISRC3_FSH_SHIFT 11 /* ISRC3_FSH - [14:11] */ +#define ARIZONA_ISRC3_FSH_WIDTH 4 /* ISRC3_FSH - [14:11] */ +#define ARIZONA_ISRC3_CLK_SEL_MASK 0x0700 /* ISRC3_CLK_SEL - [10:8] */ +#define ARIZONA_ISRC3_CLK_SEL_SHIFT 8 /* ISRC3_CLK_SEL - [10:8] */ +#define ARIZONA_ISRC3_CLK_SEL_WIDTH 3 /* ISRC3_CLK_SEL - [10:8] */ + +/* + * R3831 (0xEF7) - ISRC 3 CTRL 2 + */ +#define ARIZONA_ISRC3_FSL_MASK 0x7800 /* ISRC3_FSL - [14:11] */ +#define ARIZONA_ISRC3_FSL_SHIFT 11 /* ISRC3_FSL - [14:11] */ +#define ARIZONA_ISRC3_FSL_WIDTH 4 /* ISRC3_FSL - [14:11] */ + +/* + * R3832 (0xEF8) - ISRC 3 CTRL 3 + */ +#define ARIZONA_ISRC3_INT0_ENA 0x8000 /* ISRC3_INT0_ENA */ +#define ARIZONA_ISRC3_INT0_ENA_MASK 0x8000 /* ISRC3_INT0_ENA */ +#define ARIZONA_ISRC3_INT0_ENA_SHIFT 15 /* ISRC3_INT0_ENA */ +#define ARIZONA_ISRC3_INT0_ENA_WIDTH 1 /* ISRC3_INT0_ENA */ +#define ARIZONA_ISRC3_INT1_ENA 0x4000 /* ISRC3_INT1_ENA */ +#define ARIZONA_ISRC3_INT1_ENA_MASK 0x4000 /* ISRC3_INT1_ENA */ +#define ARIZONA_ISRC3_INT1_ENA_SHIFT 14 /* ISRC3_INT1_ENA */ +#define ARIZONA_ISRC3_INT1_ENA_WIDTH 1 /* ISRC3_INT1_ENA */ +#define ARIZONA_ISRC3_INT2_ENA 0x2000 /* ISRC3_INT2_ENA */ +#define ARIZONA_ISRC3_INT2_ENA_MASK 0x2000 /* ISRC3_INT2_ENA */ +#define ARIZONA_ISRC3_INT2_ENA_SHIFT 13 /* ISRC3_INT2_ENA */ +#define ARIZONA_ISRC3_INT2_ENA_WIDTH 1 /* ISRC3_INT2_ENA */ +#define ARIZONA_ISRC3_INT3_ENA 0x1000 /* ISRC3_INT3_ENA */ +#define ARIZONA_ISRC3_INT3_ENA_MASK 0x1000 /* ISRC3_INT3_ENA */ +#define ARIZONA_ISRC3_INT3_ENA_SHIFT 12 /* ISRC3_INT3_ENA */ +#define ARIZONA_ISRC3_INT3_ENA_WIDTH 1 /* ISRC3_INT3_ENA */ +#define ARIZONA_ISRC3_DEC0_ENA 0x0200 /* ISRC3_DEC0_ENA */ +#define ARIZONA_ISRC3_DEC0_ENA_MASK 0x0200 /* ISRC3_DEC0_ENA */ +#define ARIZONA_ISRC3_DEC0_ENA_SHIFT 9 /* ISRC3_DEC0_ENA */ +#define ARIZONA_ISRC3_DEC0_ENA_WIDTH 1 /* ISRC3_DEC0_ENA */ +#define ARIZONA_ISRC3_DEC1_ENA 0x0100 /* ISRC3_DEC1_ENA */ +#define ARIZONA_ISRC3_DEC1_ENA_MASK 0x0100 /* ISRC3_DEC1_ENA */ +#define ARIZONA_ISRC3_DEC1_ENA_SHIFT 8 /* ISRC3_DEC1_ENA */ +#define ARIZONA_ISRC3_DEC1_ENA_WIDTH 1 /* ISRC3_DEC1_ENA */ +#define ARIZONA_ISRC3_DEC2_ENA 0x0080 /* ISRC3_DEC2_ENA */ +#define ARIZONA_ISRC3_DEC2_ENA_MASK 0x0080 /* ISRC3_DEC2_ENA */ +#define ARIZONA_ISRC3_DEC2_ENA_SHIFT 7 /* ISRC3_DEC2_ENA */ +#define ARIZONA_ISRC3_DEC2_ENA_WIDTH 1 /* ISRC3_DEC2_ENA */ +#define ARIZONA_ISRC3_DEC3_ENA 0x0040 /* ISRC3_DEC3_ENA */ +#define ARIZONA_ISRC3_DEC3_ENA_MASK 0x0040 /* ISRC3_DEC3_ENA */ +#define ARIZONA_ISRC3_DEC3_ENA_SHIFT 6 /* ISRC3_DEC3_ENA */ +#define ARIZONA_ISRC3_DEC3_ENA_WIDTH 1 /* ISRC3_DEC3_ENA */ +#define ARIZONA_ISRC3_NOTCH_ENA 0x0001 /* ISRC3_NOTCH_ENA */ +#define ARIZONA_ISRC3_NOTCH_ENA_MASK 0x0001 /* ISRC3_NOTCH_ENA */ +#define ARIZONA_ISRC3_NOTCH_ENA_SHIFT 0 /* ISRC3_NOTCH_ENA */ +#define ARIZONA_ISRC3_NOTCH_ENA_WIDTH 1 /* ISRC3_NOTCH_ENA */ + +/* + * R4352 (0x1100) - DSP1 Control 1 + */ +#define ARIZONA_DSP1_RATE_MASK 0x7800 /* DSP1_RATE - [14:11] */ +#define ARIZONA_DSP1_RATE_SHIFT 11 /* DSP1_RATE - [14:11] */ +#define ARIZONA_DSP1_RATE_WIDTH 4 /* DSP1_RATE - [14:11] */ +#define ARIZONA_DSP1_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ +#define ARIZONA_DSP1_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ +#define ARIZONA_DSP1_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ +#define ARIZONA_DSP1_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ +#define ARIZONA_DSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ +#define ARIZONA_DSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ +#define ARIZONA_DSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ +#define ARIZONA_DSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ +#define ARIZONA_DSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ +#define ARIZONA_DSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ +#define ARIZONA_DSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ +#define ARIZONA_DSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ +#define ARIZONA_DSP1_START 0x0001 /* DSP1_START */ +#define ARIZONA_DSP1_START_MASK 0x0001 /* DSP1_START */ +#define ARIZONA_DSP1_START_SHIFT 0 /* DSP1_START */ +#define ARIZONA_DSP1_START_WIDTH 1 /* DSP1_START */ + +/* + * R4353 (0x1101) - DSP1 Clocking 1 + */ +#define ARIZONA_DSP1_CLK_SEL_MASK 0x0007 /* DSP1_CLK_SEL - [2:0] */ +#define ARIZONA_DSP1_CLK_SEL_SHIFT 0 /* DSP1_CLK_SEL - [2:0] */ +#define ARIZONA_DSP1_CLK_SEL_WIDTH 3 /* DSP1_CLK_SEL - [2:0] */ + +/* + * R4356 (0x1104) - DSP1 Status 1 + */ +#define ARIZONA_DSP1_RAM_RDY 0x0001 /* DSP1_RAM_RDY */ +#define ARIZONA_DSP1_RAM_RDY_MASK 0x0001 /* DSP1_RAM_RDY */ +#define ARIZONA_DSP1_RAM_RDY_SHIFT 0 /* DSP1_RAM_RDY */ +#define ARIZONA_DSP1_RAM_RDY_WIDTH 1 /* DSP1_RAM_RDY */ + +/* + * R4357 (0x1105) - DSP1 Status 2 + */ +#define ARIZONA_DSP1_PING_FULL 0x8000 /* DSP1_PING_FULL */ +#define ARIZONA_DSP1_PING_FULL_MASK 0x8000 /* DSP1_PING_FULL */ +#define ARIZONA_DSP1_PING_FULL_SHIFT 15 /* DSP1_PING_FULL */ +#define ARIZONA_DSP1_PING_FULL_WIDTH 1 /* DSP1_PING_FULL */ +#define ARIZONA_DSP1_PONG_FULL 0x4000 /* DSP1_PONG_FULL */ +#define ARIZONA_DSP1_PONG_FULL_MASK 0x4000 /* DSP1_PONG_FULL */ +#define ARIZONA_DSP1_PONG_FULL_SHIFT 14 /* DSP1_PONG_FULL */ +#define ARIZONA_DSP1_PONG_FULL_WIDTH 1 /* DSP1_PONG_FULL */ +#define ARIZONA_DSP1_WDMA_ACTIVE_CHANNELS_MASK 0x00FF /* DSP1_WDMA_ACTIVE_CHANNELS - [7:0] */ +#define ARIZONA_DSP1_WDMA_ACTIVE_CHANNELS_SHIFT 0 /* DSP1_WDMA_ACTIVE_CHANNELS - [7:0] */ +#define ARIZONA_DSP1_WDMA_ACTIVE_CHANNELS_WIDTH 8 /* DSP1_WDMA_ACTIVE_CHANNELS - [7:0] */ + +#endif -- cgit v1.2.3 From 3cc72986947501a6a8fd12330e0963b59ed2f964 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 19 Jun 2012 16:31:53 +0100 Subject: mfd: arizona: Core driver Several forthcoming Wolfson devices are based on a common platform known as Arizona allowing a great deal of reuse of driver code. This patch adds core support for these devices. In order to handle systems which do not use the generic clock API a simple wrapper for the 32kHz clock domain in the devices is provided. Once the generic clock API is widely available this code will be moved over to use that. For simplicity some WM5102 specific code is included in the core driver, the effort involved in splitting the device out isn't worth it. Signed-off-by: Mark Brown --- drivers/mfd/arizona-core.c | 527 ++++++++++++++++++++++++++++++++++++++ drivers/mfd/arizona.h | 33 +++ include/linux/mfd/arizona/core.h | 102 ++++++++ include/linux/mfd/arizona/pdata.h | 119 +++++++++ 4 files changed, 781 insertions(+) create mode 100644 drivers/mfd/arizona-core.c create mode 100644 drivers/mfd/arizona.h create mode 100644 include/linux/mfd/arizona/core.h create mode 100644 include/linux/mfd/arizona/pdata.h (limited to 'include') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c new file mode 100644 index 000000000000..42cb28b2b5c8 --- /dev/null +++ b/drivers/mfd/arizona-core.c @@ -0,0 +1,527 @@ +/* + * Arizona core driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "arizona.h" + +static const char *wm5102_core_supplies[] = { + "AVDD", + "DBVDD1", + "DCVDD", +}; + +int arizona_clk32k_enable(struct arizona *arizona) +{ + int ret = 0; + + mutex_lock(&arizona->clk_lock); + + arizona->clk32k_ref++; + + if (arizona->clk32k_ref == 1) + ret = regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, + ARIZONA_CLK_32K_ENA, + ARIZONA_CLK_32K_ENA); + + if (ret != 0) + arizona->clk32k_ref--; + + mutex_unlock(&arizona->clk_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(arizona_clk32k_enable); + +int arizona_clk32k_disable(struct arizona *arizona) +{ + int ret = 0; + + mutex_lock(&arizona->clk_lock); + + BUG_ON(arizona->clk32k_ref <= 0); + + arizona->clk32k_ref--; + + if (arizona->clk32k_ref == 0) + regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, + ARIZONA_CLK_32K_ENA, 0); + + mutex_unlock(&arizona->clk_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(arizona_clk32k_disable); + +static irqreturn_t arizona_clkgen_err(int irq, void *data) +{ + struct arizona *arizona = data; + + dev_err(arizona->dev, "CLKGEN error\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t arizona_underclocked(int irq, void *data) +{ + struct arizona *arizona = data; + unsigned int val; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_8, + &val); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read underclock status: %d\n", + ret); + return IRQ_NONE; + } + + if (val & ARIZONA_AIF3_UNDERCLOCKED_STS) + dev_err(arizona->dev, "AIF3 underclocked\n"); + if (val & ARIZONA_AIF3_UNDERCLOCKED_STS) + dev_err(arizona->dev, "AIF3 underclocked\n"); + if (val & ARIZONA_AIF2_UNDERCLOCKED_STS) + dev_err(arizona->dev, "AIF1 underclocked\n"); + if (val & ARIZONA_ISRC2_UNDERCLOCKED_STS) + dev_err(arizona->dev, "ISRC2 underclocked\n"); + if (val & ARIZONA_ISRC1_UNDERCLOCKED_STS) + dev_err(arizona->dev, "ISRC1 underclocked\n"); + if (val & ARIZONA_FX_UNDERCLOCKED_STS) + dev_err(arizona->dev, "FX underclocked\n"); + if (val & ARIZONA_ASRC_UNDERCLOCKED_STS) + dev_err(arizona->dev, "ASRC underclocked\n"); + if (val & ARIZONA_DAC_UNDERCLOCKED_STS) + dev_err(arizona->dev, "DAC underclocked\n"); + if (val & ARIZONA_ADC_UNDERCLOCKED_STS) + dev_err(arizona->dev, "ADC underclocked\n"); + if (val & ARIZONA_MIXER_UNDERCLOCKED_STS) + dev_err(arizona->dev, "Mixer underclocked\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t arizona_overclocked(int irq, void *data) +{ + struct arizona *arizona = data; + unsigned int val[2]; + int ret; + + ret = regmap_bulk_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_6, + &val[0], 2); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read overclock status: %d\n", + ret); + return IRQ_NONE; + } + + if (val[0] & ARIZONA_PWM_OVERCLOCKED_STS) + dev_err(arizona->dev, "PWM overclocked\n"); + if (val[0] & ARIZONA_FX_CORE_OVERCLOCKED_STS) + dev_err(arizona->dev, "FX core overclocked\n"); + if (val[0] & ARIZONA_DAC_SYS_OVERCLOCKED_STS) + dev_err(arizona->dev, "DAC SYS overclocked\n"); + if (val[0] & ARIZONA_DAC_WARP_OVERCLOCKED_STS) + dev_err(arizona->dev, "DAC WARP overclocked\n"); + if (val[0] & ARIZONA_ADC_OVERCLOCKED_STS) + dev_err(arizona->dev, "ADC overclocked\n"); + if (val[0] & ARIZONA_MIXER_OVERCLOCKED_STS) + dev_err(arizona->dev, "Mixer overclocked\n"); + if (val[0] & ARIZONA_AIF3_SYNC_OVERCLOCKED_STS) + dev_err(arizona->dev, "AIF3 overclocked\n"); + if (val[0] & ARIZONA_AIF2_SYNC_OVERCLOCKED_STS) + dev_err(arizona->dev, "AIF2 overclocked\n"); + if (val[0] & ARIZONA_AIF1_SYNC_OVERCLOCKED_STS) + dev_err(arizona->dev, "AIF1 overclocked\n"); + if (val[0] & ARIZONA_PAD_CTRL_OVERCLOCKED_STS) + dev_err(arizona->dev, "Pad control overclocked\n"); + + if (val[1] & ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS) + dev_err(arizona->dev, "Slimbus subsystem overclocked\n"); + if (val[1] & ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS) + dev_err(arizona->dev, "Slimbus async overclocked\n"); + if (val[1] & ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS) + dev_err(arizona->dev, "Slimbus sync overclocked\n"); + if (val[1] & ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS) + dev_err(arizona->dev, "ASRC async system overclocked\n"); + if (val[1] & ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS) + dev_err(arizona->dev, "ASRC async WARP overclocked\n"); + if (val[1] & ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS) + dev_err(arizona->dev, "ASRC sync system overclocked\n"); + if (val[1] & ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS) + dev_err(arizona->dev, "ASRC sync WARP overclocked\n"); + if (val[1] & ARIZONA_ADSP2_1_OVERCLOCKED_STS) + dev_err(arizona->dev, "DSP1 overclocked\n"); + if (val[1] & ARIZONA_ISRC2_OVERCLOCKED_STS) + dev_err(arizona->dev, "ISRC2 overclocked\n"); + if (val[1] & ARIZONA_ISRC1_OVERCLOCKED_STS) + dev_err(arizona->dev, "ISRC1 overclocked\n"); + + return IRQ_HANDLED; +} + +static int arizona_wait_for_boot(struct arizona *arizona) +{ + unsigned int reg; + int ret, i; + + /* + * We can't use an interrupt as we need to runtime resume to do so, + * we won't race with the interrupt handler as it'll be blocked on + * runtime resume. + */ + for (i = 0; i < 5; i++) { + msleep(1); + + ret = regmap_read(arizona->regmap, + ARIZONA_INTERRUPT_RAW_STATUS_5, ®); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read boot state: %d\n", + ret); + return ret; + } + + if (reg & ARIZONA_BOOT_DONE_STS) + break; + } + + if (reg & ARIZONA_BOOT_DONE_STS) { + regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5, + ARIZONA_BOOT_DONE_STS); + } else { + dev_err(arizona->dev, "Device boot timed out: %x\n", reg); + return -ETIMEDOUT; + } + + pm_runtime_mark_last_busy(arizona->dev); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME +static int arizona_runtime_resume(struct device *dev) +{ + struct arizona *arizona = dev_get_drvdata(dev); + int ret; + + if (arizona->pdata.ldoena) + gpio_set_value_cansleep(arizona->pdata.ldoena, 1); + + regcache_cache_only(arizona->regmap, false); + + ret = arizona_wait_for_boot(arizona); + if (ret != 0) + return ret; + + regcache_sync(arizona->regmap); + + return 0; +} + +static int arizona_runtime_suspend(struct device *dev) +{ + struct arizona *arizona = dev_get_drvdata(dev); + + if (arizona->pdata.ldoena) { + gpio_set_value_cansleep(arizona->pdata.ldoena, 0); + regcache_cache_only(arizona->regmap, true); + regcache_mark_dirty(arizona->regmap); + } + + return 0; +} +#endif + +const struct dev_pm_ops arizona_pm_ops = { + SET_RUNTIME_PM_OPS(arizona_runtime_suspend, + arizona_runtime_resume, + NULL) +}; +EXPORT_SYMBOL_GPL(arizona_pm_ops); + +static struct mfd_cell early_devs[] = { + { .name = "arizona-ldo1" }, +}; + +static struct mfd_cell wm5102_devs[] = { + { .name = "arizona-extcon" }, + { .name = "arizona-gpio" }, + { .name = "arizona-micsupp" }, + { .name = "arizona-pwm" }, + { .name = "wm5102-codec" }, +}; + +int __devinit arizona_dev_init(struct arizona *arizona) +{ + struct device *dev = arizona->dev; + const char *type_name; + unsigned int reg, val; + int ret, i; + + dev_set_drvdata(arizona->dev, arizona); + mutex_init(&arizona->clk_lock); + + if (dev_get_platdata(arizona->dev)) + memcpy(&arizona->pdata, dev_get_platdata(arizona->dev), + sizeof(arizona->pdata)); + + regcache_cache_only(arizona->regmap, true); + + switch (arizona->type) { + case WM5102: + for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++) + arizona->core_supplies[i].supply + = wm5102_core_supplies[i]; + arizona->num_core_supplies = ARRAY_SIZE(wm5102_core_supplies); + break; + default: + dev_err(arizona->dev, "Unknown device type %d\n", + arizona->type); + return -EINVAL; + } + + ret = mfd_add_devices(arizona->dev, -1, early_devs, + ARRAY_SIZE(early_devs), NULL, 0); + if (ret != 0) { + dev_err(dev, "Failed to add early children: %d\n", ret); + return ret; + } + + ret = devm_regulator_bulk_get(dev, arizona->num_core_supplies, + arizona->core_supplies); + if (ret != 0) { + dev_err(dev, "Failed to request core supplies: %d\n", + ret); + goto err_early; + } + + ret = regulator_bulk_enable(arizona->num_core_supplies, + arizona->core_supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable core supplies: %d\n", + ret); + goto err_early; + } + + if (arizona->pdata.reset) { + /* Start out with /RESET low to put the chip into reset */ + ret = gpio_request_one(arizona->pdata.reset, + GPIOF_DIR_OUT | GPIOF_INIT_LOW, + "arizona /RESET"); + if (ret != 0) { + dev_err(dev, "Failed to request /RESET: %d\n", ret); + goto err_enable; + } + + gpio_set_value_cansleep(arizona->pdata.reset, 1); + } + + if (arizona->pdata.ldoena) { + ret = gpio_request_one(arizona->pdata.ldoena, + GPIOF_DIR_OUT | GPIOF_INIT_HIGH, + "arizona LDOENA"); + if (ret != 0) { + dev_err(dev, "Failed to request LDOENA: %d\n", ret); + goto err_reset; + } + } + + regcache_cache_only(arizona->regmap, false); + + ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, ®); + if (ret != 0) { + dev_err(dev, "Failed to read ID register: %d\n", ret); + goto err_ldoena; + } + + ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION, + &arizona->rev); + if (ret != 0) { + dev_err(dev, "Failed to read revision register: %d\n", ret); + goto err_ldoena; + } + arizona->rev &= ARIZONA_DEVICE_REVISION_MASK; + + switch (reg) { + case 0x5102: + type_name = "WM5102"; + if (arizona->type != WM5102) { + dev_err(arizona->dev, "WM5102 registered as %d\n", + arizona->type); + arizona->type = WM5102; + } + ret = wm5102_patch(arizona); + break; + + default: + dev_err(arizona->dev, "Unknown device ID %x\n", reg); + goto err_ldoena; + } + + dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A'); + + if (ret != 0) + dev_err(arizona->dev, "Failed to apply patch: %d\n", ret); + + /* If we have a /RESET GPIO we'll already be reset */ + if (!arizona->pdata.reset) { + ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0); + if (ret != 0) { + dev_err(dev, "Failed to reset device: %d\n", ret); + goto err_ldoena; + } + } + + arizona_wait_for_boot(arizona); + + for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { + if (!arizona->pdata.gpio_defaults[i]) + continue; + + regmap_write(arizona->regmap, ARIZONA_GPIO1_CTRL + i, + arizona->pdata.gpio_defaults[i]); + } + + pm_runtime_set_autosuspend_delay(arizona->dev, 100); + pm_runtime_use_autosuspend(arizona->dev); + pm_runtime_enable(arizona->dev); + + /* Chip default */ + if (!arizona->pdata.clk32k_src) + arizona->pdata.clk32k_src = ARIZONA_32KZ_MCLK2; + + switch (arizona->pdata.clk32k_src) { + case ARIZONA_32KZ_MCLK1: + case ARIZONA_32KZ_MCLK2: + regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, + ARIZONA_CLK_32K_SRC_MASK, + arizona->pdata.clk32k_src - 1); + break; + case ARIZONA_32KZ_NONE: + regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, + ARIZONA_CLK_32K_SRC_MASK, 2); + break; + default: + dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n", + arizona->pdata.clk32k_src); + ret = -EINVAL; + goto err_ldoena; + } + + for (i = 0; i < ARIZONA_MAX_INPUT; i++) { + /* Default for both is 0 so noop with defaults */ + val = arizona->pdata.dmic_ref[i] + << ARIZONA_IN1_DMIC_SUP_SHIFT; + val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT; + + regmap_update_bits(arizona->regmap, + ARIZONA_IN1L_CONTROL + (i * 8), + ARIZONA_IN1_DMIC_SUP_MASK | + ARIZONA_IN1_MODE_MASK, val); + } + + for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) { + /* Default is 0 so noop with defaults */ + if (arizona->pdata.out_mono[i]) + val = ARIZONA_OUT1_MONO; + else + val = 0; + + regmap_update_bits(arizona->regmap, + ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8), + ARIZONA_OUT1_MONO, val); + } + + BUILD_BUG_ON(ARIZONA_MAX_PDM_SPK > 1); + for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) { + if (arizona->pdata.spk_mute[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_PDM_SPK1_CTRL_1, + ARIZONA_SPK1_MUTE_ENDIAN_MASK | + ARIZONA_SPK1_MUTE_SEQ1_MASK, + arizona->pdata.spk_mute[i]); + + if (arizona->pdata.spk_fmt[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_PDM_SPK1_CTRL_2, + ARIZONA_SPK1_FMT_MASK, + arizona->pdata.spk_fmt[i]); + } + + /* Set up for interrupts */ + ret = arizona_irq_init(arizona); + if (ret != 0) + goto err_ldoena; + + arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error", + arizona_clkgen_err, arizona); + arizona_request_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, "Overclocked", + arizona_overclocked, arizona); + arizona_request_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, "Underclocked", + arizona_underclocked, arizona); + + switch (arizona->type) { + case WM5102: + ret = mfd_add_devices(arizona->dev, -1, wm5102_devs, + ARRAY_SIZE(wm5102_devs), NULL, 0); + break; + } + + if (ret != 0) { + dev_err(arizona->dev, "Failed to add subdevices: %d\n", ret); + goto err_irq; + } + + return 0; + +err_irq: + arizona_irq_exit(arizona); +err_ldoena: + if (arizona->pdata.ldoena) { + gpio_set_value_cansleep(arizona->pdata.ldoena, 0); + gpio_free(arizona->pdata.ldoena); + } +err_reset: + if (arizona->pdata.reset) { + gpio_set_value_cansleep(arizona->pdata.reset, 1); + gpio_free(arizona->pdata.reset); + } +err_enable: + regulator_bulk_disable(ARRAY_SIZE(arizona->core_supplies), + arizona->core_supplies); +err_early: + mfd_remove_devices(dev); + return ret; +} +EXPORT_SYMBOL_GPL(arizona_dev_init); + +int __devexit arizona_dev_exit(struct arizona *arizona) +{ + mfd_remove_devices(arizona->dev); + arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona); + arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona); + arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona); + pm_runtime_disable(arizona->dev); + arizona_irq_exit(arizona); + return 0; +} +EXPORT_SYMBOL_GPL(arizona_dev_exit); diff --git a/drivers/mfd/arizona.h b/drivers/mfd/arizona.h new file mode 100644 index 000000000000..1c9f333a9c17 --- /dev/null +++ b/drivers/mfd/arizona.h @@ -0,0 +1,33 @@ +/* + * wm5102.h -- WM5102 MFD internals + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WM5102_H +#define _WM5102_H + +#include +#include + +struct wm_arizona; + +extern const struct regmap_config wm5102_i2c_regmap; +extern const struct regmap_config wm5102_spi_regmap; +extern const struct dev_pm_ops arizona_pm_ops; + +extern const struct regmap_irq_chip wm5102_aod; +extern const struct regmap_irq_chip wm5102_irq; + +int arizona_dev_init(struct arizona *arizona); +int arizona_dev_exit(struct arizona *arizona); +int arizona_irq_init(struct arizona *arizona); +int arizona_irq_exit(struct arizona *arizona); + +#endif diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h new file mode 100644 index 000000000000..0157d845c2ff --- /dev/null +++ b/include/linux/mfd/arizona/core.h @@ -0,0 +1,102 @@ +/* + * Arizona MFD internals + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WM_ARIZONA_CORE_H +#define _WM_ARIZONA_CORE_H + +#include +#include +#include +#include + +#define ARIZONA_MAX_CORE_SUPPLIES 3 + +enum arizona_type { + WM5102 = 1, +}; + +#define ARIZONA_IRQ_GP1 0 +#define ARIZONA_IRQ_GP2 1 +#define ARIZONA_IRQ_GP3 2 +#define ARIZONA_IRQ_GP4 3 +#define ARIZONA_IRQ_GP5_FALL 4 +#define ARIZONA_IRQ_GP5_RISE 5 +#define ARIZONA_IRQ_JD_FALL 6 +#define ARIZONA_IRQ_JD_RISE 7 +#define ARIZONA_IRQ_DSP1_RAM_RDY 8 +#define ARIZONA_IRQ_DSP_IRQ1 9 +#define ARIZONA_IRQ_DSP_IRQ2 10 +#define ARIZONA_IRQ_SPK_SHUTDOWN_WARN 11 +#define ARIZONA_IRQ_SPK_SHUTDOWN 12 +#define ARIZONA_IRQ_MICDET 13 +#define ARIZONA_IRQ_HPDET 14 +#define ARIZONA_IRQ_WSEQ_DONE 15 +#define ARIZONA_IRQ_DRC2_SIG_DET 16 +#define ARIZONA_IRQ_DRC1_SIG_DET 17 +#define ARIZONA_IRQ_ASRC2_LOCK 18 +#define ARIZONA_IRQ_ASRC1_LOCK 19 +#define ARIZONA_IRQ_UNDERCLOCKED 20 +#define ARIZONA_IRQ_OVERCLOCKED 21 +#define ARIZONA_IRQ_FLL2_LOCK 22 +#define ARIZONA_IRQ_FLL1_LOCK 23 +#define ARIZONA_IRQ_CLKGEN_ERR 24 +#define ARIZONA_IRQ_CLKGEN_ERR_ASYNC 25 +#define ARIZONA_IRQ_ASRC_CFG_ERR 26 +#define ARIZONA_IRQ_AIF3_ERR 27 +#define ARIZONA_IRQ_AIF2_ERR 28 +#define ARIZONA_IRQ_AIF1_ERR 29 +#define ARIZONA_IRQ_CTRLIF_ERR 30 +#define ARIZONA_IRQ_MIXER_DROPPED_SAMPLES 31 +#define ARIZONA_IRQ_ASYNC_CLK_ENA_LOW 32 +#define ARIZONA_IRQ_SYSCLK_ENA_LOW 33 +#define ARIZONA_IRQ_ISRC1_CFG_ERR 34 +#define ARIZONA_IRQ_ISRC2_CFG_ERR 35 +#define ARIZONA_IRQ_BOOT_DONE 36 +#define ARIZONA_IRQ_DCS_DAC_DONE 37 +#define ARIZONA_IRQ_DCS_HP_DONE 38 +#define ARIZONA_IRQ_FLL2_CLOCK_OK 39 +#define ARIZONA_IRQ_FLL1_CLOCK_OK 40 + +#define ARIZONA_NUM_IRQ 41 + +struct arizona { + struct regmap *regmap; + struct device *dev; + + enum arizona_type type; + unsigned int rev; + + int num_core_supplies; + struct regulator_bulk_data core_supplies[ARIZONA_MAX_CORE_SUPPLIES]; + + struct arizona_pdata pdata; + + int irq; + struct irq_domain *virq; + struct regmap_irq_chip_data *aod_irq_chip; + struct regmap_irq_chip_data *irq_chip; + + struct mutex clk_lock; + int clk32k_ref; +}; + +int arizona_clk32k_enable(struct arizona *arizona); +int arizona_clk32k_disable(struct arizona *arizona); + +int arizona_request_irq(struct arizona *arizona, int irq, char *name, + irq_handler_t handler, void *data); +void arizona_free_irq(struct arizona *arizona, int irq, void *data); +int arizona_set_irq_wake(struct arizona *arizona, int irq, int on); + +int wm5102_patch(struct arizona *arizona); + +#endif diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h new file mode 100644 index 000000000000..fa2cb9885d62 --- /dev/null +++ b/include/linux/mfd/arizona/pdata.h @@ -0,0 +1,119 @@ +/* + * Platform data for Arizona devices + * + * Copyright 2012 Wolfson Microelectronics. PLC. + * + * 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 _ARIZONA_PDATA_H +#define _ARIZONA_PDATA_H + +#define ARIZONA_GPN_DIR 0x8000 /* GPN_DIR */ +#define ARIZONA_GPN_DIR_MASK 0x8000 /* GPN_DIR */ +#define ARIZONA_GPN_DIR_SHIFT 15 /* GPN_DIR */ +#define ARIZONA_GPN_DIR_WIDTH 1 /* GPN_DIR */ +#define ARIZONA_GPN_PU 0x4000 /* GPN_PU */ +#define ARIZONA_GPN_PU_MASK 0x4000 /* GPN_PU */ +#define ARIZONA_GPN_PU_SHIFT 14 /* GPN_PU */ +#define ARIZONA_GPN_PU_WIDTH 1 /* GPN_PU */ +#define ARIZONA_GPN_PD 0x2000 /* GPN_PD */ +#define ARIZONA_GPN_PD_MASK 0x2000 /* GPN_PD */ +#define ARIZONA_GPN_PD_SHIFT 13 /* GPN_PD */ +#define ARIZONA_GPN_PD_WIDTH 1 /* GPN_PD */ +#define ARIZONA_GPN_LVL 0x0800 /* GPN_LVL */ +#define ARIZONA_GPN_LVL_MASK 0x0800 /* GPN_LVL */ +#define ARIZONA_GPN_LVL_SHIFT 11 /* GPN_LVL */ +#define ARIZONA_GPN_LVL_WIDTH 1 /* GPN_LVL */ +#define ARIZONA_GPN_POL 0x0400 /* GPN_POL */ +#define ARIZONA_GPN_POL_MASK 0x0400 /* GPN_POL */ +#define ARIZONA_GPN_POL_SHIFT 10 /* GPN_POL */ +#define ARIZONA_GPN_POL_WIDTH 1 /* GPN_POL */ +#define ARIZONA_GPN_OP_CFG 0x0200 /* GPN_OP_CFG */ +#define ARIZONA_GPN_OP_CFG_MASK 0x0200 /* GPN_OP_CFG */ +#define ARIZONA_GPN_OP_CFG_SHIFT 9 /* GPN_OP_CFG */ +#define ARIZONA_GPN_OP_CFG_WIDTH 1 /* GPN_OP_CFG */ +#define ARIZONA_GPN_DB 0x0100 /* GPN_DB */ +#define ARIZONA_GPN_DB_MASK 0x0100 /* GPN_DB */ +#define ARIZONA_GPN_DB_SHIFT 8 /* GPN_DB */ +#define ARIZONA_GPN_DB_WIDTH 1 /* GPN_DB */ +#define ARIZONA_GPN_FN_MASK 0x007F /* GPN_FN - [6:0] */ +#define ARIZONA_GPN_FN_SHIFT 0 /* GPN_FN - [6:0] */ +#define ARIZONA_GPN_FN_WIDTH 7 /* GPN_FN - [6:0] */ + +#define ARIZONA_MAX_GPIO 5 + +#define ARIZONA_32KZ_MCLK1 1 +#define ARIZONA_32KZ_MCLK2 2 +#define ARIZONA_32KZ_NONE 3 + +#define ARIZONA_MAX_INPUT 3 + +#define ARIZONA_DMIC_MICVDD 0 +#define ARIZONA_DMIC_MICBIAS1 1 +#define ARIZONA_DMIC_MICBIAS2 2 +#define ARIZONA_DMIC_MICBIAS3 3 + +#define ARIZONA_INMODE_DIFF 0 +#define ARIZONA_INMODE_SE 1 +#define ARIZONA_INMODE_DMIC 2 + +#define ARIZONA_MAX_OUTPUT 5 + +#define ARIZONA_MAX_PDM_SPK 1 + +struct regulator_init_data; + +struct arizona_micd_config { + unsigned int src; + unsigned int bias; + bool gpio; +}; + +struct arizona_pdata { + int reset; /** GPIO controlling /RESET, if any */ + int ldoena; /** GPIO controlling LODENA, if any */ + + /** Regulator configuration for MICVDD */ + struct regulator_init_data *micvdd; + + /** Regulator configuration for LDO1 */ + struct regulator_init_data *ldo1; + + /** If a direct 32kHz clock is provided on an MCLK specify it here */ + int clk32k_src; + + bool irq_active_high; /** IRQ polarity */ + + /* Base GPIO */ + int gpio_base; + + /** Pin state for GPIO pins */ + int gpio_defaults[ARIZONA_MAX_GPIO]; + + /** GPIO for mic detection polarity */ + int micd_pol_gpio; + + /** Headset polarity configurations */ + struct arizona_micd_config *micd_configs; + int num_micd_configs; + + /** Reference voltage for DMIC inputs */ + int dmic_ref[ARIZONA_MAX_INPUT]; + + /** Mode of input structures */ + int inmode[ARIZONA_MAX_INPUT]; + + /** Mode for outputs */ + bool out_mono[ARIZONA_MAX_OUTPUT]; + + /** PDM speaker mute setting */ + unsigned int spk_mute[ARIZONA_MAX_PDM_SPK]; + + /** PDM speaker format */ + unsigned int spk_fmt[ARIZONA_MAX_PDM_SPK]; +}; + +#endif -- cgit v1.2.3 From ee85f543710dd56ce526cb44e39191f32972e5ad Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Sat, 23 Jun 2012 10:23:48 +0800 Subject: ACPI/PM: specify lowest allowed state for device sleep state Lower device sleep state can save more power, but has more exit latency too. Sometimes, to satisfy some power QoS and other requirement, we need to constrain the lowest device sleep state. In this patch, a parameter to specify lowest allowed state for acpi_pm_device_sleep_state is added. So that the caller can enforce the constraint via the parameter. This is needed by PCIe D3cold support, where the lowest power state allowed may be D3_HOT instead of default D3_COLD. CC: Len Brown CC: linux-acpi@vger.kernel.org Reviewed-by: Rafael J. Wysocki Signed-off-by: Huang Ying Signed-off-by: Bjorn Helgaas --- drivers/acpi/sleep.c | 24 +++++++++++++++++++----- drivers/pci/pci-acpi.c | 3 ++- drivers/pnp/pnpacpi/core.c | 4 ++-- include/acpi/acpi_bus.h | 6 +++--- 4 files changed, 26 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 88561029cca8..1cc02ca2af2a 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -716,8 +716,9 @@ int acpi_suspend(u32 acpi_state) * @dev: device to examine; its driver model wakeup flags control * whether it should be able to wake up the system * @d_min_p: used to store the upper limit of allowed states range - * Return value: preferred power state of the device on success, -ENODEV on - * failure (ie. if there's no 'struct acpi_device' for @dev) + * @d_max_in: specify the lowest allowed states + * Return value: preferred power state of the device on success, -ENODEV + * (ie. if there's no 'struct acpi_device' for @dev) or -EINVAL on failure * * Find the lowest power (highest number) ACPI device power state that * device @dev can be in while the system is in the sleep state represented @@ -732,13 +733,15 @@ int acpi_suspend(u32 acpi_state) * via @wake. */ -int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) +int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) { acpi_handle handle = DEVICE_ACPI_HANDLE(dev); struct acpi_device *adev; char acpi_method[] = "_SxD"; unsigned long long d_min, d_max; + if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3) + return -EINVAL; if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { printk(KERN_DEBUG "ACPI handle has no context!\n"); return -ENODEV; @@ -746,8 +749,10 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) acpi_method[2] = '0' + acpi_target_sleep_state; /* - * If the sleep state is S0, we will return D3, but if the device has - * _S0W, we will use the value from _S0W + * If the sleep state is S0, the lowest limit from ACPI is D3, + * but if the device has _S0W, we will use the value from _S0W + * as the lowest limit from ACPI. Finally, we will constrain + * the lowest limit with the specified one. */ d_min = ACPI_STATE_D0; d_max = ACPI_STATE_D3; @@ -791,8 +796,17 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) } } + if (d_max_in < d_min) + return -EINVAL; if (d_min_p) *d_min_p = d_min; + /* constrain d_max with specified lowest limit (max number) */ + if (d_max > d_max_in) { + for (d_max = d_max_in; d_max > d_min; d_max--) { + if (adev->power.states[d_max].flags.valid) + break; + } + } return d_max; } #endif /* CONFIG_PM */ diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 61e2fefeedab..a9efebc586b4 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -189,7 +189,8 @@ static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev) { int acpi_state; - acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL); + acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL, + ACPI_STATE_D3); if (acpi_state < 0) return PCI_POWER_ERROR; diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index d21e8f59c84e..507a8e2b9a4c 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -170,8 +170,8 @@ static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state) } if (acpi_bus_power_manageable(handle)) { - int power_state = acpi_pm_device_sleep_state(&dev->dev, NULL); - + int power_state = acpi_pm_device_sleep_state(&dev->dev, NULL, + ACPI_STATE_D3); if (power_state < 0) power_state = (state.event == PM_EVENT_ON) ? ACPI_STATE_D0 : ACPI_STATE_D3; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 9e6e1c6eb60a..16bd68504ff7 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -414,13 +414,13 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int state); int acpi_disable_wakeup_device_power(struct acpi_device *dev); #ifdef CONFIG_PM -int acpi_pm_device_sleep_state(struct device *, int *); +int acpi_pm_device_sleep_state(struct device *, int *, int); #else -static inline int acpi_pm_device_sleep_state(struct device *d, int *p) +static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) { if (p) *p = ACPI_STATE_D0; - return ACPI_STATE_D3; + return (m >= ACPI_STATE_D0 && m <= ACPI_STATE_D3) ? m : ACPI_STATE_D0; } #endif -- cgit v1.2.3 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') 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 From bdcbd8e0e3ffdad32b14b6373e67bfcf5fd3f002 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 22 Jun 2012 11:29:50 +0200 Subject: mac80211: clean up debugging There are a few things that make the logging and debugging in mac80211 less useful than it should be right now: * a lot of messages should be pr_info, not pr_debug * wholesale use of pr_debug makes it require *both* Kconfig and dynamic configuration * there are still a lot of ifdefs * the style is very inconsistent, sometimes the sdata->name is printed in front Clean up everything, introducing new macros and separating out the station MLME debugging into a new Kconfig symbol. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 24 ----- net/mac80211/Kconfig | 32 +++++-- net/mac80211/Makefile | 2 +- net/mac80211/agg-rx.c | 34 +++---- net/mac80211/agg-tx.c | 72 ++++++++------- net/mac80211/cfg.c | 9 +- net/mac80211/debug.h | 152 +++++++++++++++++++++++++++++++ net/mac80211/debugfs_netdev.c | 5 +- net/mac80211/ht.c | 10 +-- net/mac80211/ibss.c | 89 +++++++++--------- net/mac80211/ieee80211_i.h | 1 + net/mac80211/iface.c | 13 +-- net/mac80211/key.c | 4 +- net/mac80211/mesh.c | 4 - net/mac80211/mesh_hwmp.c | 42 ++++----- net/mac80211/mesh_pathtbl.c | 30 +++---- net/mac80211/mesh_plink.c | 61 +++++++------ net/mac80211/mesh_sync.c | 47 +++++----- net/mac80211/mlme.c | 203 +++++++++++++++++++----------------------- net/mac80211/rx.c | 40 +++------ net/mac80211/sta_info.c | 44 ++++----- net/mac80211/status.c | 11 +-- net/mac80211/tx.c | 43 ++++----- 23 files changed, 519 insertions(+), 453 deletions(-) create mode 100644 net/mac80211/debug.h (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f11c2f8b00c9..510d852d5222 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3842,28 +3842,4 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif, */ int ieee80211_ave_rssi(struct ieee80211_vif *vif); -/* Extra debugging macros */ - -#ifdef CONFIG_MAC80211_HT_DEBUG -#define ht_vdbg(fmt, ...) \ - pr_debug(fmt, ##__VA_ARGS__) -#else -#define ht_vdbg(fmt, ...) \ -do { \ - if (0) \ - pr_debug(fmt, ##__VA_ARGS__); \ -} while (0) -#endif - -#ifdef CONFIG_MAC80211_IBSS_DEBUG -#define ibss_vdbg(fmt, ...) \ - pr_debug(fmt, ##__VA_ARGS__) -#else -#define ibss_vdbg(fmt, ...) \ -do { \ - if (0) \ - pr_debug(fmt, ##__VA_ARGS__); \ -} while (0) -#endif - #endif /* MAC80211_H */ diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 323aa19a39d5..7475e266eb4e 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -140,6 +140,26 @@ config MAC80211_VERBOSE_DEBUG Do not select this option. +config MAC80211_MLME_DEBUG + bool "Verbose managed MLME output" + depends on MAC80211_DEBUG_MENU + ---help--- + Selecting this option causes mac80211 to print out + debugging messages for the managed-mode MLME. It + should not be selected on production systems as some + of the messages are remotely triggerable. + + Do not select this option. + +config MAC80211_STA_DEBUG + bool "Verbose station debugging" + depends on MAC80211_DEBUG_MENU + ---help--- + Selecting this option causes mac80211 to print out + debugging messages for station addition/removal. + + Do not select this option. + config MAC80211_HT_DEBUG bool "Verbose HT debugging" depends on MAC80211_DEBUG_MENU @@ -163,7 +183,7 @@ config MAC80211_IBSS_DEBUG Do not select this option. -config MAC80211_VERBOSE_PS_DEBUG +config MAC80211_PS_DEBUG bool "Verbose powersave mode debugging" depends on MAC80211_DEBUG_MENU ---help--- @@ -175,7 +195,7 @@ config MAC80211_VERBOSE_PS_DEBUG Do not select this option. -config MAC80211_VERBOSE_MPL_DEBUG +config MAC80211_MPL_DEBUG bool "Verbose mesh peer link debugging" depends on MAC80211_DEBUG_MENU depends on MAC80211_MESH @@ -188,7 +208,7 @@ config MAC80211_VERBOSE_MPL_DEBUG Do not select this option. -config MAC80211_VERBOSE_MPATH_DEBUG +config MAC80211_MPATH_DEBUG bool "Verbose mesh path debugging" depends on MAC80211_DEBUG_MENU depends on MAC80211_MESH @@ -201,7 +221,7 @@ config MAC80211_VERBOSE_MPATH_DEBUG Do not select this option. -config MAC80211_VERBOSE_MHWMP_DEBUG +config MAC80211_MHWMP_DEBUG bool "Verbose mesh HWMP routing debugging" depends on MAC80211_DEBUG_MENU depends on MAC80211_MESH @@ -214,7 +234,7 @@ config MAC80211_VERBOSE_MHWMP_DEBUG Do not select this option. -config MAC80211_VERBOSE_MESH_SYNC_DEBUG +config MAC80211_MESH_SYNC_DEBUG bool "Verbose mesh mesh synchronization debugging" depends on MAC80211_DEBUG_MENU depends on MAC80211_MESH @@ -225,7 +245,7 @@ config MAC80211_VERBOSE_MESH_SYNC_DEBUG Do not select this option. -config MAC80211_VERBOSE_TDLS_DEBUG +config MAC80211_TDLS_DEBUG bool "Verbose TDLS debugging" depends on MAC80211_DEBUG_MENU ---help--- diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 2b1470bac178..231ffa02e496 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -58,4 +58,4 @@ mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y) mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y) mac80211-$(CONFIG_MAC80211_RC_MINSTREL_HT) += $(rc80211_minstrel_ht-y) -ccflags-y += -D__CHECK_ENDIAN__ +ccflags-y += -D__CHECK_ENDIAN__ -DDEBUG diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 32ef11d69798..186d9919b043 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -74,15 +74,17 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, RCU_INIT_POINTER(sta->ampdu_mlme.tid_rx[tid], NULL); - ht_vdbg("Rx BA session stop requested for %pM tid %u %s reason: %d\n", - sta->sta.addr, tid, - initiator == WLAN_BACK_RECIPIENT ? "recipient" : "inititator", - (int)reason); + ht_dbg(sta->sdata, + "Rx BA session stop requested for %pM tid %u %s reason: %d\n", + sta->sta.addr, tid, + initiator == WLAN_BACK_RECIPIENT ? "recipient" : "inititator", + (int)reason); if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP, &sta->sta, tid, NULL, 0)) - pr_debug("HW problem - can not stop rx aggregation for tid %d\n", - tid); + sdata_info(sta->sdata, + "HW problem - can not stop rx aggregation for tid %d\n", + tid); /* check if this is a self generated aggregation halt */ if (initiator == WLAN_BACK_RECIPIENT && tx) @@ -157,7 +159,7 @@ static void sta_rx_agg_session_timer_expired(unsigned long data) } rcu_read_unlock(); - ht_vdbg("rx session timer expired on tid %d\n", (u16)*ptid); + ht_dbg(sta->sdata, "rx session timer expired on tid %d\n", (u16)*ptid); set_bit(*ptid, sta->ampdu_mlme.tid_rx_timer_expired); ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work); @@ -245,7 +247,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, status = WLAN_STATUS_REQUEST_DECLINED; if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { - ht_vdbg("Suspend in progress - Denying ADDBA request\n"); + ht_dbg(sta->sdata, "Suspend in progress - Denying ADDBA request\n"); goto end_no_lock; } @@ -257,10 +259,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA))) || (buf_size > IEEE80211_MAX_AMPDU_BUF)) { status = WLAN_STATUS_INVALID_QOS_PARAM; -#ifdef CONFIG_MAC80211_HT_DEBUG - net_dbg_ratelimited("AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n", - mgmt->sa, tid, ba_policy, buf_size); -#endif /* CONFIG_MAC80211_HT_DEBUG */ + ht_dbg_ratelimited(sta->sdata, + "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n", + mgmt->sa, tid, ba_policy, buf_size); goto end_no_lock; } /* determine default buffer size */ @@ -275,10 +276,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, mutex_lock(&sta->ampdu_mlme.mtx); if (sta->ampdu_mlme.tid_rx[tid]) { -#ifdef CONFIG_MAC80211_HT_DEBUG - net_dbg_ratelimited("unexpected AddBA Req from %pM on tid %u\n", - mgmt->sa, tid); -#endif /* CONFIG_MAC80211_HT_DEBUG */ + ht_dbg_ratelimited(sta->sdata, + "unexpected AddBA Req from %pM on tid %u\n", + mgmt->sa, tid); /* delete existing Rx BA session on the same tid */ ___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, @@ -317,7 +317,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START, &sta->sta, tid, &start_seq_num, 0); - ht_vdbg("Rx A-MPDU request on tid %d result %d\n", tid, ret); + ht_dbg(sta->sdata, "Rx A-MPDU request on tid %d result %d\n", tid, ret); if (ret) { kfree(tid_agg_rx->reorder_buf); kfree(tid_agg_rx->reorder_time); diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index da07f01cfe4d..5cc1bf7d8033 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -184,8 +184,8 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, spin_unlock_bh(&sta->lock); - ht_vdbg("Tx BA session stop requested for %pM tid %u\n", - sta->sta.addr, tid); + ht_dbg(sta->sdata, "Tx BA session stop requested for %pM tid %u\n", + sta->sta.addr, tid); del_timer_sync(&tid_tx->addba_resp_timer); del_timer_sync(&tid_tx->session_timer); @@ -251,12 +251,13 @@ static void sta_addba_resp_timer_expired(unsigned long data) if (!tid_tx || test_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) { rcu_read_unlock(); - ht_vdbg("timer expired on tid %d but we are not (or no longer) expecting addBA response there\n", - tid); + ht_dbg(sta->sdata, + "timer expired on tid %d but we are not (or no longer) expecting addBA response there\n", + tid); return; } - ht_vdbg("addBA response timer expired on tid %d\n", tid); + ht_dbg(sta->sdata, "addBA response timer expired on tid %d\n", tid); ieee80211_stop_tx_ba_session(&sta->sta, tid); rcu_read_unlock(); @@ -316,8 +317,9 @@ ieee80211_agg_splice_packets(struct ieee80211_sub_if_data *sdata, ieee80211_stop_queue_agg(sdata, tid); - if (WARN(!tid_tx, "TID %d gone but expected when splicing aggregates" - " from the pending queue\n", tid)) + if (WARN(!tid_tx, + "TID %d gone but expected when splicing aggregates from the pending queue\n", + tid)) return; if (!skb_queue_empty(&tid_tx->pending)) { @@ -365,7 +367,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START, &sta->sta, tid, &start_seq_num, 0); if (ret) { - ht_vdbg("BA request denied - HW unavailable for tid %d\n", tid); + ht_dbg(sdata, + "BA request denied - HW unavailable for tid %d\n", tid); spin_lock_bh(&sta->lock); ieee80211_agg_splice_packets(sdata, tid_tx, tid); ieee80211_assign_tid_tx(sta, tid, NULL); @@ -378,7 +381,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) /* activate the timer for the recipient's addBA response */ mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL); - ht_vdbg("activated addBA response timer on tid %d\n", tid); + ht_dbg(sdata, "activated addBA response timer on tid %d\n", tid); spin_lock_bh(&sta->lock); sta->ampdu_mlme.last_addba_req_time[tid] = jiffies; @@ -425,7 +428,7 @@ static void sta_tx_agg_session_timer_expired(unsigned long data) rcu_read_unlock(); - ht_vdbg("tx session timer expired on tid %d\n", (u16)*ptid); + ht_dbg(sta->sdata, "tx session timer expired on tid %d\n", (u16)*ptid); ieee80211_stop_tx_ba_session(&sta->sta, *ptid); } @@ -449,8 +452,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW)) return -EINVAL; - ht_vdbg("Open BA session requested for %pM tid %u\n", - pubsta->addr, tid); + ht_dbg(sdata, "Open BA session requested for %pM tid %u\n", + pubsta->addr, tid); if (sdata->vif.type != NL80211_IFTYPE_STATION && sdata->vif.type != NL80211_IFTYPE_MESH_POINT && @@ -460,7 +463,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, return -EINVAL; if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { - ht_vdbg("BA sessions blocked - Denying BA session request\n"); + ht_dbg(sdata, + "BA sessions blocked - Denying BA session request\n"); return -EINVAL; } @@ -478,8 +482,9 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, */ if (sta->sdata->vif.type == NL80211_IFTYPE_ADHOC && !sta->sta.ht_cap.ht_supported) { - ht_vdbg("BA request denied - IBSS STA %pM does not advertise HT support\n", - pubsta->addr); + ht_dbg(sdata, + "BA request denied - IBSS STA %pM does not advertise HT support\n", + pubsta->addr); return -EINVAL; } @@ -499,8 +504,9 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_BURST_RETRIES && time_before(jiffies, sta->ampdu_mlme.last_addba_req_time[tid] + HT_AGG_RETRIES_PERIOD)) { - ht_vdbg("BA request denied - waiting a grace period after %d failed requests on tid %u\n", - sta->ampdu_mlme.addba_req_num[tid], tid); + ht_dbg(sdata, + "BA request denied - waiting a grace period after %d failed requests on tid %u\n", + sta->ampdu_mlme.addba_req_num[tid], tid); ret = -EBUSY; goto err_unlock_sta; } @@ -508,8 +514,9 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, tid_tx = rcu_dereference_protected_tid_tx(sta, tid); /* check if the TID is not in aggregation flow already */ if (tid_tx || sta->ampdu_mlme.tid_start_tx[tid]) { - ht_vdbg("BA request denied - session is not idle on tid %u\n", - tid); + ht_dbg(sdata, + "BA request denied - session is not idle on tid %u\n", + tid); ret = -EAGAIN; goto err_unlock_sta; } @@ -564,7 +571,7 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, tid_tx = rcu_dereference_protected_tid_tx(sta, tid); - ht_vdbg("Aggregation is on for tid %d\n", tid); + ht_dbg(sta->sdata, "Aggregation is on for tid %d\n", tid); drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_TX_OPERATIONAL, @@ -598,7 +605,8 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) trace_api_start_tx_ba_cb(sdata, ra, tid); if (tid >= STA_TID_NUM) { - ht_vdbg("Bad TID value: tid = %d (>= %d)\n", tid, STA_TID_NUM); + ht_dbg(sdata, "Bad TID value: tid = %d (>= %d)\n", + tid, STA_TID_NUM); return; } @@ -606,7 +614,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) sta = sta_info_get_bss(sdata, ra); if (!sta) { mutex_unlock(&local->sta_mtx); - ht_vdbg("Could not find station: %pM\n", ra); + ht_dbg(sdata, "Could not find station: %pM\n", ra); return; } @@ -614,7 +622,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (WARN_ON(!tid_tx)) { - ht_vdbg("addBA was not requested!\n"); + ht_dbg(sdata, "addBA was not requested!\n"); goto unlock; } @@ -714,17 +722,18 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) trace_api_stop_tx_ba_cb(sdata, ra, tid); if (tid >= STA_TID_NUM) { - ht_vdbg("Bad TID value: tid = %d (>= %d)\n", tid, STA_TID_NUM); + ht_dbg(sdata, "Bad TID value: tid = %d (>= %d)\n", + tid, STA_TID_NUM); return; } - ht_vdbg("Stopping Tx BA session for %pM tid %d\n", ra, tid); + ht_dbg(sdata, "Stopping Tx BA session for %pM tid %d\n", ra, tid); mutex_lock(&local->sta_mtx); sta = sta_info_get_bss(sdata, ra); if (!sta) { - ht_vdbg("Could not find station: %pM\n", ra); + ht_dbg(sdata, "Could not find station: %pM\n", ra); goto unlock; } @@ -733,7 +742,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (!tid_tx || !test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { - ht_vdbg("unexpected callback to A-MPDU stop\n"); + ht_dbg(sdata, "unexpected callback to A-MPDU stop\n"); goto unlock_sta; } @@ -809,13 +818,13 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, goto out; if (mgmt->u.action.u.addba_resp.dialog_token != tid_tx->dialog_token) { - ht_vdbg("wrong addBA response token, tid %d\n", tid); + ht_dbg(sta->sdata, "wrong addBA response token, tid %d\n", tid); goto out; } del_timer_sync(&tid_tx->addba_resp_timer); - ht_vdbg("switched off addBA timer for tid %d\n", tid); + ht_dbg(sta->sdata, "switched off addBA timer for tid %d\n", tid); /* * addba_resp_timer may have fired before we got here, and @@ -824,8 +833,9 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, */ if (test_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state) || test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { - ht_vdbg("got addBA resp for tid %d but we already gave up\n", - tid); + ht_dbg(sta->sdata, + "got addBA resp for tid %d but we already gave up\n", + tid); goto out; } diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d0c8f78115cb..7722a7336a58 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2771,9 +2771,8 @@ static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, !sdata->u.mgd.associated) return -EINVAL; -#ifdef CONFIG_MAC80211_VERBOSE_TDLS_DEBUG - pr_debug("TDLS mgmt action %d peer %pM\n", action_code, peer); -#endif + tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n", + action_code, peer); skb = dev_alloc_skb(local->hw.extra_tx_headroom + max(sizeof(struct ieee80211_mgmt), @@ -2882,9 +2881,7 @@ static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EINVAL; -#ifdef CONFIG_MAC80211_VERBOSE_TDLS_DEBUG - pr_debug("TDLS oper %d peer %pM\n", oper, peer); -#endif + tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); switch (oper) { case NL80211_TDLS_ENABLE_LINK: diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h new file mode 100644 index 000000000000..6e6bbb9a9d41 --- /dev/null +++ b/net/mac80211/debug.h @@ -0,0 +1,152 @@ +#ifndef __MAC80211_DEBUG_H +#define __MAC80211_DEBUG_H + +#ifdef CONFIG_MAC80211_IBSS_DEBUG +#define MAC80211_IBSS_DEBUG 1 +#else +#define MAC80211_IBSS_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_PS_DEBUG +#define MAC80211_PS_DEBUG 1 +#else +#define MAC80211_PS_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_HT_DEBUG +#define MAC80211_HT_DEBUG 1 +#else +#define MAC80211_HT_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_MPL_DEBUG +#define MAC80211_MPL_DEBUG 1 +#else +#define MAC80211_MPL_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_MPATH_DEBUG +#define MAC80211_MPATH_DEBUG 1 +#else +#define MAC80211_MPATH_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_MHWMP_DEBUG +#define MAC80211_MHWMP_DEBUG 1 +#else +#define MAC80211_MHWMP_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_MESH_SYNC_DEBUG +#define MAC80211_MESH_SYNC_DEBUG 1 +#else +#define MAC80211_MESH_SYNC_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_TDLS_DEBUG +#define MAC80211_TDLS_DEBUG 1 +#else +#define MAC80211_TDLS_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_STA_DEBUG +#define MAC80211_STA_DEBUG 1 +#else +#define MAC80211_STA_DEBUG 0 +#endif + +#ifdef CONFIG_MAC80211_MLME_DEBUG +#define MAC80211_MLME_DEBUG 1 +#else +#define MAC80211_MLME_DEBUG 0 +#endif + +#define _sdata_info(sdata, fmt, ...) \ +do { \ + pr_info("%s: " fmt, \ + (sdata)->name, ##__VA_ARGS__); \ +} while (0) + +#define _sdata_dbg(print, sdata, fmt, ...) \ +do { \ + if (print) \ + pr_debug("%s: " fmt, \ + (sdata)->name, ##__VA_ARGS__); \ +} while (0) + +#define _sdata_err(sdata, fmt, ...) \ +do { \ + pr_err("%s: " fmt, \ + (sdata)->name, ##__VA_ARGS__); \ +} while (0) + +#define _wiphy_dbg(print, wiphy, fmt, ...) \ +do { \ + if (print) \ + wiphy_dbg((wiphy), fmt, ##__VA_ARGS__); \ +} while (0) + +#define sdata_info(sdata, fmt, ...) \ + _sdata_info(sdata, fmt, ##__VA_ARGS__) +#define sdata_err(sdata, fmt, ...) \ + _sdata_err(sdata, fmt, ##__VA_ARGS__) +#define sdata_dbg(sdata, fmt, ...) \ + _sdata_dbg(1, sdata, fmt, ##__VA_ARGS__) + +#define ht_dbg(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_HT_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) + +#define ht_dbg_ratelimited(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_HT_DEBUG && net_ratelimit(), \ + sdata, fmt, ##__VA_ARGS__) + +#define ibss_dbg(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_IBSS_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) + +#define ps_dbg(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_PS_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) + +#define ps_dbg_hw(hw, fmt, ...) \ + _wiphy_dbg(MAC80211_PS_DEBUG, \ + (hw)->wiphy, fmt, ##__VA_ARGS__) + +#define ps_dbg_ratelimited(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_PS_DEBUG && net_ratelimit(), \ + sdata, fmt, ##__VA_ARGS__) + +#define mpl_dbg(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_MPL_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) + +#define mpath_dbg(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_MPATH_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) + +#define mhwmp_dbg(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_MHWMP_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) + +#define msync_dbg(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) + +#define tdls_dbg(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_TDLS_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) + +#define sta_dbg(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_STA_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) + +#define mlme_dbg(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_MLME_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) + +#define mlme_dbg_ratelimited(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_MLME_DEBUG && net_ratelimit(), \ + sdata, fmt, ##__VA_ARGS__) + +#endif /* __MAC80211_DEBUG_H */ diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 512c894893d6..6d5aec9418ee 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -695,6 +695,7 @@ void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata) sprintf(buf, "netdev:%s", sdata->name); if (!debugfs_rename(dir->d_parent, dir, dir->d_parent, buf)) - pr_err("mac80211: debugfs: failed to rename debugfs " - "dir to %s\n", buf); + sdata_err(sdata, + "debugfs: failed to rename debugfs dir to %s\n", + buf); } diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 6f8615c54b22..4b4538d63925 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -305,12 +305,10 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12; initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11; -#ifdef CONFIG_MAC80211_HT_DEBUG - net_dbg_ratelimited("delba from %pM (%s) tid %d reason code %d\n", - mgmt->sa, initiator ? "initiator" : "recipient", - tid, - le16_to_cpu(mgmt->u.action.u.delba.reason_code)); -#endif /* CONFIG_MAC80211_HT_DEBUG */ + ht_dbg_ratelimited(sdata, "delba from %pM (%s) tid %d reason code %d\n", + mgmt->sa, initiator ? "initiator" : "recipient", + tid, + le16_to_cpu(mgmt->u.action.u.delba.reason_code)); if (initiator == WLAN_BACK_INITIATOR) __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 8931110b8433..5746d62faba1 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -261,11 +261,7 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta, memcpy(addr, sta->sta.addr, ETH_ALEN); -#ifdef CONFIG_MAC80211_IBSS_DEBUG - wiphy_debug(sdata->local->hw.wiphy, - "Adding new IBSS station %pM (dev=%s)\n", - addr, sdata->name); -#endif + ibss_dbg(sdata, "Adding new IBSS station %pM\n", addr); sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); @@ -280,8 +276,9 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta, if (sta_info_insert_rcu(sta)) return sta_info_get(sdata, addr); if (auth && !sdata->u.ibss.auth_frame_registrations) { - ibss_vdbg("TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n", - sdata->vif.addr, sdata->u.ibss.bssid, addr); + ibss_dbg(sdata, + "TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n", + sdata->vif.addr, sdata->u.ibss.bssid, addr); ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, NULL, 0, addr, sdata->u.ibss.bssid, NULL, 0, 0); } @@ -304,7 +301,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, * allow new one to be added. */ if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) { - net_dbg_ratelimited("%s: No room for a new IBSS STA entry %pM\n", + net_info_ratelimited("%s: No room for a new IBSS STA entry %pM\n", sdata->name, addr); rcu_read_lock(); return NULL; @@ -351,9 +348,9 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) return; - ibss_vdbg("%s: RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n", - sdata->name, mgmt->sa, mgmt->da, mgmt->bssid, - auth_transaction); + ibss_dbg(sdata, + "RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n", + mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction); sta_info_destroy_addr(sdata, mgmt->sa); ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false); rcu_read_unlock(); @@ -416,10 +413,10 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ieee80211_mandatory_rates(local, band); if (sta->sta.supp_rates[band] != prev_rates) { - ibss_vdbg("%s: updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n", - sdata->name, sta->sta.addr, - prev_rates, - sta->sta.supp_rates[band]); + ibss_dbg(sdata, + "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n", + sta->sta.addr, prev_rates, + sta->sta.supp_rates[band]); rates_updated = true; } } else { @@ -534,16 +531,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, rx_timestamp = drv_get_tsf(local, sdata); } - ibss_vdbg("RX beacon SA=%pM BSSID=%pM TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n", - mgmt->sa, mgmt->bssid, - (unsigned long long)rx_timestamp, - (unsigned long long)beacon_timestamp, - (unsigned long long)(rx_timestamp - beacon_timestamp), - jiffies); + ibss_dbg(sdata, + "RX beacon SA=%pM BSSID=%pM TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n", + mgmt->sa, mgmt->bssid, + (unsigned long long)rx_timestamp, + (unsigned long long)beacon_timestamp, + (unsigned long long)(rx_timestamp - beacon_timestamp), + jiffies); if (beacon_timestamp > rx_timestamp) { - ibss_vdbg("%s: beacon TSF higher than local TSF - IBSS merge with BSSID %pM\n", - sdata->name, mgmt->bssid); + ibss_dbg(sdata, + "beacon TSF higher than local TSF - IBSS merge with BSSID %pM\n", + mgmt->bssid); ieee80211_sta_join_ibss(sdata, bss); supp_rates = ieee80211_sta_get_rates(local, elems, band, NULL); ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, @@ -569,7 +568,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, * allow new one to be added. */ if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) { - net_dbg_ratelimited("%s: No room for a new IBSS STA entry %pM\n", + net_info_ratelimited("%s: No room for a new IBSS STA entry %pM\n", sdata->name, addr); return; } @@ -645,8 +644,8 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) if (ifibss->fixed_channel) return; - pr_debug("%s: No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n", - sdata->name); + sdata_info(sdata, + "No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n"); ieee80211_request_internal_scan(sdata, ifibss->ssid, ifibss->ssid_len, NULL); @@ -674,8 +673,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) bssid[0] |= 0x02; } - pr_debug("%s: Creating new IBSS network, BSSID %pM\n", - sdata->name, bssid); + sdata_info(sdata, "Creating new IBSS network, BSSID %pM\n", bssid); capability = WLAN_CAPABILITY_IBSS; @@ -706,8 +704,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) lockdep_assert_held(&ifibss->mtx); active_ibss = ieee80211_sta_active_ibss(sdata); - ibss_vdbg("%s: sta_find_ibss (active_ibss=%d)\n", - sdata->name, active_ibss); + ibss_dbg(sdata, "sta_find_ibss (active_ibss=%d)\n", active_ibss); if (active_ibss) return; @@ -730,23 +727,24 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) struct ieee80211_bss *bss; bss = (void *)cbss->priv; - ibss_vdbg(" sta_find_ibss: selected %pM current %pM\n", - cbss->bssid, ifibss->bssid); - pr_debug("%s: Selected IBSS BSSID %pM based on configured SSID\n", - sdata->name, cbss->bssid); + ibss_dbg(sdata, + "sta_find_ibss: selected %pM current %pM\n", + cbss->bssid, ifibss->bssid); + sdata_info(sdata, + "Selected IBSS BSSID %pM based on configured SSID\n", + cbss->bssid); ieee80211_sta_join_ibss(sdata, bss); ieee80211_rx_bss_put(local, bss); return; } - ibss_vdbg(" did not try to join ibss\n"); + ibss_dbg(sdata, "sta_find_ibss: did not try to join ibss\n"); /* Selected IBSS not found in current scan results - try to scan */ if (time_after(jiffies, ifibss->last_scan_completed + IEEE80211_SCAN_INTERVAL)) { - pr_debug("%s: Trigger new scan to find an IBSS to join\n", - sdata->name); + sdata_info(sdata, "Trigger new scan to find an IBSS to join\n"); ieee80211_request_internal_scan(sdata, ifibss->ssid, ifibss->ssid_len, @@ -760,9 +758,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) ieee80211_sta_create_ibss(sdata); return; } - pr_debug("%s: IBSS not allowed on %d MHz\n", - sdata->name, - local->hw.conf.channel->center_freq); + sdata_info(sdata, "IBSS not allowed on %d MHz\n", + local->hw.conf.channel->center_freq); /* No IBSS found - decrease scan interval and continue * scanning. */ @@ -797,9 +794,9 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, tx_last_beacon = drv_tx_last_beacon(local); - ibss_vdbg("%s: RX ProbeReq SA=%pM DA=%pM BSSID=%pM (tx_last_beacon=%d)\n", - sdata->name, mgmt->sa, mgmt->da, - mgmt->bssid, tx_last_beacon); + ibss_dbg(sdata, + "RX ProbeReq SA=%pM DA=%pM BSSID=%pM (tx_last_beacon=%d)\n", + mgmt->sa, mgmt->da, mgmt->bssid, tx_last_beacon); if (!tx_last_beacon && is_multicast_ether_addr(mgmt->da)) return; @@ -812,8 +809,8 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, pos = mgmt->u.probe_req.variable; if (pos[0] != WLAN_EID_SSID || pos + 2 + pos[1] > end) { - ibss_vdbg("%s: Invalid SSID IE in ProbeReq from %pM\n", - sdata->name, mgmt->sa); + ibss_dbg(sdata, "Invalid SSID IE in ProbeReq from %pM\n", + mgmt->sa); return; } if (pos[1] != 0 && @@ -830,7 +827,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, resp = (struct ieee80211_mgmt *) skb->data; memcpy(resp->da, mgmt->sa, ETH_ALEN); - ibss_vdbg("%s: Sending ProbeResp to %pM\n", sdata->name, resp->da); + ibss_dbg(sdata, "Sending ProbeResp to %pM\n", resp->da); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; ieee80211_tx_skb(sdata, skb); } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 36ce2bb066bf..f834a005e1c5 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -30,6 +30,7 @@ #include #include "key.h" #include "sta_info.h" +#include "debug.h" struct ieee80211_local; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 728d3eac1f59..576880317d0e 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -57,9 +57,6 @@ static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) return -EINVAL; } -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - pr_debug("%s: setting MTU %d\n", dev->name, new_mtu); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ dev->mtu = new_mtu; return 0; } @@ -1223,7 +1220,7 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, if (__ffs64(mask) + hweight64(mask) != fls64(mask)) { /* not a contiguous mask ... not handled now! */ - pr_debug("not contiguous\n"); + pr_info("not contiguous\n"); break; } @@ -1414,10 +1411,6 @@ static u32 ieee80211_idle_off(struct ieee80211_local *local, if (!(local->hw.conf.flags & IEEE80211_CONF_IDLE)) return 0; -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, "device no longer idle - %s\n", reason); -#endif - local->hw.conf.flags &= ~IEEE80211_CONF_IDLE; return IEEE80211_CONF_CHANGE_IDLE; } @@ -1427,10 +1420,6 @@ static u32 ieee80211_idle_on(struct ieee80211_local *local) if (local->hw.conf.flags & IEEE80211_CONF_IDLE) return 0; -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, "device now idle\n"); -#endif - drv_flush(local, false); local->hw.conf.flags |= IEEE80211_CONF_IDLE; diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 5bb600d93d77..b3b7e526e245 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -139,7 +139,7 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) } if (ret != -ENOSPC && ret != -EOPNOTSUPP) - wiphy_err(key->local->hw.wiphy, + sdata_err(sdata, "failed to set key (%d, %pM) to hardware (%d)\n", key->conf.keyidx, sta ? sta->sta.addr : bcast_addr, ret); @@ -186,7 +186,7 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) sta ? &sta->sta : NULL, &key->conf); if (ret) - wiphy_err(key->local->hw.wiphy, + sdata_err(sdata, "failed to remove key (%d, %pM) from hardware (%d)\n", key->conf.keyidx, sta ? sta->sta.addr : bcast_addr, ret); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index ae40a83675e9..764593d65fc3 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -523,10 +523,6 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata, { bool free_plinks; -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - pr_debug("%s: running mesh housekeeping\n", sdata->name); -#endif - ieee80211_sta_expire(sdata, IEEE80211_MESH_PEER_INACTIVITY_LIMIT); mesh_path_expire(sdata); diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index aed1821bd6f1..fb7b6a11d0ba 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -13,13 +13,6 @@ #include "wme.h" #include "mesh.h" -#ifdef CONFIG_MAC80211_VERBOSE_MHWMP_DEBUG -#define mhwmp_dbg(fmt, args...) \ - pr_debug("Mesh HWMP (%s): " fmt "\n", sdata->name, ##args) -#else -#define mhwmp_dbg(fmt, args...) do { (void)(0); } while (0) -#endif - #define TEST_FRAME_LEN 8192 #define MAX_METRIC 0xffffffff #define ARITH_SHIFT 8 @@ -144,19 +137,19 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, switch (action) { case MPATH_PREQ: - mhwmp_dbg("sending PREQ to %pM", target); + mhwmp_dbg(sdata, "sending PREQ to %pM\n", target); ie_len = 37; pos = skb_put(skb, 2 + ie_len); *pos++ = WLAN_EID_PREQ; break; case MPATH_PREP: - mhwmp_dbg("sending PREP to %pM", target); + mhwmp_dbg(sdata, "sending PREP to %pM\n", target); ie_len = 31; pos = skb_put(skb, 2 + ie_len); *pos++ = WLAN_EID_PREP; break; case MPATH_RANN: - mhwmp_dbg("sending RANN from %pM", orig_addr); + mhwmp_dbg(sdata, "sending RANN from %pM\n", orig_addr); ie_len = sizeof(struct ieee80211_rann_ie); pos = skb_put(skb, 2 + ie_len); *pos++ = WLAN_EID_RANN; @@ -535,10 +528,10 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, flags = PREQ_IE_FLAGS(preq_elem); root_is_gate = !!(flags & RANN_FLAG_IS_GATE); - mhwmp_dbg("received PREQ from %pM", orig_addr); + mhwmp_dbg(sdata, "received PREQ from %pM\n", orig_addr); if (ether_addr_equal(target_addr, sdata->vif.addr)) { - mhwmp_dbg("PREQ is for us"); + mhwmp_dbg(sdata, "PREQ is for us\n"); forward = false; reply = true; metric = 0; @@ -590,7 +583,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, lifetime = PREQ_IE_LIFETIME(preq_elem); ttl = ifmsh->mshcfg.element_ttl; if (ttl != 0) { - mhwmp_dbg("replying to the PREQ"); + mhwmp_dbg(sdata, "replying to the PREQ\n"); mesh_path_sel_frame_tx(MPATH_PREP, 0, orig_addr, cpu_to_le32(orig_sn), 0, target_addr, cpu_to_le32(target_sn), mgmt->sa, 0, ttl, @@ -611,7 +604,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, ifmsh->mshstats.dropped_frames_ttl++; return; } - mhwmp_dbg("forwarding the PREQ from %pM", orig_addr); + mhwmp_dbg(sdata, "forwarding the PREQ from %pM\n", orig_addr); --ttl; preq_id = PREQ_IE_PREQ_ID(preq_elem); hopcount = PREQ_IE_HOPCOUNT(preq_elem) + 1; @@ -658,7 +651,8 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, u8 next_hop[ETH_ALEN]; u32 target_sn, orig_sn, lifetime; - mhwmp_dbg("received PREP from %pM", PREP_IE_ORIG_ADDR(prep_elem)); + mhwmp_dbg(sdata, "received PREP from %pM\n", + PREP_IE_ORIG_ADDR(prep_elem)); orig_addr = PREP_IE_ORIG_ADDR(prep_elem); if (ether_addr_equal(orig_addr, sdata->vif.addr)) @@ -784,8 +778,9 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, if (ether_addr_equal(orig_addr, sdata->vif.addr)) return; - mhwmp_dbg("received RANN from %pM via neighbour %pM (is_gate=%d)", - orig_addr, mgmt->sa, root_is_gate); + mhwmp_dbg(sdata, + "received RANN from %pM via neighbour %pM (is_gate=%d)\n", + orig_addr, mgmt->sa, root_is_gate); rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); @@ -818,8 +813,9 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, root_path_confirmation_jiffies(sdata)) || time_before(jiffies, mpath->last_preq_to_root))) && !(mpath->flags & MESH_PATH_FIXED) && (ttl != 0)) { - mhwmp_dbg("%s time to refresh root mpath %pM", sdata->name, - orig_addr); + mhwmp_dbg(sdata, + "time to refresh root mpath %pM\n", + orig_addr); mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH); mpath->last_preq_to_root = jiffies; } @@ -926,7 +922,7 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags) preq_node = kmalloc(sizeof(struct mesh_preq_queue), GFP_ATOMIC); if (!preq_node) { - mhwmp_dbg("could not allocate PREQ node"); + mhwmp_dbg(sdata, "could not allocate PREQ node\n"); return; } @@ -935,7 +931,7 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags) spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); kfree(preq_node); if (printk_ratelimit()) - mhwmp_dbg("PREQ node queue full"); + mhwmp_dbg(sdata, "PREQ node queue full\n"); return; } @@ -1183,7 +1179,7 @@ void mesh_path_timer(unsigned long data) if (!mpath->is_gate && mesh_gate_num(sdata) > 0) { ret = mesh_path_send_to_gates(mpath); if (ret) - mhwmp_dbg("no gate was reachable"); + mhwmp_dbg(sdata, "no gate was reachable\n"); } else mesh_path_flush_pending(mpath); } @@ -1221,7 +1217,7 @@ mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata) 0, cpu_to_le32(ifmsh->preq_id++), sdata); break; default: - mhwmp_dbg("Proactive mechanism not supported"); + mhwmp_dbg(sdata, "Proactive mechanism not supported\n"); return; } } diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 572f706fd65b..c9ae931dd693 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -18,12 +18,6 @@ #include "ieee80211_i.h" #include "mesh.h" -#ifdef CONFIG_MAC80211_VERBOSE_MPATH_DEBUG -#define mpath_dbg(fmt, args...) pr_debug(fmt, ##args) -#else -#define mpath_dbg(fmt, args...) do { (void)(0); } while (0) -#endif - /* There will be initially 2^INIT_PATHS_SIZE_ORDER buckets */ #define INIT_PATHS_SIZE_ORDER 2 @@ -322,9 +316,8 @@ static void mesh_path_move_to_queue(struct mesh_path *gate_mpath, spin_lock_irqsave(&gate_mpath->frame_queue.lock, flags); skb_queue_splice(&gateq, &gate_mpath->frame_queue); - mpath_dbg("Mpath queue for gate %pM has %d frames\n", - gate_mpath->dst, - skb_queue_len(&gate_mpath->frame_queue)); + mpath_dbg(gate_mpath->sdata, "Mpath queue for gate %pM has %d frames\n", + gate_mpath->dst, skb_queue_len(&gate_mpath->frame_queue)); spin_unlock_irqrestore(&gate_mpath->frame_queue.lock, flags); if (!copy) @@ -446,9 +439,9 @@ int mesh_path_add_gate(struct mesh_path *mpath) hlist_add_head_rcu(&new_gate->list, tbl->known_gates); spin_unlock_bh(&tbl->gates_lock); rcu_read_unlock(); - mpath_dbg("Mesh path (%s): Recorded new gate: %pM. %d known gates\n", - mpath->sdata->name, mpath->dst, - mpath->sdata->u.mesh.num_gates); + mpath_dbg(mpath->sdata, + "Mesh path: Recorded new gate: %pM. %d known gates\n", + mpath->dst, mpath->sdata->u.mesh.num_gates); return 0; err_rcu: rcu_read_unlock(); @@ -477,8 +470,8 @@ static int mesh_gate_del(struct mesh_table *tbl, struct mesh_path *mpath) spin_unlock_bh(&tbl->gates_lock); mpath->sdata->u.mesh.num_gates--; mpath->is_gate = false; - mpath_dbg("Mesh path (%s): Deleted gate: %pM. " - "%d known gates\n", mpath->sdata->name, + mpath_dbg(mpath->sdata, + "Mesh path: Deleted gate: %pM. %d known gates\n", mpath->dst, mpath->sdata->u.mesh.num_gates); break; } @@ -946,19 +939,20 @@ int mesh_path_send_to_gates(struct mesh_path *mpath) continue; if (gate->mpath->flags & MESH_PATH_ACTIVE) { - mpath_dbg("Forwarding to %pM\n", gate->mpath->dst); + mpath_dbg(sdata, "Forwarding to %pM\n", gate->mpath->dst); mesh_path_move_to_queue(gate->mpath, from_mpath, copy); from_mpath = gate->mpath; copy = true; } else { - mpath_dbg("Not forwarding %p\n", gate->mpath); - mpath_dbg("flags %x\n", gate->mpath->flags); + mpath_dbg(sdata, + "Not forwarding %p (flags %#x)\n", + gate->mpath, gate->mpath->flags); } } hlist_for_each_entry_rcu(gate, n, known_gates, list) if (gate->mpath->sdata == sdata) { - mpath_dbg("Sending to %pM\n", gate->mpath->dst); + mpath_dbg(sdata, "Sending to %pM\n", gate->mpath->dst); mesh_path_tx_pending(gate->mpath); } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index be4fad128c34..a1dbd1540276 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -13,12 +13,6 @@ #include "rate.h" #include "mesh.h" -#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG -#define mpl_dbg(fmt, args...) pr_debug(fmt, ##args) -#else -#define mpl_dbg(fmt, args...) do { (void)(0); } while (0) -#endif - #define PLINK_GET_LLID(p) (p + 2) #define PLINK_GET_PLID(p) (p + 4) @@ -134,12 +128,14 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) switch (sta->ch_type) { case NL80211_CHAN_NO_HT: - mpl_dbg("mesh_plink %pM: nonHT sta (%pM) is present", + mpl_dbg(sdata, + "mesh_plink %pM: nonHT sta (%pM) is present\n", sdata->vif.addr, sta->sta.addr); non_ht_sta = true; goto out; case NL80211_CHAN_HT20: - mpl_dbg("mesh_plink %pM: HT20 sta (%pM) is present", + mpl_dbg(sdata, + "mesh_plink %pM: HT20 sta (%pM) is present\n", sdata->vif.addr, sta->sta.addr); ht20_sta = true; default: @@ -160,7 +156,8 @@ out: sdata->vif.bss_conf.ht_operation_mode = ht_opmode; sdata->u.mesh.mshcfg.ht_opmode = ht_opmode; changed = BSS_CHANGED_HT; - mpl_dbg("mesh_plink %pM: protection mode changed to %d", + mpl_dbg(sdata, + "mesh_plink %pM: protection mode changed to %d\n", sdata->vif.addr, ht_opmode); } @@ -437,7 +434,8 @@ static void mesh_plink_timer(unsigned long data) spin_unlock_bh(&sta->lock); return; } - mpl_dbg("Mesh plink timer for %pM fired on state %d\n", + mpl_dbg(sta->sdata, + "Mesh plink timer for %pM fired on state %d\n", sta->sta.addr, sta->plink_state); reason = 0; llid = sta->llid; @@ -450,7 +448,8 @@ static void mesh_plink_timer(unsigned long data) /* retry timer */ if (sta->plink_retries < dot11MeshMaxRetries(sdata)) { u32 rand; - mpl_dbg("Mesh plink for %pM (retry, timeout): %d %d\n", + mpl_dbg(sta->sdata, + "Mesh plink for %pM (retry, timeout): %d %d\n", sta->sta.addr, sta->plink_retries, sta->plink_timeout); get_random_bytes(&rand, sizeof(u32)); @@ -530,7 +529,8 @@ int mesh_plink_open(struct sta_info *sta) sta->plink_state = NL80211_PLINK_OPN_SNT; mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); spin_unlock_bh(&sta->lock); - mpl_dbg("Mesh plink: starting establishment with %pM\n", + mpl_dbg(sdata, + "Mesh plink: starting establishment with %pM\n", sta->sta.addr); return mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, @@ -565,7 +565,6 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m u8 *baseaddr; u32 changed = 0; __le16 plid, llid, reason; -#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG static const char *mplstates[] = { [NL80211_PLINK_LISTEN] = "LISTEN", [NL80211_PLINK_OPN_SNT] = "OPN-SNT", @@ -575,14 +574,14 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m [NL80211_PLINK_HOLDING] = "HOLDING", [NL80211_PLINK_BLOCKED] = "BLOCKED" }; -#endif /* need action_code, aux */ if (len < IEEE80211_MIN_ACTION_SIZE + 3) return; if (is_multicast_ether_addr(mgmt->da)) { - mpl_dbg("Mesh plink: ignore frame from multicast address"); + mpl_dbg(sdata, + "Mesh plink: ignore frame from multicast address\n"); return; } @@ -595,12 +594,14 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } ieee802_11_parse_elems(baseaddr, len - baselen, &elems); if (!elems.peering) { - mpl_dbg("Mesh plink: missing necessary peer link ie\n"); + mpl_dbg(sdata, + "Mesh plink: missing necessary peer link ie\n"); return; } if (elems.rsn_len && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { - mpl_dbg("Mesh plink: can't establish link with secure peer\n"); + mpl_dbg(sdata, + "Mesh plink: can't establish link with secure peer\n"); return; } @@ -610,14 +611,15 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) || (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6 && ie_len != 8)) { - mpl_dbg("Mesh plink: incorrect plink ie length %d %d\n", - ftype, ie_len); + mpl_dbg(sdata, + "Mesh plink: incorrect plink ie length %d %d\n", + ftype, ie_len); return; } if (ftype != WLAN_SP_MESH_PEERING_CLOSE && (!elems.mesh_id || !elems.mesh_config)) { - mpl_dbg("Mesh plink: missing necessary ie\n"); + mpl_dbg(sdata, "Mesh plink: missing necessary ie\n"); return; } /* Note the lines below are correct, the llid in the frame is the plid @@ -632,21 +634,21 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m sta = sta_info_get(sdata, mgmt->sa); if (!sta && ftype != WLAN_SP_MESH_PEERING_OPEN) { - mpl_dbg("Mesh plink: cls or cnf from unknown peer\n"); + mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n"); rcu_read_unlock(); return; } if (ftype == WLAN_SP_MESH_PEERING_OPEN && !rssi_threshold_check(sta, sdata)) { - mpl_dbg("Mesh plink: %pM does not meet rssi threshold\n", + mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n", mgmt->sa); rcu_read_unlock(); return; } if (sta && !test_sta_flag(sta, WLAN_STA_AUTH)) { - mpl_dbg("Mesh plink: Action frame from non-authed peer\n"); + mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n"); rcu_read_unlock(); return; } @@ -683,7 +685,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } else if (!sta) { /* ftype == WLAN_SP_MESH_PEERING_OPEN */ if (!mesh_plink_free_count(sdata)) { - mpl_dbg("Mesh plink error: no more free plinks\n"); + mpl_dbg(sdata, "Mesh plink error: no more free plinks\n"); rcu_read_unlock(); return; } @@ -724,7 +726,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m event = CLS_ACPT; break; default: - mpl_dbg("Mesh plink: unknown frame subtype\n"); + mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n"); rcu_read_unlock(); return; } @@ -734,13 +736,14 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m /* allocate sta entry if necessary and update info */ sta = mesh_peer_init(sdata, mgmt->sa, &elems); if (!sta) { - mpl_dbg("Mesh plink: failed to init peer!\n"); + mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); rcu_read_unlock(); return; } } - mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %s %d %d %d\n", + mpl_dbg(sdata, + "Mesh plink (peer, state, llid, plid, event): %pM %s %d %d %d\n", mgmt->sa, mplstates[sta->plink_state], le16_to_cpu(sta->llid), le16_to_cpu(sta->plid), event); @@ -851,7 +854,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m mesh_plink_inc_estab_count(sdata); changed |= mesh_set_ht_prot_mode(sdata); changed |= BSS_CHANGED_BEACON; - mpl_dbg("Mesh plink with %pM ESTABLISHED\n", + mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); break; default: @@ -887,7 +890,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m mesh_plink_inc_estab_count(sdata); changed |= mesh_set_ht_prot_mode(sdata); changed |= BSS_CHANGED_BEACON; - mpl_dbg("Mesh plink with %pM ESTABLISHED\n", + mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CONFIRM, diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c index 0ccdad49f987..accfa00ffcdf 100644 --- a/net/mac80211/mesh_sync.c +++ b/net/mac80211/mesh_sync.c @@ -12,13 +12,6 @@ #include "mesh.h" #include "driver-ops.h" -#ifdef CONFIG_MAC80211_VERBOSE_MESH_SYNC_DEBUG -#define msync_dbg(fmt, args...) \ - pr_debug("Mesh sync (%s): " fmt "\n", sdata->name, ##args) -#else -#define msync_dbg(fmt, args...) do { (void)(0); } while (0) -#endif - /* This is not in the standard. It represents a tolerable tbtt drift below * which we do no TSF adjustment. */ @@ -65,14 +58,14 @@ void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata) spin_lock_bh(&ifmsh->sync_offset_lock); if (ifmsh->sync_offset_clockdrift_max < beacon_int_fraction) { - msync_dbg("TBTT : max clockdrift=%lld; adjusting", - (long long) ifmsh->sync_offset_clockdrift_max); + msync_dbg(sdata, "TBTT : max clockdrift=%lld; adjusting\n", + (long long) ifmsh->sync_offset_clockdrift_max); tsfdelta = -ifmsh->sync_offset_clockdrift_max; ifmsh->sync_offset_clockdrift_max = 0; } else { - msync_dbg("TBTT : max clockdrift=%lld; adjusting by %llu", - (long long) ifmsh->sync_offset_clockdrift_max, - (unsigned long long) beacon_int_fraction); + msync_dbg(sdata, "TBTT : max clockdrift=%lld; adjusting by %llu\n", + (long long) ifmsh->sync_offset_clockdrift_max, + (unsigned long long) beacon_int_fraction); tsfdelta = -beacon_int_fraction; ifmsh->sync_offset_clockdrift_max -= beacon_int_fraction; } @@ -120,7 +113,7 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) { clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); - msync_dbg("STA %pM : is adjusting TBTT", sta->sta.addr); + msync_dbg(sdata, "STA %pM : is adjusting TBTT\n", sta->sta.addr); goto no_sync; } @@ -169,7 +162,8 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { s64 t_clockdrift = sta->t_offset_setpoint - sta->t_offset; - msync_dbg("STA %pM : sta->t_offset=%lld, sta->t_offset_setpoint=%lld, t_clockdrift=%lld", + msync_dbg(sdata, + "STA %pM : sta->t_offset=%lld, sta->t_offset_setpoint=%lld, t_clockdrift=%lld\n", sta->sta.addr, (long long) sta->t_offset, (long long) @@ -178,7 +172,8 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, if (t_clockdrift > TOFFSET_MAXIMUM_ADJUSTMENT || t_clockdrift < -TOFFSET_MAXIMUM_ADJUSTMENT) { - msync_dbg("STA %pM : t_clockdrift=%lld too large, setpoint reset", + msync_dbg(sdata, + "STA %pM : t_clockdrift=%lld too large, setpoint reset\n", sta->sta.addr, (long long) t_clockdrift); clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); @@ -197,8 +192,8 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, } else { sta->t_offset_setpoint = sta->t_offset - TOFFSET_SET_MARGIN; set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); - msync_dbg("STA %pM : offset was invalid, " - " sta->t_offset=%lld", + msync_dbg(sdata, + "STA %pM : offset was invalid, sta->t_offset=%lld\n", sta->sta.addr, (long long) sta->t_offset); rcu_read_unlock(); @@ -226,17 +221,15 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata) * to the driver tsf setter, we punt * the tsf adjustment to the mesh tasklet */ - msync_dbg("TBTT : kicking off TBTT " - "adjustment with " - "clockdrift_max=%lld", - ifmsh->sync_offset_clockdrift_max); + msync_dbg(sdata, + "TBTT : kicking off TBTT adjustment with clockdrift_max=%lld\n", + ifmsh->sync_offset_clockdrift_max); set_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags); } else { - msync_dbg("TBTT : max clockdrift=%lld; " - "too small to adjust", - (long long) - ifmsh->sync_offset_clockdrift_max); + msync_dbg(sdata, + "TBTT : max clockdrift=%lld; too small to adjust\n", + (long long)ifmsh->sync_offset_clockdrift_max); ifmsh->sync_offset_clockdrift_max = 0; } spin_unlock_bh(&ifmsh->sync_offset_lock); @@ -268,7 +261,7 @@ static void mesh_sync_vendor_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, const u8 *oui; WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR); - msync_dbg("called mesh_sync_vendor_rx_bcn_presp"); + msync_dbg(sdata, "called mesh_sync_vendor_rx_bcn_presp\n"); oui = mesh_get_vendor_oui(sdata); /* here you would implement the vendor offset tracking for this oui */ } @@ -278,7 +271,7 @@ static void mesh_sync_vendor_adjust_tbtt(struct ieee80211_sub_if_data *sdata) const u8 *oui; WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR); - msync_dbg("called mesh_sync_vendor_adjust_tbtt"); + msync_dbg(sdata, "called mesh_sync_vendor_adjust_tbtt\n"); oui = mesh_get_vendor_oui(sdata); /* here you would implement the vendor tsf adjustment for this oui */ } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2b450f541993..e11cd0e033ef 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1186,19 +1186,16 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, params.txop = get_unaligned_le16(pos + 2); params.uapsd = uapsd; -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, - "WMM queue=%d aci=%d acm=%d aifs=%d " - "cWmin=%d cWmax=%d txop=%d uapsd=%d\n", - queue, aci, acm, - params.aifs, params.cw_min, params.cw_max, - params.txop, params.uapsd); -#endif + mlme_dbg(sdata, + "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d\n", + queue, aci, acm, + params.aifs, params.cw_min, params.cw_max, + params.txop, params.uapsd); sdata->tx_conf[queue] = params; if (drv_conf_tx(local, sdata, queue, ¶ms)) - wiphy_debug(local->hw.wiphy, - "failed to set TX queue parameters for queue %d\n", - queue); + sdata_err(sdata, + "failed to set TX queue parameters for queue %d\n", + queue); } /* enable WMM or activate new settings */ @@ -1565,11 +1562,10 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, goto out; } -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG if (beacon) - net_dbg_ratelimited("%s: detected beacon loss from AP - sending probe request\n", - sdata->name); -#endif + mlme_dbg_ratelimited(sdata, + "detected beacon loss from AP - sending probe request\n"); + ieee80211_cqm_rssi_notify(&sdata->vif, NL80211_CQM_RSSI_BEACON_LOSS_EVENT, GFP_KERNEL); @@ -1654,7 +1650,7 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN); - pr_debug("%s: Connection to AP %pM lost\n", sdata->name, bssid); + sdata_info(sdata, "Connection to AP %pM lost\n", bssid); ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, @@ -1788,8 +1784,8 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, return RX_MGMT_NONE; if (status_code != WLAN_STATUS_SUCCESS) { - pr_debug("%s: %pM denied authentication (status %d)\n", - sdata->name, mgmt->sa, status_code); + sdata_info(sdata, "%pM denied authentication (status %d)\n", + mgmt->sa, status_code); ieee80211_destroy_auth_data(sdata, false); return RX_MGMT_CFG80211_RX_AUTH; } @@ -1812,7 +1808,7 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, return RX_MGMT_NONE; } - pr_debug("%s: authenticated\n", sdata->name); + sdata_info(sdata, "authenticated\n"); ifmgd->auth_data->done = true; ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC; run_again(ifmgd, ifmgd->auth_data->timeout); @@ -1825,7 +1821,7 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, goto out_err; } if (sta_info_move_state(sta, IEEE80211_STA_AUTH)) { - pr_debug("%s: failed moving %pM to auth\n", sdata->name, bssid); + sdata_info(sdata, "failed moving %pM to auth\n", bssid); goto out_err; } mutex_unlock(&sdata->local->sta_mtx); @@ -1859,8 +1855,8 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); - pr_debug("%s: deauthenticated from %pM (Reason: %u)\n", - sdata->name, bssid, reason_code); + sdata_info(sdata, "deauthenticated from %pM (Reason: %u)\n", + bssid, reason_code); ieee80211_set_disassoc(sdata, 0, 0, false, NULL); @@ -1890,8 +1886,8 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); - pr_debug("%s: disassociated from %pM (Reason: %u)\n", - sdata->name, mgmt->sa, reason_code); + sdata_info(sdata, "disassociated from %pM (Reason: %u)\n", + mgmt->sa, reason_code); ieee80211_set_disassoc(sdata, 0, 0, false, NULL); @@ -1983,15 +1979,15 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) - pr_debug("%s: invalid AID value 0x%x; bits 15:14 not set\n", - sdata->name, aid); + sdata_info(sdata, "invalid AID value 0x%x; bits 15:14 not set\n", + aid); aid &= ~(BIT(15) | BIT(14)); ifmgd->broken_ap = false; if (aid == 0 || aid > IEEE80211_MAX_AID) { - pr_debug("%s: invalid AID value %d (out of range), turn off PS\n", - sdata->name, aid); + sdata_info(sdata, "invalid AID value %d (out of range), turn off PS\n", + aid); aid = 0; ifmgd->broken_ap = true; } @@ -2000,8 +1996,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); if (!elems.supp_rates) { - pr_debug("%s: no SuppRates element in AssocResp\n", - sdata->name); + sdata_info(sdata, "no SuppRates element in AssocResp\n"); return false; } @@ -2041,8 +2036,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) err = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); if (err) { - pr_debug("%s: failed to move station %pM to desired state\n", - sdata->name, sta->sta.addr); + sdata_info(sdata, + "failed to move station %pM to desired state\n", + sta->sta.addr); WARN_ON(__sta_info_destroy(sta)); mutex_unlock(&sdata->local->sta_mtx); return false; @@ -2125,9 +2121,10 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); aid = le16_to_cpu(mgmt->u.assoc_resp.aid); - pr_debug("%s: RX %sssocResp from %pM (capab=0x%x status=%d aid=%d)\n", - sdata->name, reassoc ? "Rea" : "A", mgmt->sa, - capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); + sdata_info(sdata, + "RX %sssocResp from %pM (capab=0x%x status=%d aid=%d)\n", + reassoc ? "Rea" : "A", mgmt->sa, + capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); pos = mgmt->u.assoc_resp.variable; ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); @@ -2138,8 +2135,9 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, u32 tu, ms; tu = get_unaligned_le32(elems.timeout_int + 1); ms = tu * 1024 / 1000; - pr_debug("%s: %pM rejected association temporarily; comeback duration %u TU (%u ms)\n", - sdata->name, mgmt->sa, tu, ms); + sdata_info(sdata, + "%pM rejected association temporarily; comeback duration %u TU (%u ms)\n", + mgmt->sa, tu, ms); assoc_data->timeout = jiffies + msecs_to_jiffies(ms); if (ms > IEEE80211_ASSOC_TIMEOUT) run_again(ifmgd, assoc_data->timeout); @@ -2149,11 +2147,11 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, *bss = assoc_data->bss; if (status_code != WLAN_STATUS_SUCCESS) { - pr_debug("%s: %pM denied association (code=%d)\n", - sdata->name, mgmt->sa, status_code); + sdata_info(sdata, "%pM denied association (code=%d)\n", + mgmt->sa, status_code); ieee80211_destroy_assoc_data(sdata, false); } else { - pr_debug("%s: associated\n", sdata->name); + sdata_info(sdata, "associated\n"); if (!ieee80211_assoc_success(sdata, *bss, mgmt, len)) { /* oops -- internal error -- send timeout for now */ @@ -2261,7 +2259,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, if (ifmgd->auth_data && !ifmgd->auth_data->bss->proberesp_ies && ether_addr_equal(mgmt->bssid, ifmgd->auth_data->bss->bssid)) { /* got probe response, continue with auth */ - pr_debug("%s: direct probe responded\n", sdata->name); + sdata_info(sdata, "direct probe responded\n"); ifmgd->auth_data->tries = 0; ifmgd->auth_data->timeout = jiffies; run_again(ifmgd, ifmgd->auth_data->timeout); @@ -2397,10 +2395,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, } if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - net_dbg_ratelimited("%s: cancelling probereq poll due to a received beacon\n", - sdata->name); -#endif + mlme_dbg_ratelimited(sdata, + "cancelling probereq poll due to a received beacon\n"); mutex_lock(&local->mtx); ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL; ieee80211_run_deferred_scan(local); @@ -2625,8 +2621,8 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) auth_data->tries++; if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) { - pr_debug("%s: authentication with %pM timed out\n", - sdata->name, auth_data->bss->bssid); + sdata_info(sdata, "authentication with %pM timed out\n", + auth_data->bss->bssid); /* * Most likely AP is not in the range so remove the @@ -2638,9 +2634,9 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) } if (auth_data->bss->proberesp_ies) { - pr_debug("%s: send auth to %pM (try %d/%d)\n", - sdata->name, auth_data->bss->bssid, auth_data->tries, - IEEE80211_AUTH_MAX_TRIES); + sdata_info(sdata, "send auth to %pM (try %d/%d)\n", + auth_data->bss->bssid, auth_data->tries, + IEEE80211_AUTH_MAX_TRIES); auth_data->expected_transaction = 2; ieee80211_send_auth(sdata, 1, auth_data->algorithm, @@ -2650,9 +2646,9 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) } else { const u8 *ssidie; - pr_debug("%s: direct probe to %pM (try %d/%i)\n", - sdata->name, auth_data->bss->bssid, auth_data->tries, - IEEE80211_AUTH_MAX_TRIES); + sdata_info(sdata, "direct probe to %pM (try %d/%i)\n", + auth_data->bss->bssid, auth_data->tries, + IEEE80211_AUTH_MAX_TRIES); ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID); if (!ssidie) @@ -2680,8 +2676,8 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata) assoc_data->tries++; if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) { - pr_debug("%s: association with %pM timed out\n", - sdata->name, assoc_data->bss->bssid); + sdata_info(sdata, "association with %pM timed out\n", + assoc_data->bss->bssid); /* * Most likely AP is not in the range so remove the @@ -2692,9 +2688,9 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata) return -ETIMEDOUT; } - pr_debug("%s: associate with %pM (try %d/%d)\n", - sdata->name, assoc_data->bss->bssid, assoc_data->tries, - IEEE80211_ASSOC_MAX_TRIES); + sdata_info(sdata, "associate with %pM (try %d/%d)\n", + assoc_data->bss->bssid, assoc_data->tries, + IEEE80211_ASSOC_MAX_TRIES); ieee80211_send_assoc(sdata); assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; @@ -2767,45 +2763,31 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) ieee80211_reset_ap_probe(sdata); else if (ifmgd->nullfunc_failed) { if (ifmgd->probe_send_count < max_tries) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, - "%s: No ack for nullfunc frame to" - " AP %pM, try %d/%i\n", - sdata->name, bssid, - ifmgd->probe_send_count, max_tries); -#endif + mlme_dbg(sdata, + "No ack for nullfunc frame to AP %pM, try %d/%i\n", + bssid, ifmgd->probe_send_count, + max_tries); ieee80211_mgd_probe_ap_send(sdata); } else { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, - "%s: No ack for nullfunc frame to" - " AP %pM, disconnecting.\n", - sdata->name, bssid); -#endif + mlme_dbg(sdata, + "No ack for nullfunc frame to AP %pM, disconnecting.\n", + bssid); ieee80211_sta_connection_lost(sdata, bssid, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); } } else if (time_is_after_jiffies(ifmgd->probe_timeout)) run_again(ifmgd, ifmgd->probe_timeout); else if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, - "%s: Failed to send nullfunc to AP %pM" - " after %dms, disconnecting.\n", - sdata->name, - bssid, probe_wait_ms); -#endif + mlme_dbg(sdata, + "Failed to send nullfunc to AP %pM after %dms, disconnecting\n", + bssid, probe_wait_ms); ieee80211_sta_connection_lost(sdata, bssid, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); } else if (ifmgd->probe_send_count < max_tries) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, - "%s: No probe response from AP %pM" - " after %dms, try %d/%i\n", - sdata->name, - bssid, probe_wait_ms, - ifmgd->probe_send_count, max_tries); -#endif + mlme_dbg(sdata, + "No probe response from AP %pM after %dms, try %d/%i\n", + bssid, probe_wait_ms, + ifmgd->probe_send_count, max_tries); ieee80211_mgd_probe_ap_send(sdata); } else { /* @@ -2920,11 +2902,8 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME; mutex_lock(&ifmgd->mtx); if (ifmgd->associated) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(sdata->local->hw.wiphy, - "%s: driver requested disconnect after resume.\n", - sdata->name); -#endif + mlme_dbg(sdata, + "driver requested disconnect after resume\n"); ieee80211_sta_connection_lost(sdata, ifmgd->associated->bssid, WLAN_REASON_UNSPECIFIED); @@ -3065,10 +3044,11 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, * since we look at probe response/beacon data here * it should be OK. */ - pr_debug("%s: Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", - sdata->name, cbss->channel->center_freq, - ht_cfreq, ht_oper->primary_chan, - cbss->channel->band); + sdata_info(sdata, + "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", + cbss->channel->center_freq, + ht_cfreq, ht_oper->primary_chan, + cbss->channel->band); ht_oper = NULL; } } @@ -3092,8 +3072,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, if (!ieee80211_set_channel_type(local, sdata, channel_type)) { /* can only fail due to HT40+/- mismatch */ channel_type = NL80211_CHAN_HT20; - pr_debug("%s: disabling 40 MHz due to multi-vif mismatch\n", - sdata->name); + sdata_info(sdata, + "disabling 40 MHz due to multi-vif mismatch\n"); ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ; WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type)); @@ -3122,8 +3102,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, * we can connect -- with a warning. */ if (!basic_rates && min_rate_index >= 0) { - pr_debug("%s: No basic rates, using min rate instead\n", - sdata->name); + sdata_info(sdata, + "No basic rates, using min rate instead\n"); basic_rates = BIT(min_rate_index); } @@ -3149,8 +3129,9 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, err = sta_info_insert(sta); sta = NULL; if (err) { - pr_debug("%s: failed to insert STA entry for the AP (error %d)\n", - sdata->name, err); + sdata_info(sdata, + "failed to insert STA entry for the AP (error %d)\n", + err); return err; } } else @@ -3228,7 +3209,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, if (ifmgd->associated) ieee80211_set_disassoc(sdata, 0, 0, false, NULL); - pr_debug("%s: authenticate with %pM\n", sdata->name, req->bss->bssid); + sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid); err = ieee80211_prep_connection(sdata, req->bss, false); if (err) @@ -3410,8 +3391,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, * Wait up to one beacon interval ... * should this be more if we miss one? */ - pr_debug("%s: waiting for beacon from %pM\n", - sdata->name, ifmgd->bssid); + sdata_info(sdata, "waiting for beacon from %pM\n", + ifmgd->bssid); assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval); } else { assoc_data->have_beacon = true; @@ -3430,8 +3411,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, corrupt_type = "beacon"; } else if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_PROBE_RESP) corrupt_type = "probe response"; - pr_debug("%s: associating with AP with corrupt %s\n", - sdata->name, corrupt_type); + sdata_info(sdata, "associating with AP with corrupt %s\n", + corrupt_type); } err = 0; @@ -3460,8 +3441,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, return 0; } - pr_debug("%s: deauthenticating from %pM by local choice (reason=%d)\n", - sdata->name, req->bssid, req->reason_code); + sdata_info(sdata, + "deauthenticating from %pM by local choice (reason=%d)\n", + req->bssid, req->reason_code); if (ifmgd->associated && ether_addr_equal(ifmgd->associated->bssid, req->bssid)) @@ -3503,8 +3485,9 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, return -ENOLINK; } - pr_debug("%s: disassociating from %pM by local choice (reason=%d)\n", - sdata->name, req->bss->bssid, req->reason_code); + sdata_info(sdata, + "disassociating from %pM by local choice (reason=%d)\n", + req->bss->bssid, req->reason_code); memcpy(bssid, req->bss->bssid, ETH_ALEN); ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC, diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 9f1bd692589b..ab5185054e6c 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -632,11 +632,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, HT_RX_REORDER_BUF_TIMEOUT)) goto set_release_timer; -#ifdef CONFIG_MAC80211_HT_DEBUG - if (net_ratelimit()) - wiphy_debug(sdata->local->hw.wiphy, - "release an RX reorder frame due to timeout on earlier frames\n"); -#endif + ht_dbg_ratelimited(sdata, + "release an RX reorder frame due to timeout on earlier frames\n"); ieee80211_release_reorder_frame(sdata, tid_agg_rx, j); /* @@ -1136,24 +1133,18 @@ static void ap_sta_ps_start(struct sta_info *sta) set_sta_flag(sta, WLAN_STA_PS_STA); if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta); -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - pr_debug("%s: STA %pM aid %d enters power save mode\n", - sdata->name, sta->sta.addr, sta->sta.aid); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + ps_dbg(sdata, "STA %pM aid %d enters power save mode\n", + sta->sta.addr, sta->sta.aid); } static void ap_sta_ps_end(struct sta_info *sta) { -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - pr_debug("%s: STA %pM aid %d exits power save mode\n", - sta->sdata->name, sta->sta.addr, sta->sta.aid); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + ps_dbg(sta->sdata, "STA %pM aid %d exits power save mode\n", + sta->sta.addr, sta->sta.aid); if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - pr_debug("%s: STA %pM aid %d driver-ps-blocked\n", - sta->sdata->name, sta->sta.addr, sta->sta.aid); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + ps_dbg(sta->sdata, "STA %pM aid %d driver-ps-blocked\n", + sta->sta.addr, sta->sta.aid); return; } @@ -1383,17 +1374,8 @@ ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata, if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX) sdata->fragment_next = 0; - if (!skb_queue_empty(&entry->skb_list)) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *) entry->skb_list.next->data; - pr_debug("%s: RX reassembly removed oldest fragment entry (idx=%d age=%lu seq=%d last_frag=%d addr1=%pM addr2=%pM\n", - sdata->name, idx, - jiffies - entry->first_frag_time, entry->seq, - entry->last_frag, hdr->addr1, hdr->addr2); -#endif + if (!skb_queue_empty(&entry->skb_list)) __skb_queue_purge(&entry->skb_list); - } __skb_queue_tail(&entry->skb_list, *skb); /* no need for locking */ *skb = NULL; @@ -1751,7 +1733,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx) */ xmit_skb = skb_copy(skb, GFP_ATOMIC); if (!xmit_skb) - net_dbg_ratelimited("%s: failed to clone multicast frame\n", + net_info_ratelimited("%s: failed to clone multicast frame\n", dev->name); } else { dsta = sta_info_get(sdata, skb->data); @@ -1955,7 +1937,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) fwd_skb = skb_copy(skb, GFP_ATOMIC); if (!fwd_skb) { - net_dbg_ratelimited("%s: failed to clone mesh frame\n", + net_info_ratelimited("%s: failed to clone mesh frame\n", sdata->name); goto out; } diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 77dcf2f89d42..06fa75ceb025 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -169,9 +169,7 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) if (sta->rate_ctrl) rate_control_free_sta(sta); -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, "Destroyed STA %pM\n", sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr); kfree(sta); } @@ -278,9 +276,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, for (i = 0; i < NUM_RX_DATA_QUEUES; i++) sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX); -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, "Allocated STA %pM\n", sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); #ifdef CONFIG_MAC80211_MESH sta->plink_state = NL80211_PLINK_LISTEN; @@ -333,8 +329,9 @@ static int sta_info_insert_drv_state(struct ieee80211_local *local, } if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { - pr_debug("%s: failed to move IBSS STA %pM to state %d (%d) - keeping it anyway\n", - sdata->name, sta->sta.addr, state + 1, err); + sdata_info(sdata, + "failed to move IBSS STA %pM to state %d (%d) - keeping it anyway\n", + sta->sta.addr, state + 1, err); err = 0; } @@ -389,9 +386,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) sinfo.generation = local->sta_generation; cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, "Inserted STA %pM\n", sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + sta_dbg(sdata, "Inserted STA %pM\n", sta->sta.addr); /* move reference to rcu-protected */ rcu_read_lock(); @@ -617,9 +612,8 @@ static bool sta_info_cleanup_expire_buffered_ac(struct ieee80211_local *local, break; local->total_ps_buffered--; -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - pr_debug("Buffered frame expired (STA %pM)\n", sta->sta.addr); -#endif + ps_dbg(sta->sdata, "Buffered frame expired (STA %pM)\n", + sta->sta.addr); dev_kfree_skb(skb); } @@ -745,9 +739,8 @@ int __must_check __sta_info_destroy(struct sta_info *sta) mesh_accept_plinks_update(sdata); #endif -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, "Removed STA %pM\n", sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr); + cancel_work_sync(&sta->drv_unblock_wk); cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL); @@ -887,8 +880,8 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, continue; if (time_after(jiffies, sta->last_rx + exp_time)) { - ibss_vdbg("%s: expiring inactive STA %pM\n", - sdata->name, sta->sta.addr); + ibss_dbg(sdata, "expiring inactive STA %pM\n", + sta->sta.addr); WARN_ON(__sta_info_destroy(sta)); } } @@ -986,10 +979,9 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) sta_info_recalc_tim(sta); -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - pr_debug("%s: STA %pM aid %d sending %d filtered/%d PS frames since STA not sleeping anymore\n", - sdata->name, sta->sta.addr, sta->sta.aid, filtered, buffered); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + ps_dbg(sdata, + "STA %pM aid %d sending %d filtered/%d PS frames since STA not sleeping anymore\n", + sta->sta.addr, sta->sta.aid, filtered, buffered); } static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, @@ -1379,10 +1371,8 @@ int sta_info_move_state(struct sta_info *sta, return -EINVAL; } -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - pr_debug("%s: moving STA %pM to state %d\n", - sta->sdata->name, sta->sta.addr, new_state); -#endif + sta_dbg(sta->sdata, "moving STA %pM to state %d\n", + sta->sta.addr, new_state); /* * notify the driver before the actual changes so it can diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 51a6d1e6e250..2ed2f27fe8a7 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -155,13 +155,10 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, return; } -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - if (net_ratelimit()) - wiphy_debug(local->hw.wiphy, - "dropped TX filtered frame, queue_len=%d PS=%d @%lu\n", - skb_queue_len(&sta->tx_filtered[ac]), - !!test_sta_flag(sta, WLAN_STA_PS_STA), jiffies); -#endif + ps_dbg_ratelimited(sta->sdata, + "dropped TX filtered frame, queue_len=%d PS=%d @%lu\n", + skb_queue_len(&sta->tx_filtered[ac]), + !!test_sta_flag(sta, WLAN_STA_PS_STA), jiffies); dev_kfree_skb(skb); } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index af25c4e7ec5c..37eda7e00e03 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -297,9 +297,10 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) if (unlikely(!assoc && ieee80211_is_data(hdr->frame_control))) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG - pr_debug("%s: dropped data frame to not associated station %pM\n", - tx->sdata->name, hdr->addr1); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + sdata_info(tx->sdata, + "dropped data frame to not associated station %pM\n", + hdr->addr1); +#endif I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc); return TX_DROP; } @@ -366,10 +367,7 @@ static void purge_old_ps_buffers(struct ieee80211_local *local) rcu_read_unlock(); local->total_ps_buffered = total; -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - wiphy_debug(local->hw.wiphy, "PS buffers full - purged %d frames\n", - purged); -#endif + ps_dbg_hw(&local->hw, "PS buffers full - purged %d frames\n", purged); } static ieee80211_tx_result @@ -411,10 +409,8 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx) purge_old_ps_buffers(tx->local); if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= AP_MAX_BC_BUFFER) { -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - net_dbg_ratelimited("%s: BC TX buffer full - dropping the oldest frame\n", - tx->sdata->name); -#endif + ps_dbg(tx->sdata, + "BC TX buffer full - dropping the oldest frame\n"); dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf)); } else tx->local->total_ps_buffered++; @@ -465,18 +461,15 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) return TX_CONTINUE; } -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - pr_debug("STA %pM aid %d: PS buffer for AC %d\n", - sta->sta.addr, sta->sta.aid, ac); -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + ps_dbg(sta->sdata, "STA %pM aid %d: PS buffer for AC %d\n", + sta->sta.addr, sta->sta.aid, ac); if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) purge_old_ps_buffers(tx->local); if (skb_queue_len(&sta->ps_tx_buf[ac]) >= STA_MAX_TX_BUFFER) { struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf[ac]); -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - net_dbg_ratelimited("%s: STA %pM TX buffer for AC %d full - dropping oldest frame\n", - tx->sdata->name, sta->sta.addr, ac); -#endif + ps_dbg(tx->sdata, + "STA %pM TX buffer for AC %d full - dropping oldest frame\n", + sta->sta.addr, ac); dev_kfree_skb(old); } else tx->local->total_ps_buffered++; @@ -498,13 +491,11 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) sta_info_recalc_tim(sta); return TX_QUEUED; + } else if (unlikely(test_sta_flag(sta, WLAN_STA_PS_STA))) { + ps_dbg(tx->sdata, + "STA %pM in PS mode, but polling/in SP -> send frame\n", + sta->sta.addr); } -#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG - else if (unlikely(test_sta_flag(sta, WLAN_STA_PS_STA))) { - pr_debug("%s: STA %pM in PS mode, but polling/in SP -> send frame\n", - tx->sdata->name, sta->sta.addr); - } -#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ return TX_CONTINUE; } @@ -1963,7 +1954,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, (cpu_to_be16(ethertype) != sdata->control_port_protocol || !ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG - net_dbg_ratelimited("%s: dropped frame to %pM (unauthorized port)\n", + net_info_ratelimited("%s: dropped frame to %pM (unauthorized port)\n", dev->name, hdr.addr1); #endif -- cgit v1.2.3 From a91a5ac6858fbf7477131e1210cb3e897b668e6f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 4 Jun 2012 20:40:53 -0700 Subject: mempool: add @gfp_mask to mempool_create_node() mempool_create_node() currently assumes %GFP_KERNEL. Its only user, blk_init_free_list(), is about to be updated to use other allocation flags - add @gfp_mask argument to the function. Signed-off-by: Tejun Heo Cc: Andrew Morton Cc: Hugh Dickins Signed-off-by: Jens Axboe --- block/blk-core.c | 4 ++-- include/linux/mempool.h | 3 ++- mm/mempool.c | 12 +++++++----- 3 files changed, 11 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 93eb3e4f88ce..64f9a8668253 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -531,8 +531,8 @@ static int blk_init_free_list(struct request_queue *q) init_waitqueue_head(&rl->wait[BLK_RW_ASYNC]); rl->rq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab, - mempool_free_slab, request_cachep, q->node); - + mempool_free_slab, request_cachep, + GFP_KERNEL, q->node); if (!rl->rq_pool) return -ENOMEM; diff --git a/include/linux/mempool.h b/include/linux/mempool.h index 7c08052e3321..39ed62ab5b8a 100644 --- a/include/linux/mempool.h +++ b/include/linux/mempool.h @@ -26,7 +26,8 @@ typedef struct mempool_s { extern mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, mempool_free_t *free_fn, void *pool_data); extern mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn, - mempool_free_t *free_fn, void *pool_data, int nid); + mempool_free_t *free_fn, void *pool_data, + gfp_t gfp_mask, int nid); extern int mempool_resize(mempool_t *pool, int new_min_nr, gfp_t gfp_mask); extern void mempool_destroy(mempool_t *pool); diff --git a/mm/mempool.c b/mm/mempool.c index d9049811f352..54990476c049 100644 --- a/mm/mempool.c +++ b/mm/mempool.c @@ -63,19 +63,21 @@ EXPORT_SYMBOL(mempool_destroy); mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, mempool_free_t *free_fn, void *pool_data) { - return mempool_create_node(min_nr,alloc_fn,free_fn, pool_data,-1); + return mempool_create_node(min_nr,alloc_fn,free_fn, pool_data, + GFP_KERNEL, NUMA_NO_NODE); } EXPORT_SYMBOL(mempool_create); mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn, - mempool_free_t *free_fn, void *pool_data, int node_id) + mempool_free_t *free_fn, void *pool_data, + gfp_t gfp_mask, int node_id) { mempool_t *pool; - pool = kmalloc_node(sizeof(*pool), GFP_KERNEL | __GFP_ZERO, node_id); + pool = kmalloc_node(sizeof(*pool), gfp_mask | __GFP_ZERO, node_id); if (!pool) return NULL; pool->elements = kmalloc_node(min_nr * sizeof(void *), - GFP_KERNEL, node_id); + gfp_mask, node_id); if (!pool->elements) { kfree(pool); return NULL; @@ -93,7 +95,7 @@ mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn, while (pool->curr_nr < pool->min_nr) { void *element; - element = pool->alloc(GFP_KERNEL, pool->pool_data); + element = pool->alloc(gfp_mask, pool->pool_data); if (unlikely(!element)) { mempool_destroy(pool); return NULL; -- cgit v1.2.3 From 86072d8112595ea1b6beeb33f578e7c2839e014e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 4 Jun 2012 20:40:54 -0700 Subject: block: drop custom queue draining used by scsi_transport_{iscsi|fc} iscsi_remove_host() uses bsg_remove_queue() which implements custom queue draining. fc_bsg_remove() open-codes mostly identical logic. The draining logic isn't correct in that blk_stop_queue() doesn't prevent new requests from being queued - it just stops processing, so nothing prevents new requests to be queued after the logic determines that the queue is drained. blk_cleanup_queue() now implements proper queue draining and these custom draining logics aren't necessary. Drop them and use bsg_unregister_queue() + blk_cleanup_queue() instead. Signed-off-by: Tejun Heo Reviewed-by: Mike Christie Acked-by: Vivek Goyal Cc: James Bottomley Cc: James Smart Signed-off-by: Jens Axboe --- block/bsg-lib.c | 53 ------------------------------------- drivers/scsi/scsi_transport_fc.c | 38 -------------------------- drivers/scsi/scsi_transport_iscsi.c | 2 +- include/linux/bsg-lib.h | 1 - 4 files changed, 1 insertion(+), 93 deletions(-) (limited to 'include') diff --git a/block/bsg-lib.c b/block/bsg-lib.c index 7ad49c88f6b1..deee61fbb741 100644 --- a/block/bsg-lib.c +++ b/block/bsg-lib.c @@ -243,56 +243,3 @@ int bsg_setup_queue(struct device *dev, struct request_queue *q, return 0; } EXPORT_SYMBOL_GPL(bsg_setup_queue); - -/** - * bsg_remove_queue - Deletes the bsg dev from the q - * @q: the request_queue that is to be torn down. - * - * Notes: - * Before unregistering the queue empty any requests that are blocked - */ -void bsg_remove_queue(struct request_queue *q) -{ - struct request *req; /* block request */ - int counts; /* totals for request_list count and starved */ - - if (!q) - return; - - /* Stop taking in new requests */ - spin_lock_irq(q->queue_lock); - blk_stop_queue(q); - - /* drain all requests in the queue */ - while (1) { - /* need the lock to fetch a request - * this may fetch the same reqeust as the previous pass - */ - req = blk_fetch_request(q); - /* save requests in use and starved */ - counts = q->rq.count[0] + q->rq.count[1] + - q->rq.starved[0] + q->rq.starved[1]; - spin_unlock_irq(q->queue_lock); - /* any requests still outstanding? */ - if (counts == 0) - break; - - /* This may be the same req as the previous iteration, - * always send the blk_end_request_all after a prefetch. - * It is not okay to not end the request because the - * prefetch started the request. - */ - if (req) { - /* return -ENXIO to indicate that this queue is - * going away - */ - req->errors = -ENXIO; - blk_end_request_all(req, -ENXIO); - } - - msleep(200); /* allow bsg to possibly finish */ - spin_lock_irq(q->queue_lock); - } - bsg_unregister_queue(q); -} -EXPORT_SYMBOL_GPL(bsg_remove_queue); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 579760420d53..a9617ad05f33 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -4130,45 +4130,7 @@ fc_bsg_rportadd(struct Scsi_Host *shost, struct fc_rport *rport) static void fc_bsg_remove(struct request_queue *q) { - struct request *req; /* block request */ - int counts; /* totals for request_list count and starved */ - if (q) { - /* Stop taking in new requests */ - spin_lock_irq(q->queue_lock); - blk_stop_queue(q); - - /* drain all requests in the queue */ - while (1) { - /* need the lock to fetch a request - * this may fetch the same reqeust as the previous pass - */ - req = blk_fetch_request(q); - /* save requests in use and starved */ - counts = q->rq.count[0] + q->rq.count[1] + - q->rq.starved[0] + q->rq.starved[1]; - spin_unlock_irq(q->queue_lock); - /* any requests still outstanding? */ - if (counts == 0) - break; - - /* This may be the same req as the previous iteration, - * always send the blk_end_request_all after a prefetch. - * It is not okay to not end the request because the - * prefetch started the request. - */ - if (req) { - /* return -ENXIO to indicate that this queue is - * going away - */ - req->errors = -ENXIO; - blk_end_request_all(req, -ENXIO); - } - - msleep(200); /* allow bsg to possibly finish */ - spin_lock_irq(q->queue_lock); - } - bsg_unregister_queue(q); blk_cleanup_queue(q); } diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 1cf640e575da..c737a16b0a1d 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -575,7 +575,7 @@ static int iscsi_remove_host(struct transport_container *tc, struct iscsi_cls_host *ihost = shost->shost_data; if (ihost->bsg_q) { - bsg_remove_queue(ihost->bsg_q); + bsg_unregister_queue(ihost->bsg_q); blk_cleanup_queue(ihost->bsg_q); } return 0; diff --git a/include/linux/bsg-lib.h b/include/linux/bsg-lib.h index f55ab8cdc106..4d0fb3df2f4a 100644 --- a/include/linux/bsg-lib.h +++ b/include/linux/bsg-lib.h @@ -67,7 +67,6 @@ void bsg_job_done(struct bsg_job *job, int result, int bsg_setup_queue(struct device *dev, struct request_queue *q, char *name, bsg_job_fn *job_fn, int dd_job_size); void bsg_request_fn(struct request_queue *q); -void bsg_remove_queue(struct request_queue *q); void bsg_goose_queue(struct request_queue *q); #endif -- cgit v1.2.3 From 8a5ecdd42862bf87ceab00bf2a15d7eabf58c02d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 4 Jun 2012 20:40:58 -0700 Subject: block: add q->nr_rqs[] and move q->rq.elvpriv to q->nr_rqs_elvpriv Add q->nr_rqs[] which currently behaves the same as q->rq.count[] and move q->rq.elvpriv to q->nr_rqs_elvpriv. blk_drain_queue() is updated to use q->nr_rqs[] instead of q->rq.count[]. These counters separates queue-wide request statistics from the request list and allow implementation of per-queue request allocation. While at it, properly indent fields of struct request_list. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-core.c | 13 +++++++------ include/linux/blkdev.h | 11 ++++++----- 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 71894e143b91..a2648153691f 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -387,7 +387,7 @@ void blk_drain_queue(struct request_queue *q, bool drain_all) if (!list_empty(&q->queue_head) && q->request_fn) __blk_run_queue(q); - drain |= q->rq.elvpriv; + drain |= q->nr_rqs_elvpriv; /* * Unfortunately, requests are queued at and tracked from @@ -397,7 +397,7 @@ void blk_drain_queue(struct request_queue *q, bool drain_all) if (drain_all) { drain |= !list_empty(&q->queue_head); for (i = 0; i < 2; i++) { - drain |= q->rq.count[i]; + drain |= q->nr_rqs[i]; drain |= q->in_flight[i]; drain |= !list_empty(&q->flush_queue[i]); } @@ -526,7 +526,6 @@ static int blk_init_free_list(struct request_queue *q) rl->count[BLK_RW_SYNC] = rl->count[BLK_RW_ASYNC] = 0; rl->starved[BLK_RW_SYNC] = rl->starved[BLK_RW_ASYNC] = 0; - rl->elvpriv = 0; init_waitqueue_head(&rl->wait[BLK_RW_SYNC]); init_waitqueue_head(&rl->wait[BLK_RW_ASYNC]); @@ -791,9 +790,10 @@ static void freed_request(struct request_queue *q, unsigned int flags) struct request_list *rl = &q->rq; int sync = rw_is_sync(flags); + q->nr_rqs[sync]--; rl->count[sync]--; if (flags & REQ_ELVPRIV) - rl->elvpriv--; + q->nr_rqs_elvpriv--; __freed_request(q, sync); @@ -902,6 +902,7 @@ static struct request *__get_request(struct request_queue *q, int rw_flags, if (rl->count[is_sync] >= (3 * q->nr_requests / 2)) return NULL; + q->nr_rqs[is_sync]++; rl->count[is_sync]++; rl->starved[is_sync] = 0; @@ -917,7 +918,7 @@ static struct request *__get_request(struct request_queue *q, int rw_flags, */ if (blk_rq_should_init_elevator(bio) && !blk_queue_bypass(q)) { rw_flags |= REQ_ELVPRIV; - rl->elvpriv++; + q->nr_rqs_elvpriv++; if (et->icq_cache && ioc) icq = ioc_lookup_icq(ioc, q); } @@ -978,7 +979,7 @@ fail_elvpriv: rq->elv.icq = NULL; spin_lock_irq(q->queue_lock); - rl->elvpriv--; + q->nr_rqs_elvpriv--; spin_unlock_irq(q->queue_lock); goto out; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 07954b05b86c..7e44ed93f84b 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -51,11 +51,10 @@ struct request_list { * count[], starved[], and wait[] are indexed by * BLK_RW_SYNC/BLK_RW_ASYNC */ - int count[2]; - int starved[2]; - int elvpriv; - mempool_t *rq_pool; - wait_queue_head_t wait[2]; + int count[2]; + int starved[2]; + mempool_t *rq_pool; + wait_queue_head_t wait[2]; }; /* @@ -282,6 +281,8 @@ struct request_queue { struct list_head queue_head; struct request *last_merge; struct elevator_queue *elevator; + int nr_rqs[2]; /* # allocated [a]sync rqs */ + int nr_rqs_elvpriv; /* # allocated rqs w/ elvpriv */ /* * the queue request freelist, one for reads and one for writes -- cgit v1.2.3 From 5b788ce3e2acac9bf109743b1281d77347cf2101 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 4 Jun 2012 20:40:59 -0700 Subject: block: prepare for multiple request_lists Request allocation is about to be made per-blkg meaning that there'll be multiple request lists. * Make queue full state per request_list. blk_*queue_full() functions are renamed to blk_*rl_full() and takes @rl instead of @q. * Rename blk_init_free_list() to blk_init_rl() and make it take @rl instead of @q. Also add @gfp_mask parameter. * Add blk_exit_rl() instead of destroying rl directly from blk_release_queue(). * Add request_list->q and make request alloc/free functions - blk_free_request(), [__]freed_request(), __get_request() - take @rl instead of @q. This patch doesn't introduce any functional difference. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-core.c | 56 ++++++++++++++++++++++++++++---------------------- block/blk-sysfs.c | 12 +++++------ block/blk.h | 3 +++ include/linux/blkdev.h | 32 ++++++++++++++++------------- 4 files changed, 57 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index a2648153691f..f392a2edf462 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -517,13 +517,13 @@ void blk_cleanup_queue(struct request_queue *q) } EXPORT_SYMBOL(blk_cleanup_queue); -static int blk_init_free_list(struct request_queue *q) +int blk_init_rl(struct request_list *rl, struct request_queue *q, + gfp_t gfp_mask) { - struct request_list *rl = &q->rq; - if (unlikely(rl->rq_pool)) return 0; + rl->q = q; rl->count[BLK_RW_SYNC] = rl->count[BLK_RW_ASYNC] = 0; rl->starved[BLK_RW_SYNC] = rl->starved[BLK_RW_ASYNC] = 0; init_waitqueue_head(&rl->wait[BLK_RW_SYNC]); @@ -531,13 +531,19 @@ static int blk_init_free_list(struct request_queue *q) rl->rq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab, mempool_free_slab, request_cachep, - GFP_KERNEL, q->node); + gfp_mask, q->node); if (!rl->rq_pool) return -ENOMEM; return 0; } +void blk_exit_rl(struct request_list *rl) +{ + if (rl->rq_pool) + mempool_destroy(rl->rq_pool); +} + struct request_queue *blk_alloc_queue(gfp_t gfp_mask) { return blk_alloc_queue_node(gfp_mask, -1); @@ -679,7 +685,7 @@ blk_init_allocated_queue(struct request_queue *q, request_fn_proc *rfn, if (!q) return NULL; - if (blk_init_free_list(q)) + if (blk_init_rl(&q->rq, q, GFP_KERNEL)) return NULL; q->request_fn = rfn; @@ -721,15 +727,15 @@ bool blk_get_queue(struct request_queue *q) } EXPORT_SYMBOL(blk_get_queue); -static inline void blk_free_request(struct request_queue *q, struct request *rq) +static inline void blk_free_request(struct request_list *rl, struct request *rq) { if (rq->cmd_flags & REQ_ELVPRIV) { - elv_put_request(q, rq); + elv_put_request(rl->q, rq); if (rq->elv.icq) put_io_context(rq->elv.icq->ioc); } - mempool_free(rq, q->rq.rq_pool); + mempool_free(rq, rl->rq_pool); } /* @@ -766,9 +772,9 @@ static void ioc_set_batching(struct request_queue *q, struct io_context *ioc) ioc->last_waited = jiffies; } -static void __freed_request(struct request_queue *q, int sync) +static void __freed_request(struct request_list *rl, int sync) { - struct request_list *rl = &q->rq; + struct request_queue *q = rl->q; if (rl->count[sync] < queue_congestion_off_threshold(q)) blk_clear_queue_congested(q, sync); @@ -777,7 +783,7 @@ static void __freed_request(struct request_queue *q, int sync) if (waitqueue_active(&rl->wait[sync])) wake_up(&rl->wait[sync]); - blk_clear_queue_full(q, sync); + blk_clear_rl_full(rl, sync); } } @@ -785,9 +791,9 @@ static void __freed_request(struct request_queue *q, int sync) * A request has just been released. Account for it, update the full and * congestion status, wake up any waiters. Called under q->queue_lock. */ -static void freed_request(struct request_queue *q, unsigned int flags) +static void freed_request(struct request_list *rl, unsigned int flags) { - struct request_list *rl = &q->rq; + struct request_queue *q = rl->q; int sync = rw_is_sync(flags); q->nr_rqs[sync]--; @@ -795,10 +801,10 @@ static void freed_request(struct request_queue *q, unsigned int flags) if (flags & REQ_ELVPRIV) q->nr_rqs_elvpriv--; - __freed_request(q, sync); + __freed_request(rl, sync); if (unlikely(rl->starved[sync ^ 1])) - __freed_request(q, sync ^ 1); + __freed_request(rl, sync ^ 1); } /* @@ -838,7 +844,7 @@ static struct io_context *rq_ioc(struct bio *bio) /** * __get_request - get a free request - * @q: request_queue to allocate request from + * @rl: request list to allocate from * @rw_flags: RW and SYNC flags * @bio: bio to allocate request for (can be %NULL) * @gfp_mask: allocation mask @@ -850,11 +856,11 @@ static struct io_context *rq_ioc(struct bio *bio) * Returns %NULL on failure, with @q->queue_lock held. * Returns !%NULL on success, with @q->queue_lock *not held*. */ -static struct request *__get_request(struct request_queue *q, int rw_flags, +static struct request *__get_request(struct request_list *rl, int rw_flags, struct bio *bio, gfp_t gfp_mask) { + struct request_queue *q = rl->q; struct request *rq; - struct request_list *rl = &q->rq; struct elevator_type *et = q->elevator->type; struct io_context *ioc = rq_ioc(bio); struct io_cq *icq = NULL; @@ -876,9 +882,9 @@ static struct request *__get_request(struct request_queue *q, int rw_flags, * This process will be allowed to complete a batch of * requests, others will be blocked. */ - if (!blk_queue_full(q, is_sync)) { + if (!blk_rl_full(rl, is_sync)) { ioc_set_batching(q, ioc); - blk_set_queue_full(q, is_sync); + blk_set_rl_full(rl, is_sync); } else { if (may_queue != ELV_MQUEUE_MUST && !ioc_batching(q, ioc)) { @@ -928,7 +934,7 @@ static struct request *__get_request(struct request_queue *q, int rw_flags, spin_unlock_irq(q->queue_lock); /* allocate and init request */ - rq = mempool_alloc(q->rq.rq_pool, gfp_mask); + rq = mempool_alloc(rl->rq_pool, gfp_mask); if (!rq) goto fail_alloc; @@ -992,7 +998,7 @@ fail_alloc: * queue, but this is pretty rare. */ spin_lock_irq(q->queue_lock); - freed_request(q, rw_flags); + freed_request(rl, rw_flags); /* * in the very unlikely event that allocation failed and no @@ -1029,7 +1035,7 @@ static struct request *get_request(struct request_queue *q, int rw_flags, struct request_list *rl = &q->rq; struct request *rq; retry: - rq = __get_request(q, rw_flags, bio, gfp_mask); + rq = __get_request(&q->rq, rw_flags, bio, gfp_mask); if (rq) return rq; @@ -1229,8 +1235,8 @@ void __blk_put_request(struct request_queue *q, struct request *req) BUG_ON(!list_empty(&req->queuelist)); BUG_ON(!hlist_unhashed(&req->hash)); - blk_free_request(q, req); - freed_request(q, flags); + blk_free_request(&q->rq, req); + freed_request(&q->rq, flags); } } EXPORT_SYMBOL_GPL(__blk_put_request); diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index aa41b47c22d2..234ce7c082fa 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -66,16 +66,16 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count) blk_clear_queue_congested(q, BLK_RW_ASYNC); if (rl->count[BLK_RW_SYNC] >= q->nr_requests) { - blk_set_queue_full(q, BLK_RW_SYNC); + blk_set_rl_full(rl, BLK_RW_SYNC); } else { - blk_clear_queue_full(q, BLK_RW_SYNC); + blk_clear_rl_full(rl, BLK_RW_SYNC); wake_up(&rl->wait[BLK_RW_SYNC]); } if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) { - blk_set_queue_full(q, BLK_RW_ASYNC); + blk_set_rl_full(rl, BLK_RW_ASYNC); } else { - blk_clear_queue_full(q, BLK_RW_ASYNC); + blk_clear_rl_full(rl, BLK_RW_ASYNC); wake_up(&rl->wait[BLK_RW_ASYNC]); } spin_unlock_irq(q->queue_lock); @@ -476,7 +476,6 @@ static void blk_release_queue(struct kobject *kobj) { struct request_queue *q = container_of(kobj, struct request_queue, kobj); - struct request_list *rl = &q->rq; blk_sync_queue(q); @@ -489,8 +488,7 @@ static void blk_release_queue(struct kobject *kobj) elevator_exit(q->elevator); } - if (rl->rq_pool) - mempool_destroy(rl->rq_pool); + blk_exit_rl(&q->rq); if (q->queue_tags) __blk_queue_free_tags(q); diff --git a/block/blk.h b/block/blk.h index 85f6ae42f7d3..a134231fd22a 100644 --- a/block/blk.h +++ b/block/blk.h @@ -18,6 +18,9 @@ static inline void __blk_get_queue(struct request_queue *q) kobject_get(&q->kobj); } +int blk_init_rl(struct request_list *rl, struct request_queue *q, + gfp_t gfp_mask); +void blk_exit_rl(struct request_list *rl); void init_request_from_bio(struct request *req, struct bio *bio); void blk_rq_bio_prep(struct request_queue *q, struct request *rq, struct bio *bio); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 7e44ed93f84b..f2385ee7c7b2 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -46,7 +46,12 @@ struct blkcg_gq; struct request; typedef void (rq_end_io_fn)(struct request *, int); +#define BLK_RL_SYNCFULL (1U << 0) +#define BLK_RL_ASYNCFULL (1U << 1) + struct request_list { + struct request_queue *q; /* the queue this rl belongs to */ + /* * count[], starved[], and wait[] are indexed by * BLK_RW_SYNC/BLK_RW_ASYNC @@ -55,6 +60,7 @@ struct request_list { int starved[2]; mempool_t *rq_pool; wait_queue_head_t wait[2]; + unsigned int flags; }; /* @@ -562,27 +568,25 @@ static inline bool rq_is_sync(struct request *rq) return rw_is_sync(rq->cmd_flags); } -static inline int blk_queue_full(struct request_queue *q, int sync) +static inline bool blk_rl_full(struct request_list *rl, bool sync) { - if (sync) - return test_bit(QUEUE_FLAG_SYNCFULL, &q->queue_flags); - return test_bit(QUEUE_FLAG_ASYNCFULL, &q->queue_flags); + unsigned int flag = sync ? BLK_RL_SYNCFULL : BLK_RL_ASYNCFULL; + + return rl->flags & flag; } -static inline void blk_set_queue_full(struct request_queue *q, int sync) +static inline void blk_set_rl_full(struct request_list *rl, bool sync) { - if (sync) - queue_flag_set(QUEUE_FLAG_SYNCFULL, q); - else - queue_flag_set(QUEUE_FLAG_ASYNCFULL, q); + unsigned int flag = sync ? BLK_RL_SYNCFULL : BLK_RL_ASYNCFULL; + + rl->flags |= flag; } -static inline void blk_clear_queue_full(struct request_queue *q, int sync) +static inline void blk_clear_rl_full(struct request_list *rl, bool sync) { - if (sync) - queue_flag_clear(QUEUE_FLAG_SYNCFULL, q); - else - queue_flag_clear(QUEUE_FLAG_ASYNCFULL, q); + unsigned int flag = sync ? BLK_RL_SYNCFULL : BLK_RL_ASYNCFULL; + + rl->flags &= ~flag; } -- cgit v1.2.3 From 721002ec1dd55a52425455826af49cf8853b2d4f Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:02:45 +0530 Subject: usb: otg: utils: rename function name in OTG utils _transceiver() in otg.c is replaced with _phy. usb_set_transceiver is replaced with usb_add_phy to make it similar to other usb standard function names like usb_add_hcd. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/power/ab8500_charger.c | 6 +++--- drivers/power/isp1704_charger.c | 6 +++--- drivers/power/pda_power.c | 6 +++--- drivers/power/twl4030_charger.c | 6 +++--- drivers/usb/chipidea/udc.c | 8 ++++---- drivers/usb/gadget/fsl_udc_core.c | 2 +- drivers/usb/gadget/mv_udc_core.c | 2 +- drivers/usb/gadget/omap_udc.c | 6 +++--- drivers/usb/gadget/pxa25x_udc.c | 6 +++--- drivers/usb/gadget/pxa27x_udc.c | 4 ++-- drivers/usb/gadget/s3c-hsudc.c | 4 ++-- drivers/usb/host/ehci-fsl.c | 6 +++--- drivers/usb/host/ehci-msm.c | 6 +++--- drivers/usb/host/ehci-mv.c | 6 +++--- drivers/usb/host/ehci-tegra.c | 6 +++--- drivers/usb/host/ohci-omap.c | 6 +++--- drivers/usb/musb/am35x.c | 4 ++-- drivers/usb/musb/blackfin.c | 4 ++-- drivers/usb/musb/da8xx.c | 4 ++-- drivers/usb/musb/davinci.c | 6 +++--- drivers/usb/musb/musb_core.c | 2 +- drivers/usb/musb/musb_dsps.c | 6 +++--- drivers/usb/musb/omap2430.c | 4 ++-- drivers/usb/musb/tusb6010.c | 6 +++--- drivers/usb/musb/ux500.c | 4 ++-- drivers/usb/otg/ab8500-usb.c | 4 ++-- drivers/usb/otg/fsl_otg.c | 6 +++--- drivers/usb/otg/gpio_vbus.c | 4 ++-- drivers/usb/otg/isp1301_omap.c | 4 ++-- drivers/usb/otg/msm_otg.c | 6 +++--- drivers/usb/otg/mv_otg.c | 6 +++--- drivers/usb/otg/nop-usb-xceiv.c | 4 ++-- drivers/usb/otg/otg.c | 32 ++++++++++++++++---------------- drivers/usb/otg/twl4030-usb.c | 2 +- drivers/usb/otg/twl6030-usb.c | 2 +- include/linux/usb/otg.h | 10 +++++----- 36 files changed, 103 insertions(+), 103 deletions(-) (limited to 'include') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index d2303d0b7c75..cf5ffc4d1048 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -2517,7 +2517,7 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev) dev_err(di->dev, "%s mask and set failed\n", __func__); usb_unregister_notifier(di->usb_phy, &di->nb); - usb_put_transceiver(di->usb_phy); + usb_put_phy(di->usb_phy); /* Delete the work queue */ destroy_workqueue(di->charger_wq); @@ -2688,7 +2688,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) goto free_ac; } - di->usb_phy = usb_get_transceiver(); + di->usb_phy = usb_get_phy(); if (!di->usb_phy) { dev_err(di->dev, "failed to get usb transceiver\n"); ret = -EINVAL; @@ -2747,7 +2747,7 @@ free_irq: free_irq(irq, di); } put_usb_phy: - usb_put_transceiver(di->usb_phy); + usb_put_phy(di->usb_phy); free_usb: power_supply_unregister(&di->usb_chg.psy); free_ac: diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index e5ccd2979773..50773ae6f72e 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -415,7 +415,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) if (!isp) return -ENOMEM; - isp->phy = usb_get_transceiver(); + isp->phy = usb_get_phy(); if (!isp->phy) goto fail0; @@ -475,7 +475,7 @@ fail2: power_supply_unregister(&isp->psy); fail1: isp1704_charger_set_power(isp, 0); - usb_put_transceiver(isp->phy); + usb_put_phy(isp->phy); fail0: kfree(isp); @@ -490,7 +490,7 @@ static int __devexit isp1704_charger_remove(struct platform_device *pdev) usb_unregister_notifier(isp->phy, &isp->nb); power_supply_unregister(&isp->psy); - usb_put_transceiver(isp->phy); + usb_put_phy(isp->phy); isp1704_charger_set_power(isp, 0); kfree(isp); diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 214468f4444a..e0f206b0775b 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -321,7 +321,7 @@ static int pda_power_probe(struct platform_device *pdev) } #ifdef CONFIG_USB_OTG_UTILS - transceiver = usb_get_transceiver(); + transceiver = usb_get_phy(); if (transceiver && !pdata->is_usb_online) { pdata->is_usb_online = otg_is_usb_online; } @@ -409,7 +409,7 @@ usb_supply_failed: free_irq(ac_irq->start, &pda_psy_ac); #ifdef CONFIG_USB_OTG_UTILS if (transceiver) - usb_put_transceiver(transceiver); + usb_put_phy(transceiver); #endif ac_irq_failed: if (pdata->is_ac_online) @@ -444,7 +444,7 @@ static int pda_power_remove(struct platform_device *pdev) power_supply_unregister(&pda_psy_ac); #ifdef CONFIG_USB_OTG_UTILS if (transceiver) - usb_put_transceiver(transceiver); + usb_put_phy(transceiver); #endif if (ac_draw) { regulator_put(ac_draw); diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index fdad850c77d3..fcddd115cc08 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -479,7 +479,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) INIT_WORK(&bci->work, twl4030_bci_usb_work); - bci->transceiver = usb_get_transceiver(); + bci->transceiver = usb_get_phy(); if (bci->transceiver != NULL) { bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; usb_register_notifier(bci->transceiver, &bci->usb_nb); @@ -509,7 +509,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) fail_unmask_interrupts: if (bci->transceiver != NULL) { usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_transceiver(bci->transceiver); + usb_put_phy(bci->transceiver); } free_irq(bci->irq_bci, bci); fail_bci_irq: @@ -540,7 +540,7 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) if (bci->transceiver != NULL) { usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_transceiver(bci->transceiver); + usb_put_phy(bci->transceiver); } free_irq(bci->irq_bci, bci); free_irq(bci->irq_chg, bci); diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 51f96942dc5e..4468f2c2dddd 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1687,7 +1687,7 @@ static int udc_start(struct ci13xxx *udc) udc->gadget.ep0 = &udc->ep0in->ep; - udc->transceiver = usb_get_transceiver(); + udc->transceiver = usb_get_phy(); if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) { if (udc->transceiver == NULL) { @@ -1731,7 +1731,7 @@ static int udc_start(struct ci13xxx *udc) remove_trans: if (udc->transceiver) { otg_set_peripheral(udc->transceiver->otg, &udc->gadget); - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); } dev_err(dev, "error = %i\n", retval); @@ -1741,7 +1741,7 @@ unreg_device: device_unregister(&udc->gadget.dev); put_transceiver: if (udc->transceiver) - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); free_pools: dma_pool_destroy(udc->td_pool); free_qh_pool: @@ -1774,7 +1774,7 @@ static void udc_stop(struct ci13xxx *udc) if (udc->transceiver) { otg_set_peripheral(udc->transceiver->otg, NULL); - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); } dbg_remove_files(&udc->gadget.dev); device_unregister(&udc->gadget.dev); diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 28316858208b..d7038509b956 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2455,7 +2455,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG if (pdata->operating_mode == FSL_USB2_DR_OTG) { - udc_controller->transceiver = usb_get_transceiver(); + udc_controller->transceiver = usb_get_phy(); if (!udc_controller->transceiver) { ERR("Can't find OTG driver!\n"); ret = -ENODEV; diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index dbcd1329495e..5d779955d5a6 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2180,7 +2180,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->mode == MV_USB_MODE_OTG) - udc->transceiver = usb_get_transceiver(); + udc->transceiver = usb_get_phy(); #endif udc->clknum = pdata->clknum; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 7ba32469c5bd..74b9bb8099e7 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2865,7 +2865,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) * use it. Except for OTG, we don't _need_ to talk to one; * but not having one probably means no VBUS detection. */ - xceiv = usb_get_transceiver(); + xceiv = usb_get_phy(); if (xceiv) type = xceiv->label; else if (config->otg) { @@ -3011,7 +3011,7 @@ cleanup1: cleanup0: if (xceiv) - usb_put_transceiver(xceiv); + usb_put_phy(xceiv); if (cpu_is_omap16xx() || cpu_is_omap24xx() || cpu_is_omap7xx()) { clk_disable(hhc_clk); @@ -3041,7 +3041,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev) pullup_disable(udc); if (udc->transceiver) { - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); udc->transceiver = NULL; } omap_writew(0, UDC_SYSCON1); diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index d7c8cb3bf759..a658e446caba 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2159,7 +2159,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; - dev->transceiver = usb_get_transceiver(); + dev->transceiver = usb_get_phy(); if (gpio_is_valid(dev->mach->gpio_pullup)) { if ((retval = gpio_request(dev->mach->gpio_pullup, @@ -2238,7 +2238,7 @@ lubbock_fail0: gpio_free(dev->mach->gpio_pullup); err_gpio_pullup: if (dev->transceiver) { - usb_put_transceiver(dev->transceiver); + usb_put_phy(dev->transceiver); dev->transceiver = NULL; } clk_put(dev->clk); @@ -2280,7 +2280,7 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev) clk_put(dev->clk); if (dev->transceiver) { - usb_put_transceiver(dev->transceiver); + usb_put_phy(dev->transceiver); dev->transceiver = NULL; } diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 98acb3ab9e17..b982304a49c1 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -2464,7 +2464,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev) udc->dev = &pdev->dev; udc->mach = pdev->dev.platform_data; - udc->transceiver = usb_get_transceiver(); + udc->transceiver = usb_get_phy(); gpio = udc->mach->gpio_pullup; if (gpio_is_valid(gpio)) { @@ -2543,7 +2543,7 @@ static int __exit pxa_udc_remove(struct platform_device *_dev) if (gpio_is_valid(gpio)) gpio_free(gpio); - usb_put_transceiver(udc->transceiver); + usb_put_phy(udc->transceiver); udc->transceiver = NULL; platform_set_drvdata(_dev, NULL); diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 36c6836eeb0f..9ad33395f564 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1282,7 +1282,7 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev) hsudc->dev = dev; hsudc->pd = pdev->dev.platform_data; - hsudc->transceiver = usb_get_transceiver(); + hsudc->transceiver = usb_get_phy(); for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++) hsudc->supplies[i].supply = s3c_hsudc_supply_names[i]; @@ -1386,7 +1386,7 @@ err_remap: release_mem_region(res->start, resource_size(res)); err_res: if (hsudc->transceiver) - usb_put_transceiver(hsudc->transceiver); + usb_put_phy(hsudc->transceiver); regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies); err_supplies: diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 43362577b54a..0e8976a0ed51 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -142,7 +142,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, if (pdata->operating_mode == FSL_USB2_DR_OTG) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - ehci->transceiver = usb_get_transceiver(); + ehci->transceiver = usb_get_phy(); dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", hcd, ehci, ehci->transceiver); @@ -150,7 +150,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, retval = otg_set_host(ehci->transceiver->otg, &ehci_to_hcd(ehci)->self); if (retval) { - usb_put_transceiver(ehci->transceiver); + usb_put_phy(ehci->transceiver); goto err4; } } else { @@ -194,7 +194,7 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, if (ehci->transceiver) { otg_set_host(ehci->transceiver->otg, NULL); - usb_put_transceiver(ehci->transceiver); + usb_put_phy(ehci->transceiver); } usb_remove_hcd(hcd); diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 9803a55fd5f4..7badd5db398c 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -145,7 +145,7 @@ static int ehci_msm_probe(struct platform_device *pdev) * powering up VBUS, mapping of registers address space and power * management. */ - phy = usb_get_transceiver(); + phy = usb_get_phy(); if (!phy) { dev_err(&pdev->dev, "unable to find transceiver\n"); ret = -ENODEV; @@ -169,7 +169,7 @@ static int ehci_msm_probe(struct platform_device *pdev) return 0; put_transceiver: - usb_put_transceiver(phy); + usb_put_phy(phy); unmap: iounmap(hcd->regs); put_hcd: @@ -187,7 +187,7 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev) pm_runtime_set_suspended(&pdev->dev); otg_set_host(phy->otg, NULL); - usb_put_transceiver(phy); + usb_put_phy(phy); usb_put_hcd(hcd); diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index a936bbcff8f4..24f838fe25ac 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -253,7 +253,7 @@ static int mv_ehci_probe(struct platform_device *pdev) ehci_mv->mode = pdata->mode; if (ehci_mv->mode == MV_USB_MODE_OTG) { #ifdef CONFIG_USB_OTG_UTILS - ehci_mv->otg = usb_get_transceiver(); + ehci_mv->otg = usb_get_phy(); if (!ehci_mv->otg) { dev_err(&pdev->dev, "unable to find transceiver\n"); @@ -303,7 +303,7 @@ err_set_vbus: #ifdef CONFIG_USB_OTG_UTILS err_put_transceiver: if (ehci_mv->otg) - usb_put_transceiver(ehci_mv->otg); + usb_put_phy(ehci_mv->otg); #endif err_disable_clk: mv_ehci_disable(ehci_mv); @@ -333,7 +333,7 @@ static int mv_ehci_remove(struct platform_device *pdev) if (ehci_mv->otg) { otg_set_host(ehci_mv->otg->otg, NULL); - usb_put_transceiver(ehci_mv->otg); + usb_put_phy(ehci_mv->otg); } if (ehci_mv->mode == MV_USB_MODE_HOST) { diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 68548236ec42..ee17d19b1b82 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -749,7 +749,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->operating_mode == TEGRA_USB_OTG) { - tegra->transceiver = usb_get_transceiver(); + tegra->transceiver = usb_get_phy(); if (tegra->transceiver) otg_set_host(tegra->transceiver->otg, &hcd->self); } @@ -775,7 +775,7 @@ fail: #ifdef CONFIG_USB_OTG_UTILS if (tegra->transceiver) { otg_set_host(tegra->transceiver->otg, NULL); - usb_put_transceiver(tegra->transceiver); + usb_put_phy(tegra->transceiver); } #endif tegra_usb_phy_close(tegra->phy); @@ -810,7 +810,7 @@ static int tegra_ehci_remove(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS if (tegra->transceiver) { otg_set_host(tegra->transceiver->otg, NULL); - usb_put_transceiver(tegra->transceiver); + usb_put_phy(tegra->transceiver); } #endif diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 9ce35d0d9d5d..c2c1f55889a4 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -211,14 +211,14 @@ static int ohci_omap_init(struct usb_hcd *hcd) #ifdef CONFIG_USB_OTG if (need_transceiver) { - ohci->transceiver = usb_get_transceiver(); + ohci->transceiver = usb_get_phy(); if (ohci->transceiver) { int status = otg_set_host(ohci->transceiver->otg, &ohci_to_hcd(ohci)->self); dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n", ohci->transceiver->label, status); if (status) { - usb_put_transceiver(ohci->transceiver); + usb_put_phy(ohci->transceiver); return status; } } else { @@ -405,7 +405,7 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) usb_remove_hcd(hcd); if (ohci->transceiver) { (void) otg_set_host(ohci->transceiver->otg, 0); - usb_put_transceiver(ohci->transceiver); + usb_put_phy(ohci->transceiver); } if (machine_is_omap_osk()) gpio_free(9); diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 9f3eda91ea4d..a75989bbb3d4 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -364,7 +364,7 @@ static int am35x_musb_init(struct musb *musb) return -ENODEV; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) return -ENODEV; @@ -406,7 +406,7 @@ static int am35x_musb_exit(struct musb *musb) if (data->set_phy_power) data->set_phy_power(0); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index a087ed6c3be9..522a4a263df8 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -415,7 +415,7 @@ static int bfin_musb_init(struct musb *musb) gpio_direction_output(musb->config->gpio_vrsel, 0); usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) { gpio_free(musb->config->gpio_vrsel); return -ENODEV; @@ -440,7 +440,7 @@ static int bfin_musb_exit(struct musb *musb) { gpio_free(musb->config->gpio_vrsel); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; } diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 8bd9566f3fbb..61868d604b28 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -425,7 +425,7 @@ static int da8xx_musb_init(struct musb *musb) goto fail; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) goto fail; @@ -458,7 +458,7 @@ static int da8xx_musb_exit(struct musb *musb) phy_off(); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 768b4b55c816..441f776366f3 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -384,7 +384,7 @@ static int davinci_musb_init(struct musb *musb) u32 revision; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) goto unregister; @@ -443,7 +443,7 @@ static int davinci_musb_init(struct musb *musb) return 0; fail: - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); unregister: usb_nop_xceiv_unregister(); return -ENODEV; @@ -493,7 +493,7 @@ static int davinci_musb_exit(struct musb *musb) phy_off(); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index db3dff854b71..26f1befb4896 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1909,7 +1909,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) /* The musb_platform_init() call: * - adjusts musb->mregs and musb->isr if needed, * - may initialize an integrated tranceiver - * - initializes musb->xceiv, usually by otg_get_transceiver() + * - initializes musb->xceiv, usually by otg_get_phy() * - stops powering VBUS * * There are various transceiver configurations. Blackfin, diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 23db42db761a..716c113608f4 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -376,7 +376,7 @@ static int dsps_musb_init(struct musb *musb) /* NOP driver needs change if supporting dual instance */ usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) return -ENODEV; @@ -409,7 +409,7 @@ static int dsps_musb_init(struct musb *musb) return 0; err0: - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return status; } @@ -430,7 +430,7 @@ static int dsps_musb_exit(struct musb *musb) data->set_phy_power(0); /* NOP driver needs change if supporting dual instance */ - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index c7785e81254c..e16dbbf7f305 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -292,7 +292,7 @@ static int omap2430_musb_init(struct musb *musb) * up through ULPI. TWL4030-family PMICs include one, * which needs a driver, drivers aren't always needed. */ - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; @@ -391,7 +391,7 @@ static int omap2430_musb_exit(struct musb *musb) cancel_work_sync(&musb->otg_notifier_work); omap2430_low_level_exit(musb); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); return 0; } diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index de1355946a83..a004736186f1 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1078,7 +1078,7 @@ static int tusb_musb_init(struct musb *musb) int ret; usb_nop_xceiv_register(); - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) return -ENODEV; @@ -1130,7 +1130,7 @@ done: if (sync) iounmap(sync); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); } return ret; @@ -1146,7 +1146,7 @@ static int tusb_musb_exit(struct musb *musb) iounmap(musb->sync_va); - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); usb_nop_xceiv_unregister(); return 0; } diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index aa09dd417b94..53006b113b12 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -37,7 +37,7 @@ struct ux500_glue { static int ux500_musb_init(struct musb *musb) { - musb->xceiv = usb_get_transceiver(); + musb->xceiv = usb_get_phy(); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; @@ -48,7 +48,7 @@ static int ux500_musb_init(struct musb *musb) static int ux500_musb_exit(struct musb *musb) { - usb_put_transceiver(musb->xceiv); + usb_put_phy(musb->xceiv); return 0; } diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index a84af677dc59..672e28c81437 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -529,7 +529,7 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) if (err < 0) goto fail0; - err = usb_set_transceiver(&ab->phy); + err = usb_add_phy(&ab->phy); if (err) { dev_err(&pdev->dev, "Can't register transceiver\n"); goto fail1; @@ -556,7 +556,7 @@ static int __devexit ab8500_usb_remove(struct platform_device *pdev) cancel_work_sync(&ab->phy_dis_work); - usb_set_transceiver(NULL); + usb_add_phy(NULL); ab8500_usb_host_phy_dis(ab); ab8500_usb_peri_phy_dis(ab); diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c index be4a63e8302f..73561edd81de 100644 --- a/drivers/usb/otg/fsl_otg.c +++ b/drivers/usb/otg/fsl_otg.c @@ -806,7 +806,7 @@ static int fsl_otg_conf(struct platform_device *pdev) fsl_otg_dev = fsl_otg_tc; /* Store the otg transceiver */ - status = usb_set_transceiver(&fsl_otg_tc->phy); + status = usb_add_phy(&fsl_otg_tc->phy); if (status) { pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); goto err; @@ -824,7 +824,7 @@ err: int usb_otg_start(struct platform_device *pdev) { struct fsl_otg *p_otg; - struct usb_phy *otg_trans = usb_get_transceiver(); + struct usb_phy *otg_trans = usb_get_phy(); struct otg_fsm *fsm; int status; struct resource *res; @@ -1134,7 +1134,7 @@ static int __devexit fsl_otg_remove(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - usb_set_transceiver(NULL); + usb_add_phy(NULL); free_irq(fsl_otg_dev->irq, fsl_otg_dev); iounmap((void *)usb_dr_regs); diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c index bde6298a9693..9b3c264cdb56 100644 --- a/drivers/usb/otg/gpio_vbus.c +++ b/drivers/usb/otg/gpio_vbus.c @@ -320,7 +320,7 @@ static int __init gpio_vbus_probe(struct platform_device *pdev) } /* only active when a gadget is registered */ - err = usb_set_transceiver(&gpio_vbus->phy); + err = usb_add_phy(&gpio_vbus->phy); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -354,7 +354,7 @@ static int __exit gpio_vbus_remove(struct platform_device *pdev) cancel_delayed_work_sync(&gpio_vbus->work); regulator_put(gpio_vbus->vbus_draw); - usb_set_transceiver(NULL); + usb_add_phy(NULL); free_irq(gpio_vbus->irq, pdev); if (gpio_is_valid(pdata->gpio_pullup)) diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index 33cd709b084e..b74df3fec56f 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -1611,7 +1611,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES); #endif - status = usb_set_transceiver(&isp->phy); + status = usb_add_phy(&isp->phy); if (status < 0) dev_err(&i2c->dev, "can't register transceiver, %d\n", status); @@ -1650,7 +1650,7 @@ subsys_initcall(isp_init); static void __exit isp_exit(void) { if (the_transceiver) - usb_set_transceiver(NULL); + usb_add_phy(NULL); i2c_del_driver(&isp1301_driver); } module_exit(isp_exit); diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 1d0347c247d1..dd606c010035 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -1555,9 +1555,9 @@ static int __init msm_otg_probe(struct platform_device *pdev) phy->otg->set_host = msm_otg_set_host; phy->otg->set_peripheral = msm_otg_set_peripheral; - ret = usb_set_transceiver(&motg->phy); + ret = usb_add_phy(&motg->phy); if (ret) { - dev_err(&pdev->dev, "usb_set_transceiver failed\n"); + dev_err(&pdev->dev, "usb_add_phy failed\n"); goto free_irq; } @@ -1624,7 +1624,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); pm_runtime_disable(&pdev->dev); - usb_set_transceiver(NULL); + usb_add_phy(NULL); free_irq(motg->irq, motg); /* diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c index 6cc6c3ffbb83..18e90fe1fbd1 100644 --- a/drivers/usb/otg/mv_otg.c +++ b/drivers/usb/otg/mv_otg.c @@ -690,7 +690,7 @@ int mv_otg_remove(struct platform_device *pdev) for (clk_i = 0; clk_i <= mvotg->clknum; clk_i++) clk_put(mvotg->clk[clk_i]); - usb_set_transceiver(NULL); + usb_add_phy(NULL); platform_set_drvdata(pdev, NULL); kfree(mvotg->phy.otg); @@ -853,7 +853,7 @@ static int mv_otg_probe(struct platform_device *pdev) goto err_disable_clk; } - retval = usb_set_transceiver(&mvotg->phy); + retval = usb_add_phy(&mvotg->phy); if (retval < 0) { dev_err(&pdev->dev, "can't register transceiver, %d\n", retval); @@ -880,7 +880,7 @@ static int mv_otg_probe(struct platform_device *pdev) return 0; err_set_transceiver: - usb_set_transceiver(NULL); + usb_add_phy(NULL); err_free_irq: free_irq(mvotg->irq, mvotg); err_disable_clk: diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 58b26df6afd1..33000dae7200 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -117,7 +117,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev) nop->phy.otg->set_host = nop_set_host; nop->phy.otg->set_peripheral = nop_set_peripheral; - err = usb_set_transceiver(&nop->phy); + err = usb_add_phy(&nop->phy); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -139,7 +139,7 @@ static int __devexit nop_usb_xceiv_remove(struct platform_device *pdev) { struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); - usb_set_transceiver(NULL); + usb_add_phy(NULL); platform_set_drvdata(pdev, NULL); kfree(nop->phy.otg); diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 801e597a1541..300a995cfdbe 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -18,53 +18,53 @@ static struct usb_phy *phy; /** - * usb_get_transceiver - find the (single) USB transceiver + * usb_get_phy - find the (single) USB PHY * - * Returns the transceiver driver, after getting a refcount to it; or - * null if there is no such transceiver. The caller is responsible for - * calling usb_put_transceiver() to release that count. + * Returns the phy driver, after getting a refcount to it; or + * null if there is no such phy. The caller is responsible for + * calling usb_put_phy() to release that count. * * For use by USB host and peripheral drivers. */ -struct usb_phy *usb_get_transceiver(void) +struct usb_phy *usb_get_phy(void) { if (phy) get_device(phy->dev); return phy; } -EXPORT_SYMBOL(usb_get_transceiver); +EXPORT_SYMBOL(usb_get_phy); /** - * usb_put_transceiver - release the (single) USB transceiver - * @x: the transceiver returned by usb_get_transceiver() + * usb_put_phy - release the (single) USB PHY + * @x: the phy returned by usb_get_phy() * - * Releases a refcount the caller received from usb_get_transceiver(). + * Releases a refcount the caller received from usb_get_phy(). * * For use by USB host and peripheral drivers. */ -void usb_put_transceiver(struct usb_phy *x) +void usb_put_phy(struct usb_phy *x) { if (x) put_device(x->dev); } -EXPORT_SYMBOL(usb_put_transceiver); +EXPORT_SYMBOL(usb_put_phy); /** - * usb_set_transceiver - declare the (single) USB transceiver - * @x: the USB transceiver to be used; or NULL + * usb_add_phy - declare the (single) USB PHY + * @x: the USB phy to be used; or NULL * - * This call is exclusively for use by transceiver drivers, which + * This call is exclusively for use by phy drivers, which * coordinate the activities of drivers for host and peripheral * controllers, and in some cases for VBUS current regulation. */ -int usb_set_transceiver(struct usb_phy *x) +int usb_add_phy(struct usb_phy *x) { if (phy && x) return -EBUSY; phy = x; return 0; } -EXPORT_SYMBOL(usb_set_transceiver); +EXPORT_SYMBOL(usb_add_phy); const char *otg_state_string(enum usb_otg_state state) { diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 02979306bf83..01022c891e26 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -633,7 +633,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_set_transceiver(&twl->phy); + usb_add_phy(&twl->phy); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index d2a9a8e691b9..a8be20878eb9 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -443,7 +443,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_set_transceiver(&twl->phy); + usb_add_phy(&twl->phy); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 38ab3f46346f..0e739c810525 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -121,7 +121,7 @@ struct usb_phy { /* for board-specific init logic */ -extern int usb_set_transceiver(struct usb_phy *); +extern int usb_add_phy(struct usb_phy *); #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) /* sometimes transceivers are accessed only through e.g. ULPI */ @@ -172,16 +172,16 @@ usb_phy_shutdown(struct usb_phy *x) /* for usb host and peripheral controller drivers */ #ifdef CONFIG_USB_OTG_UTILS -extern struct usb_phy *usb_get_transceiver(void); -extern void usb_put_transceiver(struct usb_phy *); +extern struct usb_phy *usb_get_phy(void); +extern void usb_put_phy(struct usb_phy *); extern const char *otg_state_string(enum usb_otg_state state); #else -static inline struct usb_phy *usb_get_transceiver(void) +static inline struct usb_phy *usb_get_phy(void) { return NULL; } -static inline void usb_put_transceiver(struct usb_phy *x) +static inline void usb_put_phy(struct usb_phy *x) { } -- cgit v1.2.3 From 662dca54ca67c92b7aa14b9a2ec54acacf33ce45 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:02:46 +0530 Subject: usb: otg: support for multiple transceivers by a single controller Add a linked list for keeping multiple PHY instances with different types so that we can have separate USB2 and USB3 PHYs on one single board. _get_phy_ has been changed so that the controller gets the transceiver by type. _remove_phy_ has been added to let the phy be removed from the phy list. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/power/ab8500_charger.c | 2 +- drivers/power/isp1704_charger.c | 2 +- drivers/power/pda_power.c | 2 +- drivers/power/twl4030_charger.c | 2 +- drivers/usb/chipidea/udc.c | 2 +- drivers/usb/gadget/fsl_udc_core.c | 2 +- drivers/usb/gadget/mv_udc_core.c | 2 +- drivers/usb/gadget/omap_udc.c | 2 +- drivers/usb/gadget/pxa25x_udc.c | 2 +- drivers/usb/gadget/pxa27x_udc.c | 2 +- drivers/usb/gadget/s3c-hsudc.c | 2 +- drivers/usb/host/ehci-fsl.c | 2 +- drivers/usb/host/ehci-msm.c | 2 +- drivers/usb/host/ehci-mv.c | 2 +- drivers/usb/host/ehci-tegra.c | 2 +- drivers/usb/host/ohci-omap.c | 2 +- drivers/usb/musb/am35x.c | 2 +- drivers/usb/musb/blackfin.c | 2 +- drivers/usb/musb/da8xx.c | 2 +- drivers/usb/musb/davinci.c | 2 +- drivers/usb/musb/musb_dsps.c | 2 +- drivers/usb/musb/omap2430.c | 2 +- drivers/usb/musb/tusb6010.c | 2 +- drivers/usb/musb/ux500.c | 2 +- drivers/usb/otg/ab8500-usb.c | 4 +- drivers/usb/otg/fsl_otg.c | 6 +-- drivers/usb/otg/gpio_vbus.c | 4 +- drivers/usb/otg/isp1301_omap.c | 4 +- drivers/usb/otg/msm_otg.c | 4 +- drivers/usb/otg/mv_otg.c | 6 +-- drivers/usb/otg/nop-usb-xceiv.c | 4 +- drivers/usb/otg/otg.c | 96 ++++++++++++++++++++++++++++++++++----- drivers/usb/otg/twl4030-usb.c | 2 +- drivers/usb/otg/twl6030-usb.c | 2 +- include/linux/usb/otg.h | 29 ++++++++++-- 35 files changed, 152 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index cf5ffc4d1048..6bd6f1c41967 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -2688,7 +2688,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) goto free_ac; } - di->usb_phy = usb_get_phy(); + di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); if (!di->usb_phy) { dev_err(di->dev, "failed to get usb transceiver\n"); ret = -EINVAL; diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index 50773ae6f72e..090e5f9e72c9 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -415,7 +415,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) if (!isp) return -ENOMEM; - isp->phy = usb_get_phy(); + isp->phy = usb_get_phy(USB_PHY_TYPE_USB2); if (!isp->phy) goto fail0; diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index e0f206b0775b..7602d49e4d81 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -321,7 +321,7 @@ static int pda_power_probe(struct platform_device *pdev) } #ifdef CONFIG_USB_OTG_UTILS - transceiver = usb_get_phy(); + transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (transceiver && !pdata->is_usb_online) { pdata->is_usb_online = otg_is_usb_online; } diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index fcddd115cc08..13f9db2e8538 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -479,7 +479,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) INIT_WORK(&bci->work, twl4030_bci_usb_work); - bci->transceiver = usb_get_phy(); + bci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (bci->transceiver != NULL) { bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; usb_register_notifier(bci->transceiver, &bci->usb_nb); diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 4468f2c2dddd..a06d28b119f5 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1687,7 +1687,7 @@ static int udc_start(struct ci13xxx *udc) udc->gadget.ep0 = &udc->ep0in->ep; - udc->transceiver = usb_get_phy(); + udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) { if (udc->transceiver == NULL) { diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index d7038509b956..0808820ba495 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2455,7 +2455,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG if (pdata->operating_mode == FSL_USB2_DR_OTG) { - udc_controller->transceiver = usb_get_phy(); + udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (!udc_controller->transceiver) { ERR("Can't find OTG driver!\n"); ret = -ENODEV; diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 5d779955d5a6..75ff41a5c959 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2180,7 +2180,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->mode == MV_USB_MODE_OTG) - udc->transceiver = usb_get_phy(); + udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); #endif udc->clknum = pdata->clknum; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 74b9bb8099e7..cf8bf26f12ed 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2865,7 +2865,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) * use it. Except for OTG, we don't _need_ to talk to one; * but not having one probably means no VBUS detection. */ - xceiv = usb_get_phy(); + xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (xceiv) type = xceiv->label; else if (config->otg) { diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index a658e446caba..cc0b1e63dcab 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2159,7 +2159,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; - dev->transceiver = usb_get_phy(); + dev->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (gpio_is_valid(dev->mach->gpio_pullup)) { if ((retval = gpio_request(dev->mach->gpio_pullup, diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index b982304a49c1..8f744aab9628 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -2464,7 +2464,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev) udc->dev = &pdev->dev; udc->mach = pdev->dev.platform_data; - udc->transceiver = usb_get_phy(); + udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); gpio = udc->mach->gpio_pullup; if (gpio_is_valid(gpio)) { diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 9ad33395f564..22326f274466 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1282,7 +1282,7 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev) hsudc->dev = dev; hsudc->pd = pdev->dev.platform_data; - hsudc->transceiver = usb_get_phy(); + hsudc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); for (i = 0; i < ARRAY_SIZE(hsudc->supplies); i++) hsudc->supplies[i].supply = s3c_hsudc_supply_names[i]; diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 0e8976a0ed51..ba290589d858 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -142,7 +142,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, if (pdata->operating_mode == FSL_USB2_DR_OTG) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - ehci->transceiver = usb_get_phy(); + ehci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", hcd, ehci, ehci->transceiver); diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 7badd5db398c..c7615fb93dbb 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -145,7 +145,7 @@ static int ehci_msm_probe(struct platform_device *pdev) * powering up VBUS, mapping of registers address space and power * management. */ - phy = usb_get_phy(); + phy = usb_get_phy(USB_PHY_TYPE_USB2); if (!phy) { dev_err(&pdev->dev, "unable to find transceiver\n"); ret = -ENODEV; diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 24f838fe25ac..ef7aa0df40a6 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -253,7 +253,7 @@ static int mv_ehci_probe(struct platform_device *pdev) ehci_mv->mode = pdata->mode; if (ehci_mv->mode == MV_USB_MODE_OTG) { #ifdef CONFIG_USB_OTG_UTILS - ehci_mv->otg = usb_get_phy(); + ehci_mv->otg = usb_get_phy(USB_PHY_TYPE_USB2); if (!ehci_mv->otg) { dev_err(&pdev->dev, "unable to find transceiver\n"); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index ee17d19b1b82..14df2f5cf6ae 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -749,7 +749,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) #ifdef CONFIG_USB_OTG_UTILS if (pdata->operating_mode == TEGRA_USB_OTG) { - tegra->transceiver = usb_get_phy(); + tegra->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (tegra->transceiver) otg_set_host(tegra->transceiver->otg, &hcd->self); } diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index c2c1f55889a4..92a77dfd1930 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -211,7 +211,7 @@ static int ohci_omap_init(struct usb_hcd *hcd) #ifdef CONFIG_USB_OTG if (need_transceiver) { - ohci->transceiver = usb_get_phy(); + ohci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (ohci->transceiver) { int status = otg_set_host(ohci->transceiver->otg, &ohci_to_hcd(ohci)->self); diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index a75989bbb3d4..4a8cbf0e8d51 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -364,7 +364,7 @@ static int am35x_musb_init(struct musb *musb) return -ENODEV; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) return -ENODEV; diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 522a4a263df8..452940986d6d 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -415,7 +415,7 @@ static int bfin_musb_init(struct musb *musb) gpio_direction_output(musb->config->gpio_vrsel, 0); usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) { gpio_free(musb->config->gpio_vrsel); return -ENODEV; diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 61868d604b28..d731c80c4fef 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -425,7 +425,7 @@ static int da8xx_musb_init(struct musb *musb) goto fail; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) goto fail; diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 441f776366f3..582268de3fa2 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -384,7 +384,7 @@ static int davinci_musb_init(struct musb *musb) u32 revision; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) goto unregister; diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 716c113608f4..92603e498e61 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -376,7 +376,7 @@ static int dsps_musb_init(struct musb *musb) /* NOP driver needs change if supporting dual instance */ usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) return -ENODEV; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index e16dbbf7f305..e279cf32772e 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -292,7 +292,7 @@ static int omap2430_musb_init(struct musb *musb) * up through ULPI. TWL4030-family PMICs include one, * which needs a driver, drivers aren't always needed. */ - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index a004736186f1..8ddf3d5f7cdc 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1078,7 +1078,7 @@ static int tusb_musb_init(struct musb *musb) int ret; usb_nop_xceiv_register(); - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) return -ENODEV; diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 53006b113b12..46cf80a8cacd 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -37,7 +37,7 @@ struct ux500_glue { static int ux500_musb_init(struct musb *musb) { - musb->xceiv = usb_get_phy(); + musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index 672e28c81437..ae8ad561f083 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -529,7 +529,7 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) if (err < 0) goto fail0; - err = usb_add_phy(&ab->phy); + err = usb_add_phy(&ab->phy, USB_PHY_TYPE_USB2); if (err) { dev_err(&pdev->dev, "Can't register transceiver\n"); goto fail1; @@ -556,7 +556,7 @@ static int __devexit ab8500_usb_remove(struct platform_device *pdev) cancel_work_sync(&ab->phy_dis_work); - usb_add_phy(NULL); + usb_remove_phy(&ab->phy); ab8500_usb_host_phy_dis(ab); ab8500_usb_peri_phy_dis(ab); diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c index 73561edd81de..23c798cb2d7f 100644 --- a/drivers/usb/otg/fsl_otg.c +++ b/drivers/usb/otg/fsl_otg.c @@ -806,7 +806,7 @@ static int fsl_otg_conf(struct platform_device *pdev) fsl_otg_dev = fsl_otg_tc; /* Store the otg transceiver */ - status = usb_add_phy(&fsl_otg_tc->phy); + status = usb_add_phy(&fsl_otg_tc->phy, USB_PHY_TYPE_USB2); if (status) { pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); goto err; @@ -824,7 +824,7 @@ err: int usb_otg_start(struct platform_device *pdev) { struct fsl_otg *p_otg; - struct usb_phy *otg_trans = usb_get_phy(); + struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2); struct otg_fsm *fsm; int status; struct resource *res; @@ -1134,7 +1134,7 @@ static int __devexit fsl_otg_remove(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; - usb_add_phy(NULL); + usb_remove_phy(&fsl_otg_dev->phy); free_irq(fsl_otg_dev->irq, fsl_otg_dev); iounmap((void *)usb_dr_regs); diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c index 9b3c264cdb56..a67ffe22179a 100644 --- a/drivers/usb/otg/gpio_vbus.c +++ b/drivers/usb/otg/gpio_vbus.c @@ -320,7 +320,7 @@ static int __init gpio_vbus_probe(struct platform_device *pdev) } /* only active when a gadget is registered */ - err = usb_add_phy(&gpio_vbus->phy); + err = usb_add_phy(&gpio_vbus->phy, USB_PHY_TYPE_USB2); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -354,7 +354,7 @@ static int __exit gpio_vbus_remove(struct platform_device *pdev) cancel_delayed_work_sync(&gpio_vbus->work); regulator_put(gpio_vbus->vbus_draw); - usb_add_phy(NULL); + usb_remove_phy(&gpio_vbus->phy); free_irq(gpio_vbus->irq, pdev); if (gpio_is_valid(pdata->gpio_pullup)) diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index b74df3fec56f..75cea4ab0985 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -1611,7 +1611,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id) dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES); #endif - status = usb_add_phy(&isp->phy); + status = usb_add_phy(&isp->phy, USB_PHY_TYPE_USB2); if (status < 0) dev_err(&i2c->dev, "can't register transceiver, %d\n", status); @@ -1650,7 +1650,7 @@ subsys_initcall(isp_init); static void __exit isp_exit(void) { if (the_transceiver) - usb_add_phy(NULL); + usb_remove_phy(&the_transceiver->phy); i2c_del_driver(&isp1301_driver); } module_exit(isp_exit); diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index dd606c010035..9f5fc906041a 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -1555,7 +1555,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) phy->otg->set_host = msm_otg_set_host; phy->otg->set_peripheral = msm_otg_set_peripheral; - ret = usb_add_phy(&motg->phy); + ret = usb_add_phy(&motg->phy, USB_PHY_TYPE_USB2); if (ret) { dev_err(&pdev->dev, "usb_add_phy failed\n"); goto free_irq; @@ -1624,7 +1624,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); pm_runtime_disable(&pdev->dev); - usb_add_phy(NULL); + usb_remove_phy(phy); free_irq(motg->irq, motg); /* diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c index 18e90fe1fbd1..3f124e8f5792 100644 --- a/drivers/usb/otg/mv_otg.c +++ b/drivers/usb/otg/mv_otg.c @@ -690,7 +690,7 @@ int mv_otg_remove(struct platform_device *pdev) for (clk_i = 0; clk_i <= mvotg->clknum; clk_i++) clk_put(mvotg->clk[clk_i]); - usb_add_phy(NULL); + usb_remove_phy(&mvotg->phy); platform_set_drvdata(pdev, NULL); kfree(mvotg->phy.otg); @@ -853,7 +853,7 @@ static int mv_otg_probe(struct platform_device *pdev) goto err_disable_clk; } - retval = usb_add_phy(&mvotg->phy); + retval = usb_add_phy(&mvotg->phy, USB_PHY_TYPE_USB2); if (retval < 0) { dev_err(&pdev->dev, "can't register transceiver, %d\n", retval); @@ -880,7 +880,7 @@ static int mv_otg_probe(struct platform_device *pdev) return 0; err_set_transceiver: - usb_add_phy(NULL); + usb_remove_phy(&mvotg->phy); err_free_irq: free_irq(mvotg->irq, mvotg); err_disable_clk: diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 33000dae7200..803f958f4133 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -117,7 +117,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev) nop->phy.otg->set_host = nop_set_host; nop->phy.otg->set_peripheral = nop_set_peripheral; - err = usb_add_phy(&nop->phy); + err = usb_add_phy(&nop->phy, USB_PHY_TYPE_USB2); if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); @@ -139,7 +139,7 @@ static int __devexit nop_usb_xceiv_remove(struct platform_device *pdev) { struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); - usb_add_phy(NULL); + usb_remove_phy(&nop->phy); platform_set_drvdata(pdev, NULL); kfree(nop->phy.otg); diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 300a995cfdbe..a23065820ead 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -11,14 +11,32 @@ #include #include +#include #include #include -static struct usb_phy *phy; +static LIST_HEAD(phy_list); +static DEFINE_SPINLOCK(phy_lock); + +static struct usb_phy *__usb_find_phy(struct list_head *list, + enum usb_phy_type type) +{ + struct usb_phy *phy = NULL; + + list_for_each_entry(phy, list, head) { + if (phy->type != type) + continue; + + return phy; + } + + return ERR_PTR(-ENODEV); +} /** - * usb_get_phy - find the (single) USB PHY + * usb_get_phy - find the USB PHY + * @type - the type of the phy the controller requires * * Returns the phy driver, after getting a refcount to it; or * null if there is no such phy. The caller is responsible for @@ -26,16 +44,30 @@ static struct usb_phy *phy; * * For use by USB host and peripheral drivers. */ -struct usb_phy *usb_get_phy(void) +struct usb_phy *usb_get_phy(enum usb_phy_type type) { - if (phy) - get_device(phy->dev); + struct usb_phy *phy = NULL; + unsigned long flags; + + spin_lock_irqsave(&phy_lock, flags); + + phy = __usb_find_phy(&phy_list, type); + if (IS_ERR(phy)) { + pr_err("unable to find transceiver of type %s\n", + usb_phy_type_string(type)); + return phy; + } + + get_device(phy->dev); + + spin_unlock_irqrestore(&phy_lock, flags); + return phy; } EXPORT_SYMBOL(usb_get_phy); /** - * usb_put_phy - release the (single) USB PHY + * usb_put_phy - release the USB PHY * @x: the phy returned by usb_get_phy() * * Releases a refcount the caller received from usb_get_phy(). @@ -50,22 +82,62 @@ void usb_put_phy(struct usb_phy *x) EXPORT_SYMBOL(usb_put_phy); /** - * usb_add_phy - declare the (single) USB PHY + * usb_add_phy - declare the USB PHY * @x: the USB phy to be used; or NULL + * @type - the type of this PHY * * This call is exclusively for use by phy drivers, which * coordinate the activities of drivers for host and peripheral * controllers, and in some cases for VBUS current regulation. */ -int usb_add_phy(struct usb_phy *x) +int usb_add_phy(struct usb_phy *x, enum usb_phy_type type) { - if (phy && x) - return -EBUSY; - phy = x; - return 0; + int ret = 0; + unsigned long flags; + struct usb_phy *phy; + + if (x && x->type != USB_PHY_TYPE_UNDEFINED) { + dev_err(x->dev, "not accepting initialized PHY %s\n", x->label); + return -EINVAL; + } + + spin_lock_irqsave(&phy_lock, flags); + + list_for_each_entry(phy, &phy_list, head) { + if (phy->type == type) { + ret = -EBUSY; + dev_err(x->dev, "transceiver type %s already exists\n", + usb_phy_type_string(type)); + goto out; + } + } + + x->type = type; + list_add_tail(&x->head, &phy_list); + +out: + spin_unlock_irqrestore(&phy_lock, flags); + return ret; } EXPORT_SYMBOL(usb_add_phy); +/** + * usb_remove_phy - remove the OTG PHY + * @x: the USB OTG PHY to be removed; + * + * This reverts the effects of usb_add_phy + */ +void usb_remove_phy(struct usb_phy *x) +{ + unsigned long flags; + + spin_lock_irqsave(&phy_lock, flags); + if (x) + list_del(&x->head); + spin_unlock_irqrestore(&phy_lock, flags); +} +EXPORT_SYMBOL(usb_remove_phy); + const char *otg_state_string(enum usb_otg_state state) { switch (state) { diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 01022c891e26..25a09fabbe35 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -633,7 +633,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_add_phy(&twl->phy); + usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index a8be20878eb9..dbee00aea755 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -443,7 +443,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) kfree(twl); return err; } - usb_add_phy(&twl->phy); + usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 0e739c810525..1def65fb57d0 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -43,6 +43,13 @@ enum usb_phy_events { USB_EVENT_ENUMERATED, /* gadget driver enumerated */ }; +/* associate a type with PHY */ +enum usb_phy_type { + USB_PHY_TYPE_UNDEFINED, + USB_PHY_TYPE_USB2, + USB_PHY_TYPE_USB3, +}; + struct usb_phy; /* for transceivers connected thru an ULPI interface, the user must @@ -89,6 +96,7 @@ struct usb_phy { const char *label; unsigned int flags; + enum usb_phy_type type; enum usb_otg_state state; enum usb_phy_events last_event; @@ -105,6 +113,9 @@ struct usb_phy { u16 port_status; u16 port_change; + /* to support controllers that have multiple transceivers */ + struct list_head head; + /* initialize/shutdown the OTG controller */ int (*init)(struct usb_phy *x); void (*shutdown)(struct usb_phy *x); @@ -121,7 +132,8 @@ struct usb_phy { /* for board-specific init logic */ -extern int usb_add_phy(struct usb_phy *); +extern int usb_add_phy(struct usb_phy *, enum usb_phy_type type); +extern void usb_remove_phy(struct usb_phy *); #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) /* sometimes transceivers are accessed only through e.g. ULPI */ @@ -172,11 +184,11 @@ usb_phy_shutdown(struct usb_phy *x) /* for usb host and peripheral controller drivers */ #ifdef CONFIG_USB_OTG_UTILS -extern struct usb_phy *usb_get_phy(void); +extern struct usb_phy *usb_get_phy(enum usb_phy_type type); extern void usb_put_phy(struct usb_phy *); extern const char *otg_state_string(enum usb_otg_state state); #else -static inline struct usb_phy *usb_get_phy(void) +static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) { return NULL; } @@ -276,4 +288,15 @@ usb_unregister_notifier(struct usb_phy *x, struct notifier_block *nb) /* for OTG controller drivers (and maybe other stuff) */ extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num); +static inline const char *usb_phy_type_string(enum usb_phy_type type) +{ + switch (type) { + case USB_PHY_TYPE_USB2: + return "USB2 PHY"; + case USB_PHY_TYPE_USB3: + return "USB3 PHY"; + default: + return "UNKNOWN PHY TYPE"; + } +} #endif /* __LINUX_USB_OTG_H */ -- cgit v1.2.3 From 410219dcd2ba8d8b4bcfa9c232f35bf505bc021a Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:02:47 +0530 Subject: usb: otg: utils: devres: Add API's to associate a device with the phy Used devres API's to associate the phy with a device so that on driver detach, release function is invoked on the devres data(usb_phy) and devres data(usb_phy) is released. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/otg/otg.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/usb/otg.h | 13 +++++++++++ 2 files changed, 75 insertions(+) (limited to 'include') diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index a23065820ead..0fa4d8c1b1e8 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -34,6 +35,48 @@ static struct usb_phy *__usb_find_phy(struct list_head *list, return ERR_PTR(-ENODEV); } +static void devm_usb_phy_release(struct device *dev, void *res) +{ + struct usb_phy *phy = *(struct usb_phy **)res; + + usb_put_phy(phy); +} + +static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) +{ + return res == match_data; +} + +/** + * devm_usb_get_phy - find the USB PHY + * @dev - device that requests this phy + * @type - the type of the phy the controller requires + * + * Gets the phy using usb_get_phy(), and associates a device with it using + * devres. On driver detach, release function is invoked on the devres data, + * then, devres data is freed. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type) +{ + struct usb_phy **ptr, *phy; + + ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + phy = usb_get_phy(type); + if (phy) { + *ptr = phy; + devres_add(dev, ptr); + } else + devres_free(ptr); + + return phy; +} +EXPORT_SYMBOL(devm_usb_get_phy); + /** * usb_get_phy - find the USB PHY * @type - the type of the phy the controller requires @@ -66,6 +109,25 @@ struct usb_phy *usb_get_phy(enum usb_phy_type type) } EXPORT_SYMBOL(usb_get_phy); +/** + * devm_usb_put_phy - release the USB PHY + * @dev - device that wants to release this phy + * @phy - the phy returned by devm_usb_get_phy() + * + * destroys the devres associated with this phy and invokes usb_put_phy + * to release the phy. + * + * For use by USB host and peripheral drivers. + */ +void devm_usb_put_phy(struct device *dev, struct usb_phy *phy) +{ + int r; + + r = devres_destroy(dev, devm_usb_phy_release, devm_usb_phy_match, phy); + dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); +} +EXPORT_SYMBOL(devm_usb_put_phy); + /** * usb_put_phy - release the USB PHY * @x: the phy returned by usb_get_phy() diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 1def65fb57d0..0cb2ec2e50c0 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -185,7 +185,10 @@ usb_phy_shutdown(struct usb_phy *x) /* for usb host and peripheral controller drivers */ #ifdef CONFIG_USB_OTG_UTILS extern struct usb_phy *usb_get_phy(enum usb_phy_type type); +extern struct usb_phy *devm_usb_get_phy(struct device *dev, + enum usb_phy_type type); extern void usb_put_phy(struct usb_phy *); +extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x); extern const char *otg_state_string(enum usb_otg_state state); #else static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) @@ -193,10 +196,20 @@ static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) return NULL; } +static inline struct usb_phy *devm_usb_get_phy(struct device *dev, + enum usb_phy_type type) +{ + return NULL; +} + static inline void usb_put_phy(struct usb_phy *x) { } +static inline void devm_usb_put_phy(struct device *dev, struct usb_phy *x) +{ +} + static inline const char *otg_state_string(enum usb_otg_state state) { return NULL; -- cgit v1.2.3 From c9721438c009adf8e81d376839ed037c53b9b8d9 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 22 Jun 2012 17:40:52 +0530 Subject: usb: musb: twl: use mailbox API to send VBUS or ID events The atomic notifier from twl4030/twl6030 to notifiy VBUS and ID events, is replaced by a direct call to omap musb blue. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/musb/omap2430.c | 94 +++++++++++++++++++++++++++---------------- drivers/usb/otg/twl4030-usb.c | 46 ++++++++++----------- drivers/usb/otg/twl6030-usb.c | 47 ++++++++++------------ include/linux/usb/musb-omap.h | 30 ++++++++++++++ 4 files changed, 133 insertions(+), 84 deletions(-) create mode 100644 include/linux/usb/musb-omap.h (limited to 'include') diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index f40c8053a291..063687085d1e 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "musb_core.h" #include "omap2430.h" @@ -41,11 +42,13 @@ struct omap2430_glue { struct device *dev; struct platform_device *musb; - u8 xceiv_event; + enum omap_musb_vbus_id_status status; struct work_struct omap_musb_mailbox_work; }; #define glue_to_musb(g) platform_get_drvdata(g->musb) +struct omap2430_glue *_glue; + static struct timer_list musb_idle_timer; static void musb_do_idle(unsigned long _musb) @@ -225,54 +228,58 @@ static inline void omap2430_low_level_init(struct musb *musb) musb_writel(musb->mregs, OTG_FORCESTDBY, l); } -static int musb_otg_notifications(struct notifier_block *nb, - unsigned long event, void *unused) +void omap_musb_mailbox(enum omap_musb_vbus_id_status status) { - struct musb *musb = container_of(nb, struct musb, nb); - struct device *dev = musb->controller; - struct omap2430_glue *glue = dev_get_drvdata(dev->parent); + struct omap2430_glue *glue = _glue; + struct musb *musb = glue_to_musb(glue); - glue->xceiv_event = event; - schedule_work(&glue->omap_musb_mailbox_work); + glue->status = status; + if (!musb) { + dev_err(glue->dev, "musb core is not yet ready\n"); + return; + } - return NOTIFY_OK; + schedule_work(&glue->omap_musb_mailbox_work); } +EXPORT_SYMBOL_GPL(omap_musb_mailbox); -static void omap_musb_mailbox_work(struct work_struct *data_notifier_work) +static void omap_musb_set_mailbox(struct omap2430_glue *glue) { - struct omap2430_glue *glue = container_of(data_notifier_work, - struct omap2430_glue, omap_musb_mailbox_work); struct musb *musb = glue_to_musb(glue); struct device *dev = musb->controller; struct musb_hdrc_platform_data *pdata = dev->platform_data; struct omap_musb_board_data *data = pdata->board_data; - switch (glue->xceiv_event) { - case USB_EVENT_ID: - dev_dbg(musb->controller, "ID GND\n"); + switch (glue->status) { + case OMAP_MUSB_ID_GROUND: + dev_dbg(dev, "ID GND\n"); + musb->xceiv->last_event = USB_EVENT_ID; if (!is_otg_enabled(musb) || musb->gadget_driver) { - pm_runtime_get_sync(musb->controller); + pm_runtime_get_sync(dev); usb_phy_init(musb->xceiv); omap2430_musb_set_vbus(musb, 1); } break; - case USB_EVENT_VBUS: - dev_dbg(musb->controller, "VBUS Connect\n"); + case OMAP_MUSB_VBUS_VALID: + dev_dbg(dev, "VBUS Connect\n"); + musb->xceiv->last_event = USB_EVENT_VBUS; if (musb->gadget_driver) - pm_runtime_get_sync(musb->controller); + pm_runtime_get_sync(dev); usb_phy_init(musb->xceiv); break; - case USB_EVENT_NONE: - dev_dbg(musb->controller, "VBUS Disconnect\n"); + case OMAP_MUSB_ID_FLOAT: + case OMAP_MUSB_VBUS_OFF: + dev_dbg(dev, "VBUS Disconnect\n"); + musb->xceiv->last_event = USB_EVENT_NONE; if (is_otg_enabled(musb) || is_peripheral_enabled(musb)) if (musb->gadget_driver) { - pm_runtime_mark_last_busy(musb->controller); - pm_runtime_put_autosuspend(musb->controller); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } if (data->interface_type == MUSB_INTERFACE_UTMI) { @@ -282,15 +289,24 @@ static void omap_musb_mailbox_work(struct work_struct *data_notifier_work) usb_phy_shutdown(musb->xceiv); break; default: - dev_dbg(musb->controller, "ID float\n"); + dev_dbg(dev, "ID float\n"); } } + +static void omap_musb_mailbox_work(struct work_struct *mailbox_work) +{ + struct omap2430_glue *glue = container_of(mailbox_work, + struct omap2430_glue, omap_musb_mailbox_work); + omap_musb_set_mailbox(glue); +} + static int omap2430_musb_init(struct musb *musb) { u32 l; int status = 0; struct device *dev = musb->controller; + struct omap2430_glue *glue = dev_get_drvdata(dev->parent); struct musb_hdrc_platform_data *plat = dev->platform_data; struct omap_musb_board_data *data = plat->board_data; @@ -330,14 +346,11 @@ static int omap2430_musb_init(struct musb *musb) musb_readl(musb->mregs, OTG_INTERFSEL), musb_readl(musb->mregs, OTG_SIMENABLE)); - musb->nb.notifier_call = musb_otg_notifications; - status = usb_register_notifier(musb->xceiv, &musb->nb); - - if (status) - dev_dbg(musb->controller, "notification register failed\n"); - setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); + if (glue->status != OMAP_MUSB_UNKNOWN) + omap_musb_set_mailbox(glue); + pm_runtime_put_noidle(musb->controller); return 0; @@ -350,12 +363,13 @@ static void omap2430_musb_enable(struct musb *musb) u8 devctl; unsigned long timeout = jiffies + msecs_to_jiffies(1000); struct device *dev = musb->controller; + struct omap2430_glue *glue = dev_get_drvdata(dev->parent); struct musb_hdrc_platform_data *pdata = dev->platform_data; struct omap_musb_board_data *data = pdata->board_data; - switch (musb->xceiv->last_event) { + switch (glue->status) { - case USB_EVENT_ID: + case OMAP_MUSB_ID_GROUND: usb_phy_init(musb->xceiv); if (data->interface_type != MUSB_INTERFACE_UTMI) break; @@ -374,7 +388,7 @@ static void omap2430_musb_enable(struct musb *musb) } break; - case USB_EVENT_VBUS: + case OMAP_MUSB_VBUS_VALID: usb_phy_init(musb->xceiv); break; @@ -385,7 +399,10 @@ static void omap2430_musb_enable(struct musb *musb) static void omap2430_musb_disable(struct musb *musb) { - if (musb->xceiv->last_event) + struct device *dev = musb->controller; + struct omap2430_glue *glue = dev_get_drvdata(dev->parent); + + if (glue->status != OMAP_MUSB_UNKNOWN) usb_phy_shutdown(musb->xceiv); } @@ -439,11 +456,18 @@ static int __devinit omap2430_probe(struct platform_device *pdev) glue->dev = &pdev->dev; glue->musb = musb; + glue->status = OMAP_MUSB_UNKNOWN; pdata->platform_ops = &omap2430_ops; platform_set_drvdata(pdev, glue); + /* + * REVISIT if we ever have two instances of the wrapper, we will be + * in big trouble + */ + _glue = glue; + INIT_WORK(&glue->omap_musb_mailbox_work, omap_musb_mailbox_work); ret = platform_device_add_resources(musb, pdev->resource, @@ -552,7 +576,7 @@ static int __init omap2430_init(void) { return platform_driver_register(&omap2430_driver); } -module_init(omap2430_init); +subsys_initcall(omap2430_init); static void __exit omap2430_exit(void) { diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 25a09fabbe35..a7b809e217ea 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -33,11 +33,11 @@ #include #include #include +#include #include #include #include #include -#include #include /* Register defines */ @@ -159,7 +159,7 @@ struct twl4030_usb { enum twl4030_usb_mode usb_mode; int irq; - u8 linkstat; + enum omap_musb_vbus_id_status linkstat; bool vbus_supplied; u8 asleep; bool irq_enabled; @@ -246,10 +246,11 @@ twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) /*-------------------------------------------------------------------------*/ -static enum usb_phy_events twl4030_usb_linkstat(struct twl4030_usb *twl) +static enum omap_musb_vbus_id_status + twl4030_usb_linkstat(struct twl4030_usb *twl) { int status; - int linkstat = USB_EVENT_NONE; + enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN; struct usb_otg *otg = twl->phy.otg; twl->vbus_supplied = false; @@ -273,24 +274,24 @@ static enum usb_phy_events twl4030_usb_linkstat(struct twl4030_usb *twl) twl->vbus_supplied = true; if (status & BIT(2)) - linkstat = USB_EVENT_ID; + linkstat = OMAP_MUSB_ID_GROUND; else - linkstat = USB_EVENT_VBUS; - } else - linkstat = USB_EVENT_NONE; + linkstat = OMAP_MUSB_VBUS_VALID; + } else { + if (twl->linkstat != OMAP_MUSB_UNKNOWN) + linkstat = OMAP_MUSB_VBUS_OFF; + } dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", status, status, linkstat); - twl->phy.last_event = linkstat; - /* REVISIT this assumes host and peripheral controllers * are registered, and that both are active... */ spin_lock_irq(&twl->lock); twl->linkstat = linkstat; - if (linkstat == USB_EVENT_ID) { + if (linkstat == OMAP_MUSB_ID_GROUND) { otg->default_a = true; twl->phy.state = OTG_STATE_A_IDLE; } else { @@ -501,10 +502,10 @@ static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); static irqreturn_t twl4030_usb_irq(int irq, void *_twl) { struct twl4030_usb *twl = _twl; - int status; + enum omap_musb_vbus_id_status status; status = twl4030_usb_linkstat(twl); - if (status >= 0) { + if (status > 0) { /* FIXME add a set_power() method so that B-devices can * configure the charger appropriately. It's not always * correct to consume VBUS power, and how much current to @@ -516,13 +517,13 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) * USB_LINK_VBUS state. musb_hdrc won't care until it * starts to handle softconnect right. */ - if (status == USB_EVENT_NONE) + if (status == OMAP_MUSB_VBUS_OFF || + status == OMAP_MUSB_ID_FLOAT) twl4030_phy_suspend(twl, 0); else twl4030_phy_resume(twl); - atomic_notifier_call_chain(&twl->phy.notifier, status, - twl->phy.otg->gadget); + omap_musb_mailbox(twl->linkstat); } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); @@ -531,11 +532,12 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) static void twl4030_usb_phy_init(struct twl4030_usb *twl) { - int status; + enum omap_musb_vbus_id_status status; status = twl4030_usb_linkstat(twl); - if (status >= 0) { - if (status == USB_EVENT_NONE) { + if (status > 0) { + if (status == OMAP_MUSB_VBUS_OFF || + status == OMAP_MUSB_ID_FLOAT) { __twl4030_phy_power(twl, 0); twl->asleep = 1; } else { @@ -543,8 +545,7 @@ static void twl4030_usb_phy_init(struct twl4030_usb *twl) twl->asleep = 0; } - atomic_notifier_call_chain(&twl->phy.notifier, status, - twl->phy.otg->gadget); + omap_musb_mailbox(twl->linkstat); } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); } @@ -613,6 +614,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) twl->usb_mode = pdata->usb_mode; twl->vbus_supplied = false; twl->asleep = 1; + twl->linkstat = OMAP_MUSB_UNKNOWN; twl->phy.dev = twl->dev; twl->phy.label = "twl4030"; @@ -639,8 +641,6 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev) if (device_create_file(&pdev->dev, &dev_attr_vbus)) dev_warn(&pdev->dev, "could not create sysfs file\n"); - ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier); - /* Our job is to use irqs and status from the power module * to keep the transceiver disabled when nothing's connected. * diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index dbee00aea755..6c758836cfb1 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -26,10 +26,10 @@ #include #include #include +#include #include #include #include -#include #include #include @@ -100,7 +100,7 @@ struct twl6030_usb { int irq1; int irq2; - u8 linkstat; + enum omap_musb_vbus_id_status linkstat; u8 asleep; bool irq_enabled; bool vbus_enable; @@ -147,7 +147,7 @@ static int twl6030_phy_init(struct usb_phy *x) dev = twl->dev; pdata = dev->platform_data; - if (twl->linkstat == USB_EVENT_ID) + if (twl->linkstat == OMAP_MUSB_ID_GROUND) pdata->phy_power(twl->dev, 1, 1); else pdata->phy_power(twl->dev, 0, 1); @@ -235,13 +235,13 @@ static ssize_t twl6030_usb_vbus_show(struct device *dev, spin_lock_irqsave(&twl->lock, flags); switch (twl->linkstat) { - case USB_EVENT_VBUS: + case OMAP_MUSB_VBUS_VALID: ret = snprintf(buf, PAGE_SIZE, "vbus\n"); break; - case USB_EVENT_ID: + case OMAP_MUSB_ID_GROUND: ret = snprintf(buf, PAGE_SIZE, "id\n"); break; - case USB_EVENT_NONE: + case OMAP_MUSB_VBUS_OFF: ret = snprintf(buf, PAGE_SIZE, "none\n"); break; default: @@ -257,7 +257,7 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl) { struct twl6030_usb *twl = _twl; struct usb_otg *otg = twl->phy.otg; - int status; + enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; u8 vbus_state, hw_state; hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); @@ -268,22 +268,20 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl) if (vbus_state & VBUS_DET) { regulator_enable(twl->usb3v3); twl->asleep = 1; - status = USB_EVENT_VBUS; + status = OMAP_MUSB_VBUS_VALID; otg->default_a = false; twl->phy.state = OTG_STATE_B_IDLE; twl->linkstat = status; - twl->phy.last_event = status; - atomic_notifier_call_chain(&twl->phy.notifier, - status, otg->gadget); + omap_musb_mailbox(status); } else { - status = USB_EVENT_NONE; - twl->linkstat = status; - twl->phy.last_event = status; - atomic_notifier_call_chain(&twl->phy.notifier, - status, otg->gadget); - if (twl->asleep) { - regulator_disable(twl->usb3v3); - twl->asleep = 0; + if (twl->linkstat != OMAP_MUSB_UNKNOWN) { + status = OMAP_MUSB_VBUS_OFF; + twl->linkstat = status; + omap_musb_mailbox(status); + if (twl->asleep) { + regulator_disable(twl->usb3v3); + twl->asleep = 0; + } } } } @@ -296,7 +294,7 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) { struct twl6030_usb *twl = _twl; struct usb_otg *otg = twl->phy.otg; - int status = USB_EVENT_NONE; + enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN; u8 hw_state; hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); @@ -308,13 +306,11 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, 0x1); twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_SET, 0x10); - status = USB_EVENT_ID; + status = OMAP_MUSB_ID_GROUND; otg->default_a = true; twl->phy.state = OTG_STATE_A_IDLE; twl->linkstat = status; - twl->phy.last_event = status; - atomic_notifier_call_chain(&twl->phy.notifier, status, - otg->gadget); + omap_musb_mailbox(status); } else { twl6030_writeb(twl, TWL_MODULE_USB, USB_ID_INT_EN_HI_CLR, 0x10); @@ -419,6 +415,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) twl->irq1 = platform_get_irq(pdev, 0); twl->irq2 = platform_get_irq(pdev, 1); twl->features = pdata->features; + twl->linkstat = OMAP_MUSB_UNKNOWN; twl->phy.dev = twl->dev; twl->phy.label = "twl6030"; @@ -449,8 +446,6 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) if (device_create_file(&pdev->dev, &dev_attr_vbus)) dev_warn(&pdev->dev, "could not create sysfs file\n"); - ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier); - INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work); twl->irq_enabled = true; diff --git a/include/linux/usb/musb-omap.h b/include/linux/usb/musb-omap.h new file mode 100644 index 000000000000..7774c5986f07 --- /dev/null +++ b/include/linux/usb/musb-omap.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011-2012 by Texas Instruments + * + * The Inventra Controller Driver for Linux 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 __MUSB_OMAP_H__ +#define __MUSB_OMAP_H__ + +enum omap_musb_vbus_id_status { + OMAP_MUSB_UNKNOWN = 0, + OMAP_MUSB_ID_GROUND, + OMAP_MUSB_ID_FLOAT, + OMAP_MUSB_VBUS_VALID, + OMAP_MUSB_VBUS_OFF, +}; + +#if (defined(CONFIG_USB_MUSB_OMAP2PLUS) || \ + defined(CONFIG_USB_MUSB_OMAP2PLUS_MODULE)) +void omap_musb_mailbox(enum omap_musb_vbus_id_status status); +#else +static inline void omap_musb_mailbox(enum omap_musb_vbus_id_status status) +{ +} +#endif + +#endif /* __MUSB_OMAP_H__ */ -- cgit v1.2.3 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') 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 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') 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 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') 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 From 2bd237b8a45eb107d6304496fbd5b4a34471fbd3 Mon Sep 17 00:00:00 2001 From: Benoît Thébaudeau Date: Mon, 18 Jun 2012 15:02:44 -0300 Subject: [media] media: gpio-ir-recv: add map name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make it possible for gpio-ir-recv users to choose a map name. Cc: Ravi Kumar V Signed-off-by: Benoît Thébaudeau Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/gpio-ir-recv.c | 2 +- include/media/gpio-ir-recv.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index b41e13c393cd..15e346e0bcd0 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -93,7 +93,7 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev) rcdev->input_id.version = 0x0100; rcdev->dev.parent = &pdev->dev; rcdev->driver_name = GPIO_IR_DRIVER_NAME; - rcdev->map_name = RC_MAP_EMPTY; + rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY; gpio_dev->rcdev = rcdev; gpio_dev->gpio_nr = pdata->gpio_nr; diff --git a/include/media/gpio-ir-recv.h b/include/media/gpio-ir-recv.h index 67797bf5d432..91546f35b7e1 100644 --- a/include/media/gpio-ir-recv.h +++ b/include/media/gpio-ir-recv.h @@ -16,6 +16,7 @@ struct gpio_ir_recv_platform_data { int gpio_nr; bool active_low; + const char *map_name; }; #endif /* __GPIO_IR_RECV_H__ */ -- cgit v1.2.3 From 023b515e5b304122f3802abaa68d1da46fdf48b8 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 19 Jun 2012 09:54:54 +0200 Subject: uas: task mgmt & error handling Add task management support, wind up in abort and device reset error handlers. Cancel all in-flight urbs in bus reset handler. Signed-off-by: Gerd Hoffmann Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/uas.c | 160 +++++++++++++++++++++++++++++++++++++--------- include/linux/usb/uas.h | 40 ++++++++++++ 2 files changed, 171 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 875829a56183..638cd64f9610 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -43,7 +43,8 @@ struct uas_dev_info { struct usb_device *udev; struct usb_anchor sense_urbs; struct usb_anchor data_urbs; - int qdepth; + int qdepth, resetting; + struct response_ui response; unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe; unsigned use_streams:1; unsigned uas_sense_old:1; @@ -68,6 +69,7 @@ enum { struct uas_cmd_info { unsigned int state; unsigned int stream; + unsigned int aborted; struct urb *cmd_urb; struct urb *data_in_urb; struct urb *data_out_urb; @@ -222,16 +224,24 @@ static void uas_stat_cmplt(struct urb *urb) return; } + if (devinfo->resetting) { + usb_free_urb(urb); + return; + } + tag = be16_to_cpup(&iu->tag) - 1; if (tag == 0) cmnd = devinfo->cmnd; else cmnd = scsi_host_find_tag(shost, tag - 1); if (!cmnd) { - usb_free_urb(urb); - return; + if (iu->iu_id != IU_ID_RESPONSE) { + usb_free_urb(urb); + return; + } + } else { + cmdinfo = (void *)&cmnd->SCp; } - cmdinfo = (void *)&cmnd->SCp; switch (iu->iu_id) { case IU_ID_STATUS: @@ -260,6 +270,10 @@ static void uas_stat_cmplt(struct urb *urb) case IU_ID_WRITE_READY: uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB); break; + case IU_ID_RESPONSE: + /* store results for uas_eh_task_mgmt() */ + memcpy(&devinfo->response, iu, sizeof(devinfo->response)); + break; default: scmd_printk(KERN_ERR, cmnd, "Bogus IU (%d) received on status pipe\n", iu->iu_id); @@ -287,6 +301,9 @@ static void uas_data_cmplt(struct urb *urb) } else { sdb->resid = sdb->length - urb->actual_length; } + if (cmdinfo->aborted) { + return; + } uas_try_complete(cmnd, __func__); } @@ -377,6 +394,51 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, return NULL; } +static int uas_submit_task_urb(struct scsi_cmnd *cmnd, gfp_t gfp, + u8 function, u16 stream_id) +{ + struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; + struct usb_device *udev = devinfo->udev; + struct urb *urb = usb_alloc_urb(0, gfp); + struct task_mgmt_iu *iu; + int err = -ENOMEM; + + if (!urb) + goto err; + + iu = kzalloc(sizeof(*iu), gfp); + if (!iu) + goto err; + + iu->iu_id = IU_ID_TASK_MGMT; + iu->tag = cpu_to_be16(stream_id); + int_to_scsilun(cmnd->device->lun, &iu->lun); + + iu->function = function; + switch (function) { + case TMF_ABORT_TASK: + if (blk_rq_tagged(cmnd->request)) + iu->task_tag = cpu_to_be16(cmnd->request->tag + 2); + else + iu->task_tag = cpu_to_be16(1); + break; + } + + usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu), + usb_free_urb, NULL); + urb->transfer_flags |= URB_FREE_BUFFER; + + err = usb_submit_urb(urb, gfp); + if (err) + goto err; + + return 0; + +err: + usb_free_urb(urb); + return err; +} + /* * Why should I request the Status IU before sending the Command IU? Spec * says to, but also says the device may receive them in any order. Seems @@ -502,6 +564,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, cmdinfo->state = SUBMIT_STATUS_URB | ALLOC_CMD_URB | SUBMIT_CMD_URB; + cmdinfo->aborted = 0; switch (cmnd->sc_data_direction) { case DMA_FROM_DEVICE: @@ -537,34 +600,66 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, static DEF_SCSI_QCMD(uas_queuecommand) -static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) +static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, + const char *fname, u8 function) { - uas_log_cmd_state(cmnd, __func__); + struct Scsi_Host *shost = cmnd->device->host; + struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; + u16 tag = 9999; /* FIXME */ -/* XXX: Send ABORT TASK Task Management command */ - return FAILED; + memset(&devinfo->response, 0, sizeof(devinfo->response)); + if (uas_submit_sense_urb(shost, GFP_NOIO, tag)) { + shost_printk(KERN_INFO, shost, + "%s: %s: submit sense urb failed\n", + __func__, fname); + return FAILED; + } + if (uas_submit_task_urb(cmnd, GFP_NOIO, function, tag)) { + shost_printk(KERN_INFO, shost, + "%s: %s: submit task mgmt urb failed\n", + __func__, fname); + return FAILED; + } + if (0 == usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000)) { + shost_printk(KERN_INFO, shost, + "%s: %s timed out\n", __func__, fname); + return FAILED; + } + if (be16_to_cpu(devinfo->response.tag) != tag) { + shost_printk(KERN_INFO, shost, + "%s: %s failed (wrong tag %d/%d)\n", __func__, + fname, be16_to_cpu(devinfo->response.tag), tag); + return FAILED; + } + if (devinfo->response.response_code != RC_TMF_COMPLETE) { + shost_printk(KERN_INFO, shost, + "%s: %s failed (rc 0x%x)\n", __func__, + fname, devinfo->response.response_code); + return FAILED; + } + return SUCCESS; } -static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd) +static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) { - struct scsi_device *sdev = cmnd->device; - sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__, - cmnd->request->tag); + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; + int ret; -/* XXX: Send LOGICAL UNIT RESET Task Management command */ - return FAILED; + uas_log_cmd_state(cmnd, __func__); + cmdinfo->aborted = 1; + ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK); + if (cmdinfo->state & DATA_IN_URB_INFLIGHT) + usb_kill_urb(cmdinfo->data_in_urb); + if (cmdinfo->state & DATA_OUT_URB_INFLIGHT) + usb_kill_urb(cmdinfo->data_out_urb); + return ret; } -static int uas_eh_target_reset_handler(struct scsi_cmnd *cmnd) +static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd) { - struct scsi_device *sdev = cmnd->device; - sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__, - cmnd->request->tag); - -/* XXX: Can we reset just the one USB interface? - * Would calling usb_set_interface() have the right effect? - */ - return FAILED; + sdev_printk(KERN_INFO, cmnd->device, "%s\n", __func__); + return uas_eh_task_mgmt(cmnd, "LOGICAL UNIT RESET", + TMF_LOGICAL_UNIT_RESET); } static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) @@ -572,14 +667,21 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) struct scsi_device *sdev = cmnd->device; struct uas_dev_info *devinfo = sdev->hostdata; struct usb_device *udev = devinfo->udev; + int err; - sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__, - cmnd->request->tag); + devinfo->resetting = 1; + usb_kill_anchored_urbs(&devinfo->sense_urbs); + usb_kill_anchored_urbs(&devinfo->data_urbs); + err = usb_reset_device(udev); + devinfo->resetting = 0; - if (usb_reset_device(udev)) - return SUCCESS; + if (err) { + shost_printk(KERN_INFO, sdev->host, "%s FAILED\n", __func__); + return FAILED; + } - return FAILED; + shost_printk(KERN_INFO, sdev->host, "%s success\n", __func__); + return SUCCESS; } static int uas_slave_alloc(struct scsi_device *sdev) @@ -604,7 +706,6 @@ static struct scsi_host_template uas_host_template = { .slave_configure = uas_slave_configure, .eh_abort_handler = uas_eh_abort_handler, .eh_device_reset_handler = uas_eh_device_reset_handler, - .eh_target_reset_handler = uas_eh_target_reset_handler, .eh_bus_reset_handler = uas_eh_bus_reset_handler, .can_queue = 65536, /* Is there a limit on the _host_ ? */ .this_id = -1, @@ -766,6 +867,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) devinfo->intf = intf; devinfo->udev = udev; + devinfo->resetting = 0; init_usb_anchor(&devinfo->sense_urbs); init_usb_anchor(&devinfo->data_urbs); uas_configure_endpoints(devinfo); diff --git a/include/linux/usb/uas.h b/include/linux/usb/uas.h index 9a988e413694..5499ab5c94bd 100644 --- a/include/linux/usb/uas.h +++ b/include/linux/usb/uas.h @@ -20,6 +20,28 @@ enum { IU_ID_WRITE_READY = 0x07, }; +enum { + TMF_ABORT_TASK = 0x01, + TMF_ABORT_TASK_SET = 0x02, + TMF_CLEAR_TASK_SET = 0x04, + TMF_LOGICAL_UNIT_RESET = 0x08, + TMF_I_T_NEXUS_RESET = 0x10, + TMF_CLEAR_ACA = 0x40, + TMF_QUERY_TASK = 0x80, + TMF_QUERY_TASK_SET = 0x81, + TMF_QUERY_ASYNC_EVENT = 0x82, +}; + +enum { + RC_TMF_COMPLETE = 0x00, + RC_INVALID_INFO_UNIT = 0x02, + RC_TMF_NOT_SUPPORTED = 0x04, + RC_TMF_FAILED = 0x05, + RC_TMF_SUCCEEDED = 0x08, + RC_INCORRECT_LUN = 0x09, + RC_OVERLAPPED_TAG = 0x0a, +}; + struct command_iu { __u8 iu_id; __u8 rsvd1; @@ -32,6 +54,16 @@ struct command_iu { __u8 cdb[16]; /* XXX: Overflow-checking tools may misunderstand */ }; +struct task_mgmt_iu { + __u8 iu_id; + __u8 rsvd1; + __be16 tag; + __u8 function; + __u8 rsvd2; + __be16 task_tag; + struct scsi_lun lun; +}; + /* * Also used for the Read Ready and Write Ready IUs since they have the * same first four bytes @@ -47,6 +79,14 @@ struct sense_iu { __u8 sense[SCSI_SENSE_BUFFERSIZE]; }; +struct response_ui { + __u8 iu_id; + __u8 rsvd1; + __be16 tag; + __be16 add_response_info; + __u8 response_code; +}; + struct usb_pipe_usage_descriptor { __u8 bLength; __u8 bDescriptorType; -- cgit v1.2.3 From efc27f8ceebe5eb147fa31d6c995706d327ad855 Mon Sep 17 00:00:00 2001 From: Vijay Subramanian Date: Sun, 24 Jun 2012 13:03:07 +0000 Subject: net: Remove 'unlikely' qualifier in skb_steal_sock() With early demux enabled by default for TCP flows, there is high chance that skb->sk will be non-null. 'unlikely()' was removed from __inet_lookup_skb() but maybe it can be removed from skb_steal_sock() as well. Note: skb_steal_sock() is also called by __inet6_lookup_skb() and __udp4_lib_lookup_skb() but they are protected by their own 'unlikely' calls. Signed-off-by: Vijay Subramanian Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 87b424ae750a..21086036e348 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2154,7 +2154,7 @@ static inline void sk_change_net(struct sock *sk, struct net *net) static inline struct sock *skb_steal_sock(struct sk_buff *skb) { - if (unlikely(skb->sk)) { + if (skb->sk) { struct sock *sk = skb->sk; skb->destructor = NULL; -- cgit v1.2.3 From deaa58542b21d2b395db816952c202034319cbb4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 24 Jun 2012 20:22:49 +0000 Subject: net: struct sock cleanups Add missing kernel doc for sk_rx_dst Move sk_rx_dst to avoid two 32bit holes on 64bit arches Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sock.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 21086036e348..dcb54a0793ec 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -198,6 +198,7 @@ struct cg_proto; * @sk_lock: synchronizer * @sk_rcvbuf: size of receive buffer in bytes * @sk_wq: sock wait queue and async head + * @sk_rx_dst: receive input route used by early tcp demux * @sk_dst_cache: destination cache * @sk_dst_lock: destination cache lock * @sk_policy: flow policy @@ -317,9 +318,9 @@ struct sock { struct xfrm_policy *sk_policy[2]; #endif unsigned long sk_flags; + struct dst_entry *sk_rx_dst; struct dst_entry *sk_dst_cache; spinlock_t sk_dst_lock; - struct dst_entry *sk_rx_dst; atomic_t sk_wmem_alloc; atomic_t sk_omem_alloc; int sk_sndbuf; -- cgit v1.2.3 From c41254006377842013922fb1e407391f24c59522 Mon Sep 17 00:00:00 2001 From: Sjur Brændeland Date: Mon, 25 Jun 2012 07:49:41 +0000 Subject: caif-hsi: Add rtnl support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add RTNL support for managing the caif hsi interface. The HSI HW interface is no longer registering as a device, instead we use symbol_get to get hold of the HSI API. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 226 +++++++++++++++++++++++++++----------------- include/net/caif/caif_hsi.h | 21 +++- 2 files changed, 157 insertions(+), 90 deletions(-) (limited to 'include') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index d80759e58532..a14f85c0f0e8 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -79,7 +79,6 @@ MODULE_PARM_DESC(hsi_low_threshold, "HSI high threshold (FLOW ON)."); #define HIGH_WATER_MARK hsi_high_threshold static LIST_HEAD(cfhsi_list); -static spinlock_t cfhsi_list_lock; static void cfhsi_inactivity_tout(unsigned long arg) { @@ -1148,42 +1147,6 @@ static void cfhsi_setup(struct net_device *dev) cfhsi->ndev = dev; } -int cfhsi_probe(struct platform_device *pdev) -{ - struct cfhsi *cfhsi = NULL; - struct net_device *ndev; - - int res; - - ndev = alloc_netdev(sizeof(struct cfhsi), "cfhsi%d", cfhsi_setup); - if (!ndev) - return -ENODEV; - - cfhsi = netdev_priv(ndev); - cfhsi->ndev = ndev; - cfhsi->pdev = pdev; - - /* Assign the HSI device. */ - cfhsi->dev = pdev->dev.platform_data; - - /* Assign the driver to this HSI device. */ - cfhsi->dev->drv = &cfhsi->drv; - - /* Register network device. */ - res = register_netdev(ndev); - if (res) { - dev_err(&ndev->dev, "%s: Registration error: %d.\n", - __func__, res); - free_netdev(ndev); - } - /* Add CAIF HSI device to list. */ - spin_lock(&cfhsi_list_lock); - list_add_tail(&cfhsi->list, &cfhsi_list); - spin_unlock(&cfhsi_list_lock); - - return res; -} - static int cfhsi_open(struct net_device *ndev) { struct cfhsi *cfhsi = netdev_priv(ndev); @@ -1364,85 +1327,170 @@ static int cfhsi_close(struct net_device *ndev) return 0; } +static void cfhsi_uninit(struct net_device *dev) +{ + struct cfhsi *cfhsi = netdev_priv(dev); + ASSERT_RTNL(); + symbol_put(cfhsi_get_device); + list_del(&cfhsi->list); +} + static const struct net_device_ops cfhsi_ops = { + .ndo_uninit = cfhsi_uninit, .ndo_open = cfhsi_open, .ndo_stop = cfhsi_close, .ndo_start_xmit = cfhsi_xmit }; -int cfhsi_remove(struct platform_device *pdev) +static void cfhsi_netlink_parms(struct nlattr *data[], struct cfhsi *cfhsi) { - struct list_head *list_node; - struct list_head *n; - struct cfhsi *cfhsi = NULL; - struct cfhsi_dev *dev; + int i; - dev = (struct cfhsi_dev *)pdev->dev.platform_data; - spin_lock(&cfhsi_list_lock); - list_for_each_safe(list_node, n, &cfhsi_list) { - cfhsi = list_entry(list_node, struct cfhsi, list); - /* Find the corresponding device. */ - if (cfhsi->dev == dev) { - /* Remove from list. */ - list_del(list_node); - spin_unlock(&cfhsi_list_lock); - return 0; - } + if (!data) { + pr_debug("no params data found\n"); + return; } - spin_unlock(&cfhsi_list_lock); - return -ENODEV; + + i = __IFLA_CAIF_HSI_INACTIVITY_TOUT; + if (data[i]) + inactivity_timeout = nla_get_u32(data[i]); + + i = __IFLA_CAIF_HSI_AGGREGATION_TOUT; + if (data[i]) + aggregation_timeout = nla_get_u32(data[i]); + + i = __IFLA_CAIF_HSI_HEAD_ALIGN; + if (data[i]) + hsi_head_align = nla_get_u32(data[i]); + + i = __IFLA_CAIF_HSI_TAIL_ALIGN; + if (data[i]) + hsi_tail_align = nla_get_u32(data[i]); + + i = __IFLA_CAIF_HSI_QHIGH_WATERMARK; + if (data[i]) + hsi_high_threshold = nla_get_u32(data[i]); +} + +static int caif_hsi_changelink(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[]) +{ + cfhsi_netlink_parms(data, netdev_priv(dev)); + netdev_state_change(dev); + return 0; } -struct platform_driver cfhsi_plat_drv = { - .probe = cfhsi_probe, - .remove = cfhsi_remove, - .driver = { - .name = "cfhsi", - .owner = THIS_MODULE, - }, +static const struct nla_policy caif_hsi_policy[__IFLA_CAIF_HSI_MAX + 1] = { + [__IFLA_CAIF_HSI_INACTIVITY_TOUT] = { .type = NLA_U32, .len = 4 }, + [__IFLA_CAIF_HSI_AGGREGATION_TOUT] = { .type = NLA_U32, .len = 4 }, + [__IFLA_CAIF_HSI_HEAD_ALIGN] = { .type = NLA_U32, .len = 4 }, + [__IFLA_CAIF_HSI_TAIL_ALIGN] = { .type = NLA_U32, .len = 4 }, + [__IFLA_CAIF_HSI_QHIGH_WATERMARK] = { .type = NLA_U32, .len = 4 }, + [__IFLA_CAIF_HSI_QLOW_WATERMARK] = { .type = NLA_U32, .len = 4 }, }; -static void __exit cfhsi_exit_module(void) +static size_t caif_hsi_get_size(const struct net_device *dev) +{ + int i; + size_t s = 0; + for (i = __IFLA_CAIF_HSI_UNSPEC + 1; i < __IFLA_CAIF_HSI_MAX; i++) + s += nla_total_size(caif_hsi_policy[i].len); + return s; +} + +static int caif_hsi_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + if (nla_put_u32(skb, __IFLA_CAIF_HSI_INACTIVITY_TOUT, + inactivity_timeout) || + nla_put_u32(skb, __IFLA_CAIF_HSI_AGGREGATION_TOUT, + aggregation_timeout) || + nla_put_u32(skb, __IFLA_CAIF_HSI_HEAD_ALIGN, hsi_head_align) || + nla_put_u32(skb, __IFLA_CAIF_HSI_TAIL_ALIGN, hsi_tail_align) || + nla_put_u32(skb, __IFLA_CAIF_HSI_QHIGH_WATERMARK, + hsi_high_threshold) || + nla_put_u32(skb, __IFLA_CAIF_HSI_QLOW_WATERMARK, + hsi_low_threshold)) + return -EMSGSIZE; + + return 0; +} + +static int caif_hsi_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) { - struct list_head *list_node; - struct list_head *n; struct cfhsi *cfhsi = NULL; + struct platform_device *(*get_dev)(void); - spin_lock(&cfhsi_list_lock); - list_for_each_safe(list_node, n, &cfhsi_list) { - cfhsi = list_entry(list_node, struct cfhsi, list); + ASSERT_RTNL(); + + cfhsi = netdev_priv(dev); + cfhsi_netlink_parms(data, cfhsi); + dev_net_set(cfhsi->ndev, src_net); + + get_dev = symbol_get(cfhsi_get_device); + if (!get_dev) { + pr_err("%s: failed to get the cfhsi device symbol\n", __func__); + return -ENODEV; + } + + /* Assign the HSI device. */ + cfhsi->pdev = (*get_dev)(); + if (!cfhsi->pdev) { + pr_err("%s: failed to get the cfhsi device\n", __func__); + goto err; + } - /* Remove from list. */ - list_del(list_node); - spin_unlock(&cfhsi_list_lock); + /* Assign the HSI device. */ + cfhsi->dev = cfhsi->pdev->dev.platform_data; + + /* Assign the driver to this HSI device. */ + cfhsi->dev->drv = &cfhsi->drv; - unregister_netdevice(cfhsi->ndev); + if (register_netdevice(dev)) { + pr_warn("%s: device rtml registration failed\n", __func__); + goto err; - spin_lock(&cfhsi_list_lock); } - spin_unlock(&cfhsi_list_lock); + /* Add CAIF HSI device to list. */ + list_add_tail(&cfhsi->list, &cfhsi_list); - /* Unregister platform driver. */ - platform_driver_unregister(&cfhsi_plat_drv); + return 0; +err: + symbol_put(cfhsi_get_device); + return -ENODEV; } -static int __init cfhsi_init_module(void) +static struct rtnl_link_ops caif_hsi_link_ops __read_mostly = { + .kind = "cfhsi", + .priv_size = sizeof(struct cfhsi), + .setup = cfhsi_setup, + .maxtype = __IFLA_CAIF_HSI_MAX, + .policy = caif_hsi_policy, + .newlink = caif_hsi_newlink, + .changelink = caif_hsi_changelink, + .get_size = caif_hsi_get_size, + .fill_info = caif_hsi_fill_info, +}; + +static void __exit cfhsi_exit_module(void) { - int result; + struct list_head *list_node; + struct list_head *n; + struct cfhsi *cfhsi; - /* Initialize spin lock. */ - spin_lock_init(&cfhsi_list_lock); + rtnl_link_unregister(&caif_hsi_link_ops); - /* Register platform driver. */ - result = platform_driver_register(&cfhsi_plat_drv); - if (result) { - printk(KERN_ERR "Could not register platform HSI driver: %d.\n", - result); - goto err_dev_register; + rtnl_lock(); + list_for_each_safe(list_node, n, &cfhsi_list) { + cfhsi = list_entry(list_node, struct cfhsi, list); + unregister_netdev(cfhsi->ndev); } + rtnl_unlock(); +} - err_dev_register: - return result; +static int __init cfhsi_init_module(void) +{ + return rtnl_link_register(&caif_hsi_link_ops); } module_init(cfhsi_init_module); diff --git a/include/net/caif/caif_hsi.h b/include/net/caif/caif_hsi.h index 439dadc8102f..a77b2bd7719a 100644 --- a/include/net/caif/caif_hsi.h +++ b/include/net/caif/caif_hsi.h @@ -170,7 +170,26 @@ struct cfhsi { unsigned long bits; }; - extern struct platform_driver cfhsi_driver; +/** + * enum ifla_caif_hsi - CAIF HSI NetlinkRT parameters. + * @IFLA_CAIF_HSI_INACTIVITY_TOUT: Inactivity timeout before + * taking the HSI wakeline down, in milliseconds. + * When using RT Netlink to create, destroy or configure a CAIF HSI interface, + * enum ifla_caif_hsi is used to specify the configuration attributes. + */ +enum ifla_caif_hsi { + __IFLA_CAIF_HSI_UNSPEC, + __IFLA_CAIF_HSI_INACTIVITY_TOUT, + __IFLA_CAIF_HSI_AGGREGATION_TOUT, + __IFLA_CAIF_HSI_HEAD_ALIGN, + __IFLA_CAIF_HSI_TAIL_ALIGN, + __IFLA_CAIF_HSI_QHIGH_WATERMARK, + __IFLA_CAIF_HSI_QLOW_WATERMARK, + __IFLA_CAIF_HSI_MAX +}; + +extern struct platform_device *cfhsi_get_device(void); + #endif /* CAIF_HSI_H_ */ -- cgit v1.2.3 From 1c385f1fdf6f9c66d982802cd74349c040980b50 Mon Sep 17 00:00:00 2001 From: Sjur Brændeland Date: Mon, 25 Jun 2012 07:49:42 +0000 Subject: caif-hsi: Replace platform device with ops structure. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove use of struct platform_device, and replace it with struct cfhsi_ops. Updated variable names in the same spirit: cfhsi_get_dev to cfhsi_get_ops, cfhsi->dev to cfhsi->ops and, cfhsi->dev.drv to cfhsi->ops->cb_ops. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 109 +++++++++++++++++++++----------------------- include/net/caif/caif_hsi.h | 38 +++++++-------- 2 files changed, 70 insertions(+), 77 deletions(-) (limited to 'include') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index a14f85c0f0e8..0927c108bd14 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -184,7 +183,7 @@ static int cfhsi_flush_fifo(struct cfhsi *cfhsi) __func__); do { - ret = cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, + ret = cfhsi->ops->cfhsi_fifo_occupancy(cfhsi->ops, &fifo_occupancy); if (ret) { netdev_warn(cfhsi->ndev, @@ -197,8 +196,8 @@ static int cfhsi_flush_fifo(struct cfhsi *cfhsi) fifo_occupancy = min(sizeof(buffer), fifo_occupancy); set_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits); - ret = cfhsi->dev->cfhsi_rx(buffer, fifo_occupancy, - cfhsi->dev); + ret = cfhsi->ops->cfhsi_rx(buffer, fifo_occupancy, + cfhsi->ops); if (ret) { clear_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits); netdev_warn(cfhsi->ndev, @@ -371,7 +370,7 @@ static void cfhsi_start_tx(struct cfhsi *cfhsi) } /* Set up new transfer. */ - res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev); + res = cfhsi->ops->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->ops); if (WARN_ON(res < 0)) netdev_err(cfhsi->ndev, "%s: TX error %d.\n", __func__, res); @@ -410,11 +409,11 @@ static void cfhsi_tx_done(struct cfhsi *cfhsi) return; } -static void cfhsi_tx_done_cb(struct cfhsi_drv *drv) +static void cfhsi_tx_done_cb(struct cfhsi_cb_ops *cb_ops) { struct cfhsi *cfhsi; - cfhsi = container_of(drv, struct cfhsi, drv); + cfhsi = container_of(cb_ops, struct cfhsi, cb_ops); netdev_dbg(cfhsi->ndev, "%s.\n", __func__); @@ -476,8 +475,8 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) skb->dev = cfhsi->ndev; /* - * We are called from a arch specific platform device. - * Unfortunately we don't know what context we're + * We are in a callback handler and + * unfortunately we don't know what context we're * running in. */ if (in_interrupt()) @@ -607,7 +606,7 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) skb->dev = cfhsi->ndev; /* - * We're called from a platform device, + * We're called in callback from HSI * and don't know the context we're running in. */ if (in_interrupt()) @@ -711,8 +710,8 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) netdev_dbg(cfhsi->ndev, "%s: Start RX.\n", __func__); - res = cfhsi->dev->cfhsi_rx(rx_ptr, rx_len, - cfhsi->dev); + res = cfhsi->ops->cfhsi_rx(rx_ptr, rx_len, + cfhsi->ops); if (WARN_ON(res < 0)) { netdev_err(cfhsi->ndev, "%s: RX error %d.\n", __func__, res); @@ -765,11 +764,11 @@ static void cfhsi_rx_slowpath(unsigned long arg) cfhsi_rx_done(cfhsi); } -static void cfhsi_rx_done_cb(struct cfhsi_drv *drv) +static void cfhsi_rx_done_cb(struct cfhsi_cb_ops *cb_ops) { struct cfhsi *cfhsi; - cfhsi = container_of(drv, struct cfhsi, drv); + cfhsi = container_of(cb_ops, struct cfhsi, cb_ops); netdev_dbg(cfhsi->ndev, "%s.\n", __func__); @@ -803,7 +802,7 @@ static void cfhsi_wake_up(struct work_struct *work) } /* Activate wake line. */ - cfhsi->dev->cfhsi_wake_up(cfhsi->dev); + cfhsi->ops->cfhsi_wake_up(cfhsi->ops); netdev_dbg(cfhsi->ndev, "%s: Start waiting.\n", __func__); @@ -819,7 +818,7 @@ static void cfhsi_wake_up(struct work_struct *work) __func__, ret); clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); - cfhsi->dev->cfhsi_wake_down(cfhsi->dev); + cfhsi->ops->cfhsi_wake_down(cfhsi->ops); return; } else if (!ret) { bool ca_wake = false; @@ -830,14 +829,14 @@ static void cfhsi_wake_up(struct work_struct *work) __func__); /* Check FIFO to check if modem has sent something. */ - WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, + WARN_ON(cfhsi->ops->cfhsi_fifo_occupancy(cfhsi->ops, &fifo_occupancy)); netdev_dbg(cfhsi->ndev, "%s: Bytes in FIFO: %u.\n", __func__, (unsigned) fifo_occupancy); /* Check if we misssed the interrupt. */ - WARN_ON(cfhsi->dev->cfhsi_get_peer_wake(cfhsi->dev, + WARN_ON(cfhsi->ops->cfhsi_get_peer_wake(cfhsi->ops, &ca_wake)); if (ca_wake) { @@ -852,7 +851,7 @@ static void cfhsi_wake_up(struct work_struct *work) } clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); - cfhsi->dev->cfhsi_wake_down(cfhsi->dev); + cfhsi->ops->cfhsi_wake_down(cfhsi->ops); return; } wake_ack: @@ -865,7 +864,7 @@ wake_ack: /* Resume read operation. */ netdev_dbg(cfhsi->ndev, "%s: Start RX.\n", __func__); - res = cfhsi->dev->cfhsi_rx(cfhsi->rx_ptr, cfhsi->rx_len, cfhsi->dev); + res = cfhsi->ops->cfhsi_rx(cfhsi->rx_ptr, cfhsi->rx_len, cfhsi->ops); if (WARN_ON(res < 0)) netdev_err(cfhsi->ndev, "%s: RX err %d.\n", __func__, res); @@ -896,7 +895,7 @@ wake_ack: if (likely(len > 0)) { /* Set up new transfer. */ - res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev); + res = cfhsi->ops->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->ops); if (WARN_ON(res < 0)) { netdev_err(cfhsi->ndev, "%s: TX error %d.\n", __func__, res); @@ -923,7 +922,7 @@ static void cfhsi_wake_down(struct work_struct *work) return; /* Deactivate wake line. */ - cfhsi->dev->cfhsi_wake_down(cfhsi->dev); + cfhsi->ops->cfhsi_wake_down(cfhsi->ops); /* Wait for acknowledge. */ ret = CFHSI_WAKE_TOUT; @@ -942,7 +941,7 @@ static void cfhsi_wake_down(struct work_struct *work) netdev_err(cfhsi->ndev, "%s: Timeout.\n", __func__); /* Check if we misssed the interrupt. */ - WARN_ON(cfhsi->dev->cfhsi_get_peer_wake(cfhsi->dev, + WARN_ON(cfhsi->ops->cfhsi_get_peer_wake(cfhsi->ops, &ca_wake)); if (!ca_wake) netdev_err(cfhsi->ndev, "%s: CA Wake missed !.\n", @@ -951,7 +950,7 @@ static void cfhsi_wake_down(struct work_struct *work) /* Check FIFO occupancy. */ while (retry) { - WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, + WARN_ON(cfhsi->ops->cfhsi_fifo_occupancy(cfhsi->ops, &fifo_occupancy)); if (!fifo_occupancy) @@ -969,8 +968,7 @@ static void cfhsi_wake_down(struct work_struct *work) clear_bit(CFHSI_AWAKE, &cfhsi->bits); /* Cancel pending RX requests. */ - cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev); - + cfhsi->ops->cfhsi_rx_cancel(cfhsi->ops); } static void cfhsi_out_of_sync(struct work_struct *work) @@ -984,11 +982,11 @@ static void cfhsi_out_of_sync(struct work_struct *work) rtnl_unlock(); } -static void cfhsi_wake_up_cb(struct cfhsi_drv *drv) +static void cfhsi_wake_up_cb(struct cfhsi_cb_ops *cb_ops) { struct cfhsi *cfhsi = NULL; - cfhsi = container_of(drv, struct cfhsi, drv); + cfhsi = container_of(cb_ops, struct cfhsi, cb_ops); netdev_dbg(cfhsi->ndev, "%s.\n", __func__); @@ -1003,11 +1001,11 @@ static void cfhsi_wake_up_cb(struct cfhsi_drv *drv) queue_work(cfhsi->wq, &cfhsi->wake_up_work); } -static void cfhsi_wake_down_cb(struct cfhsi_drv *drv) +static void cfhsi_wake_down_cb(struct cfhsi_cb_ops *cb_ops) { struct cfhsi *cfhsi = NULL; - cfhsi = container_of(drv, struct cfhsi, drv); + cfhsi = container_of(cb_ops, struct cfhsi, cb_ops); netdev_dbg(cfhsi->ndev, "%s.\n", __func__); @@ -1110,7 +1108,7 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev) WARN_ON(!len); /* Set up new transfer. */ - res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev); + res = cfhsi->ops->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->ops); if (WARN_ON(res < 0)) { netdev_err(cfhsi->ndev, "%s: TX error %d.\n", __func__, res); @@ -1125,19 +1123,19 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev) return 0; } -static const struct net_device_ops cfhsi_ops; +static const struct net_device_ops cfhsi_netdevops; static void cfhsi_setup(struct net_device *dev) { int i; struct cfhsi *cfhsi = netdev_priv(dev); dev->features = 0; - dev->netdev_ops = &cfhsi_ops; dev->type = ARPHRD_CAIF; dev->flags = IFF_POINTOPOINT | IFF_NOARP; dev->mtu = CFHSI_MAX_CAIF_FRAME_SZ; dev->tx_queue_len = 0; dev->destructor = free_netdev; + dev->netdev_ops = &cfhsi_netdevops; for (i = 0; i < CFHSI_PRIO_LAST; ++i) skb_queue_head_init(&cfhsi->qhead[i]); cfhsi->cfdev.link_select = CAIF_LINK_HIGH_BANDW; @@ -1213,10 +1211,10 @@ static int cfhsi_open(struct net_device *ndev) spin_lock_init(&cfhsi->lock); /* Set up the driver. */ - cfhsi->drv.tx_done_cb = cfhsi_tx_done_cb; - cfhsi->drv.rx_done_cb = cfhsi_rx_done_cb; - cfhsi->drv.wake_up_cb = cfhsi_wake_up_cb; - cfhsi->drv.wake_down_cb = cfhsi_wake_down_cb; + cfhsi->cb_ops.tx_done_cb = cfhsi_tx_done_cb; + cfhsi->cb_ops.rx_done_cb = cfhsi_rx_done_cb; + cfhsi->cb_ops.wake_up_cb = cfhsi_wake_up_cb; + cfhsi->cb_ops.wake_down_cb = cfhsi_wake_down_cb; /* Initialize the work queues. */ INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up); @@ -1230,7 +1228,7 @@ static int cfhsi_open(struct net_device *ndev) clear_bit(CFHSI_AWAKE, &cfhsi->bits); /* Create work thread. */ - cfhsi->wq = create_singlethread_workqueue(cfhsi->pdev->name); + cfhsi->wq = create_singlethread_workqueue(cfhsi->ndev->name); if (!cfhsi->wq) { netdev_err(cfhsi->ndev, "%s: Failed to create work queue.\n", __func__); @@ -1257,7 +1255,7 @@ static int cfhsi_open(struct net_device *ndev) cfhsi->aggregation_timer.function = cfhsi_aggregation_tout; /* Activate HSI interface. */ - res = cfhsi->dev->cfhsi_up(cfhsi->dev); + res = cfhsi->ops->cfhsi_up(cfhsi->ops); if (res) { netdev_err(cfhsi->ndev, "%s: can't activate HSI interface: %d.\n", @@ -1275,7 +1273,7 @@ static int cfhsi_open(struct net_device *ndev) return res; err_net_reg: - cfhsi->dev->cfhsi_down(cfhsi->dev); + cfhsi->ops->cfhsi_down(cfhsi->ops); err_activate: destroy_workqueue(cfhsi->wq); err_create_wq: @@ -1305,7 +1303,7 @@ static int cfhsi_close(struct net_device *ndev) del_timer_sync(&cfhsi->aggregation_timer); /* Cancel pending RX request (if any) */ - cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev); + cfhsi->ops->cfhsi_rx_cancel(cfhsi->ops); /* Destroy workqueue */ destroy_workqueue(cfhsi->wq); @@ -1318,7 +1316,7 @@ static int cfhsi_close(struct net_device *ndev) cfhsi_abort_tx(cfhsi); /* Deactivate interface */ - cfhsi->dev->cfhsi_down(cfhsi->dev); + cfhsi->ops->cfhsi_down(cfhsi->ops); /* Free buffers. */ kfree(tx_buf); @@ -1335,7 +1333,7 @@ static void cfhsi_uninit(struct net_device *dev) list_del(&cfhsi->list); } -static const struct net_device_ops cfhsi_ops = { +static const struct net_device_ops cfhsi_netdevops = { .ndo_uninit = cfhsi_uninit, .ndo_open = cfhsi_open, .ndo_stop = cfhsi_close, @@ -1419,7 +1417,7 @@ static int caif_hsi_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { struct cfhsi *cfhsi = NULL; - struct platform_device *(*get_dev)(void); + struct cfhsi_ops *(*get_ops)(void); ASSERT_RTNL(); @@ -1427,36 +1425,31 @@ static int caif_hsi_newlink(struct net *src_net, struct net_device *dev, cfhsi_netlink_parms(data, cfhsi); dev_net_set(cfhsi->ndev, src_net); - get_dev = symbol_get(cfhsi_get_device); - if (!get_dev) { - pr_err("%s: failed to get the cfhsi device symbol\n", __func__); + get_ops = symbol_get(cfhsi_get_ops); + if (!get_ops) { + pr_err("%s: failed to get the cfhsi_ops\n", __func__); return -ENODEV; } /* Assign the HSI device. */ - cfhsi->pdev = (*get_dev)(); - if (!cfhsi->pdev) { - pr_err("%s: failed to get the cfhsi device\n", __func__); + cfhsi->ops = (*get_ops)(); + if (!cfhsi->ops) { + pr_err("%s: failed to get the cfhsi_ops\n", __func__); goto err; } - /* Assign the HSI device. */ - cfhsi->dev = cfhsi->pdev->dev.platform_data; - /* Assign the driver to this HSI device. */ - cfhsi->dev->drv = &cfhsi->drv; - + cfhsi->ops->cb_ops = &cfhsi->cb_ops; if (register_netdevice(dev)) { - pr_warn("%s: device rtml registration failed\n", __func__); + pr_warn("%s: caif_hsi device registration failed\n", __func__); goto err; - } /* Add CAIF HSI device to list. */ list_add_tail(&cfhsi->list, &cfhsi_list); return 0; err: - symbol_put(cfhsi_get_device); + symbol_put(cfhsi_get_ops); return -ENODEV; } diff --git a/include/net/caif/caif_hsi.h b/include/net/caif/caif_hsi.h index a77b2bd7719a..6dc7dc2674b2 100644 --- a/include/net/caif/caif_hsi.h +++ b/include/net/caif/caif_hsi.h @@ -93,25 +93,25 @@ struct cfhsi_desc { #endif /* Structure implemented by the CAIF HSI driver. */ -struct cfhsi_drv { - void (*tx_done_cb) (struct cfhsi_drv *drv); - void (*rx_done_cb) (struct cfhsi_drv *drv); - void (*wake_up_cb) (struct cfhsi_drv *drv); - void (*wake_down_cb) (struct cfhsi_drv *drv); +struct cfhsi_cb_ops { + void (*tx_done_cb) (struct cfhsi_cb_ops *drv); + void (*rx_done_cb) (struct cfhsi_cb_ops *drv); + void (*wake_up_cb) (struct cfhsi_cb_ops *drv); + void (*wake_down_cb) (struct cfhsi_cb_ops *drv); }; /* Structure implemented by HSI device. */ -struct cfhsi_dev { - int (*cfhsi_up) (struct cfhsi_dev *dev); - int (*cfhsi_down) (struct cfhsi_dev *dev); - int (*cfhsi_tx) (u8 *ptr, int len, struct cfhsi_dev *dev); - int (*cfhsi_rx) (u8 *ptr, int len, struct cfhsi_dev *dev); - int (*cfhsi_wake_up) (struct cfhsi_dev *dev); - int (*cfhsi_wake_down) (struct cfhsi_dev *dev); - int (*cfhsi_get_peer_wake) (struct cfhsi_dev *dev, bool *status); - int (*cfhsi_fifo_occupancy)(struct cfhsi_dev *dev, size_t *occupancy); - int (*cfhsi_rx_cancel)(struct cfhsi_dev *dev); - struct cfhsi_drv *drv; +struct cfhsi_ops { + int (*cfhsi_up) (struct cfhsi_ops *dev); + int (*cfhsi_down) (struct cfhsi_ops *dev); + int (*cfhsi_tx) (u8 *ptr, int len, struct cfhsi_ops *dev); + int (*cfhsi_rx) (u8 *ptr, int len, struct cfhsi_ops *dev); + int (*cfhsi_wake_up) (struct cfhsi_ops *dev); + int (*cfhsi_wake_down) (struct cfhsi_ops *dev); + int (*cfhsi_get_peer_wake) (struct cfhsi_ops *dev, bool *status); + int (*cfhsi_fifo_occupancy) (struct cfhsi_ops *dev, size_t *occupancy); + int (*cfhsi_rx_cancel)(struct cfhsi_ops *dev); + struct cfhsi_cb_ops *cb_ops; }; /* Structure holds status of received CAIF frames processing */ @@ -138,8 +138,8 @@ struct cfhsi { struct net_device *ndev; struct platform_device *pdev; struct sk_buff_head qhead[CFHSI_PRIO_LAST]; - struct cfhsi_drv drv; - struct cfhsi_dev *dev; + struct cfhsi_cb_ops cb_ops; + struct cfhsi_ops *ops; int tx_state; struct cfhsi_rx_state rx_state; unsigned long inactivity_timeout; @@ -190,6 +190,6 @@ enum ifla_caif_hsi { __IFLA_CAIF_HSI_MAX }; -extern struct platform_device *cfhsi_get_device(void); +extern struct cfhsi_ops *cfhsi_get_ops(void); #endif /* CAIF_HSI_H_ */ -- cgit v1.2.3 From 91fa0cbc0c47930f771bf5425109956cc99c6b0b Mon Sep 17 00:00:00 2001 From: Sjur Brændeland Date: Mon, 25 Jun 2012 07:49:43 +0000 Subject: caif-hsi: Remove use of module parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove use of module parameters on caif hsi device, as rtnl configuration parameters are already supported. All caif hsi configuration data is put in cfhsi_config, and default values in hsi_default_config. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 151 +++++++++++++++++++++----------------------- include/net/caif/caif_hsi.h | 14 ++-- 2 files changed, 82 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index 0927c108bd14..1c2bd01e1592 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -32,51 +32,39 @@ MODULE_DESCRIPTION("CAIF HSI driver"); #define PAD_POW2(x, pow) ((((x)&((pow)-1)) == 0) ? 0 :\ (((pow)-((x)&((pow)-1))))) -static int inactivity_timeout = 1000; -module_param(inactivity_timeout, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(inactivity_timeout, "Inactivity timeout on HSI, ms."); +static const struct cfhsi_config hsi_default_config = { -static int aggregation_timeout = 1; -module_param(aggregation_timeout, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(aggregation_timeout, "Aggregation timeout on HSI, ms."); + /* Inactivity timeout on HSI, ms */ + .inactivity_timeout = HZ, -/* - * HSI padding options. - * Warning: must be a base of 2 (& operation used) and can not be zero ! - */ -static int hsi_head_align = 4; -module_param(hsi_head_align, int, S_IRUGO); -MODULE_PARM_DESC(hsi_head_align, "HSI head alignment."); + /* Aggregation timeout (ms) of zero means no aggregation is done*/ + .aggregation_timeout = 1, -static int hsi_tail_align = 4; -module_param(hsi_tail_align, int, S_IRUGO); -MODULE_PARM_DESC(hsi_tail_align, "HSI tail alignment."); - -/* - * HSI link layer flowcontrol thresholds. - * Warning: A high threshold value migth increase throughput but it will at - * the same time prevent channel prioritization and increase the risk of - * flooding the modem. The high threshold should be above the low. - */ -static int hsi_high_threshold = 100; -module_param(hsi_high_threshold, int, S_IRUGO); -MODULE_PARM_DESC(hsi_high_threshold, "HSI high threshold (FLOW OFF)."); + /* + * HSI link layer flow-control thresholds. + * Threshold values for the HSI packet queue. Flow-control will be + * asserted when the number of packets exceeds q_high_mark. It will + * not be de-asserted before the number of packets drops below + * q_low_mark. + * Warning: A high threshold value might increase throughput but it + * will at the same time prevent channel prioritization and increase + * the risk of flooding the modem. The high threshold should be above + * the low. + */ + .q_high_mark = 100, + .q_low_mark = 50, -static int hsi_low_threshold = 50; -module_param(hsi_low_threshold, int, S_IRUGO); -MODULE_PARM_DESC(hsi_low_threshold, "HSI high threshold (FLOW ON)."); + /* + * HSI padding options. + * Warning: must be a base of 2 (& operation used) and can not be zero ! + */ + .head_align = 4, + .tail_align = 4, +}; #define ON 1 #define OFF 0 -/* - * Threshold values for the HSI packet queue. Flowcontrol will be asserted - * when the number of packets exceeds HIGH_WATER_MARK. It will not be - * de-asserted before the number of packets drops below LOW_WATER_MARK. - */ -#define LOW_WATER_MARK hsi_low_threshold -#define HIGH_WATER_MARK hsi_high_threshold - static LIST_HEAD(cfhsi_list); static void cfhsi_inactivity_tout(unsigned long arg) @@ -99,8 +87,8 @@ static void cfhsi_update_aggregation_stats(struct cfhsi *cfhsi, int hpad, tpad, len; info = (struct caif_payload_info *)&skb->cb; - hpad = 1 + PAD_POW2((info->hdr_len + 1), hsi_head_align); - tpad = PAD_POW2((skb->len + hpad), hsi_tail_align); + hpad = 1 + PAD_POW2((info->hdr_len + 1), cfhsi->cfg.head_align); + tpad = PAD_POW2((skb->len + hpad), cfhsi->cfg.tail_align); len = skb->len + hpad + tpad; if (direction > 0) @@ -113,7 +101,7 @@ static bool cfhsi_can_send_aggregate(struct cfhsi *cfhsi) { int i; - if (cfhsi->aggregation_timeout == 0) + if (cfhsi->cfg.aggregation_timeout == 0) return true; for (i = 0; i < CFHSI_PRIO_BEBK; ++i) { @@ -169,7 +157,7 @@ static void cfhsi_abort_tx(struct cfhsi *cfhsi) cfhsi->tx_state = CFHSI_TX_STATE_IDLE; if (!test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) mod_timer(&cfhsi->inactivity_timer, - jiffies + cfhsi->inactivity_timeout); + jiffies + cfhsi->cfg.inactivity_timeout); spin_unlock_bh(&cfhsi->lock); } @@ -250,8 +238,8 @@ static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi) /* Calculate needed head alignment and tail alignment. */ info = (struct caif_payload_info *)&skb->cb; - hpad = 1 + PAD_POW2((info->hdr_len + 1), hsi_head_align); - tpad = PAD_POW2((skb->len + hpad), hsi_tail_align); + hpad = 1 + PAD_POW2((info->hdr_len + 1), cfhsi->cfg.head_align); + tpad = PAD_POW2((skb->len + hpad), cfhsi->cfg.tail_align); /* Check if frame still fits with added alignment. */ if ((skb->len + hpad + tpad) <= CFHSI_MAX_EMB_FRM_SZ) { @@ -292,8 +280,8 @@ static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi) /* Calculate needed head alignment and tail alignment. */ info = (struct caif_payload_info *)&skb->cb; - hpad = 1 + PAD_POW2((info->hdr_len + 1), hsi_head_align); - tpad = PAD_POW2((skb->len + hpad), hsi_tail_align); + hpad = 1 + PAD_POW2((info->hdr_len + 1), cfhsi->cfg.head_align); + tpad = PAD_POW2((skb->len + hpad), cfhsi->cfg.tail_align); /* Fill in CAIF frame length in descriptor. */ desc->cffrm_len[nfrms] = hpad + skb->len + tpad; @@ -364,7 +352,7 @@ static void cfhsi_start_tx(struct cfhsi *cfhsi) cfhsi->tx_state = CFHSI_TX_STATE_IDLE; /* Start inactivity timer. */ mod_timer(&cfhsi->inactivity_timer, - jiffies + cfhsi->inactivity_timeout); + jiffies + cfhsi->cfg.inactivity_timeout); spin_unlock_bh(&cfhsi->lock); break; } @@ -390,7 +378,7 @@ static void cfhsi_tx_done(struct cfhsi *cfhsi) */ spin_lock_bh(&cfhsi->lock); if (cfhsi->flow_off_sent && - cfhsi_tx_queue_len(cfhsi) <= cfhsi->q_low_mark && + cfhsi_tx_queue_len(cfhsi) <= cfhsi->cfg.q_low_mark && cfhsi->cfdev.flowctrl) { cfhsi->flow_off_sent = 0; @@ -402,7 +390,7 @@ static void cfhsi_tx_done(struct cfhsi *cfhsi) cfhsi_start_tx(cfhsi); } else { mod_timer(&cfhsi->aggregation_timer, - jiffies + cfhsi->aggregation_timeout); + jiffies + cfhsi->cfg.aggregation_timeout); spin_unlock_bh(&cfhsi->lock); } @@ -645,7 +633,7 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) /* Update inactivity timer if pending. */ spin_lock_bh(&cfhsi->lock); mod_timer_pending(&cfhsi->inactivity_timer, - jiffies + cfhsi->inactivity_timeout); + jiffies + cfhsi->cfg.inactivity_timeout); spin_unlock_bh(&cfhsi->lock); if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) { @@ -880,7 +868,7 @@ wake_ack: __func__); /* Start inactivity timer. */ mod_timer(&cfhsi->inactivity_timer, - jiffies + cfhsi->inactivity_timeout); + jiffies + cfhsi->cfg.inactivity_timeout); spin_unlock_bh(&cfhsi->lock); return; } @@ -1071,7 +1059,7 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev) /* Send flow off if number of packets is above high water mark. */ if (!cfhsi->flow_off_sent && - cfhsi_tx_queue_len(cfhsi) > cfhsi->q_high_mark && + cfhsi_tx_queue_len(cfhsi) > cfhsi->cfg.q_high_mark && cfhsi->cfdev.flowctrl) { cfhsi->flow_off_sent = 1; cfhsi->cfdev.flowctrl(cfhsi->ndev, OFF); @@ -1143,6 +1131,7 @@ static void cfhsi_setup(struct net_device *dev) cfhsi->cfdev.use_stx = false; cfhsi->cfdev.use_fcs = false; cfhsi->ndev = dev; + cfhsi->cfg = hsi_default_config; } static int cfhsi_open(struct net_device *ndev) @@ -1158,9 +1147,6 @@ static int cfhsi_open(struct net_device *ndev) /* Set flow info */ cfhsi->flow_off_sent = 0; - cfhsi->q_low_mark = LOW_WATER_MARK; - cfhsi->q_high_mark = HIGH_WATER_MARK; - /* * Allocate a TX buffer with the size of a HSI packet descriptors @@ -1188,20 +1174,8 @@ static int cfhsi_open(struct net_device *ndev) goto err_alloc_rx_flip; } - /* Pre-calculate inactivity timeout. */ - if (inactivity_timeout != -1) { - cfhsi->inactivity_timeout = - inactivity_timeout * HZ / 1000; - if (!cfhsi->inactivity_timeout) - cfhsi->inactivity_timeout = 1; - else if (cfhsi->inactivity_timeout > NEXT_TIMER_MAX_DELTA) - cfhsi->inactivity_timeout = NEXT_TIMER_MAX_DELTA; - } else { - cfhsi->inactivity_timeout = NEXT_TIMER_MAX_DELTA; - } - /* Initialize aggregation timeout */ - cfhsi->aggregation_timeout = aggregation_timeout; + cfhsi->cfg.aggregation_timeout = hsi_default_config.aggregation_timeout; /* Initialize recieve vaiables. */ cfhsi->rx_ptr = cfhsi->rx_buf; @@ -1350,24 +1324,39 @@ static void cfhsi_netlink_parms(struct nlattr *data[], struct cfhsi *cfhsi) } i = __IFLA_CAIF_HSI_INACTIVITY_TOUT; - if (data[i]) - inactivity_timeout = nla_get_u32(data[i]); + /* + * Inactivity timeout in millisecs. Lowest possible value is 1, + * and highest possible is NEXT_TIMER_MAX_DELTA. + */ + if (data[i]) { + u32 inactivity_timeout = nla_get_u32(data[i]); + /* Pre-calculate inactivity timeout. */ + cfhsi->cfg.inactivity_timeout = inactivity_timeout * HZ / 1000; + if (cfhsi->cfg.inactivity_timeout == 0) + cfhsi->cfg.inactivity_timeout = 1; + else if (cfhsi->cfg.inactivity_timeout > NEXT_TIMER_MAX_DELTA) + cfhsi->cfg.inactivity_timeout = NEXT_TIMER_MAX_DELTA; + } i = __IFLA_CAIF_HSI_AGGREGATION_TOUT; if (data[i]) - aggregation_timeout = nla_get_u32(data[i]); + cfhsi->cfg.aggregation_timeout = nla_get_u32(data[i]); i = __IFLA_CAIF_HSI_HEAD_ALIGN; if (data[i]) - hsi_head_align = nla_get_u32(data[i]); + cfhsi->cfg.head_align = nla_get_u32(data[i]); i = __IFLA_CAIF_HSI_TAIL_ALIGN; if (data[i]) - hsi_tail_align = nla_get_u32(data[i]); + cfhsi->cfg.tail_align = nla_get_u32(data[i]); i = __IFLA_CAIF_HSI_QHIGH_WATERMARK; if (data[i]) - hsi_high_threshold = nla_get_u32(data[i]); + cfhsi->cfg.q_high_mark = nla_get_u32(data[i]); + + i = __IFLA_CAIF_HSI_QLOW_WATERMARK; + if (data[i]) + cfhsi->cfg.q_low_mark = nla_get_u32(data[i]); } static int caif_hsi_changelink(struct net_device *dev, struct nlattr *tb[], @@ -1398,16 +1387,20 @@ static size_t caif_hsi_get_size(const struct net_device *dev) static int caif_hsi_fill_info(struct sk_buff *skb, const struct net_device *dev) { + struct cfhsi *cfhsi = netdev_priv(dev); + if (nla_put_u32(skb, __IFLA_CAIF_HSI_INACTIVITY_TOUT, - inactivity_timeout) || + cfhsi->cfg.inactivity_timeout) || nla_put_u32(skb, __IFLA_CAIF_HSI_AGGREGATION_TOUT, - aggregation_timeout) || - nla_put_u32(skb, __IFLA_CAIF_HSI_HEAD_ALIGN, hsi_head_align) || - nla_put_u32(skb, __IFLA_CAIF_HSI_TAIL_ALIGN, hsi_tail_align) || + cfhsi->cfg.aggregation_timeout) || + nla_put_u32(skb, __IFLA_CAIF_HSI_HEAD_ALIGN, + cfhsi->cfg.head_align) || + nla_put_u32(skb, __IFLA_CAIF_HSI_TAIL_ALIGN, + cfhsi->cfg.tail_align) || nla_put_u32(skb, __IFLA_CAIF_HSI_QHIGH_WATERMARK, - hsi_high_threshold) || + cfhsi->cfg.q_high_mark) || nla_put_u32(skb, __IFLA_CAIF_HSI_QLOW_WATERMARK, - hsi_low_threshold)) + cfhsi->cfg.q_low_mark)) return -EMSGSIZE; return 0; diff --git a/include/net/caif/caif_hsi.h b/include/net/caif/caif_hsi.h index 6dc7dc2674b2..bcb9cc3ce98b 100644 --- a/include/net/caif/caif_hsi.h +++ b/include/net/caif/caif_hsi.h @@ -132,6 +132,15 @@ enum { CFHSI_PRIO_LAST, }; +struct cfhsi_config { + u32 inactivity_timeout; + u32 aggregation_timeout; + u32 head_align; + u32 tail_align; + u32 q_high_mark; + u32 q_low_mark; +}; + /* Structure implemented by CAIF HSI drivers. */ struct cfhsi { struct caif_dev_common cfdev; @@ -142,7 +151,7 @@ struct cfhsi { struct cfhsi_ops *ops; int tx_state; struct cfhsi_rx_state rx_state; - unsigned long inactivity_timeout; + struct cfhsi_config cfg; int rx_len; u8 *rx_ptr; u8 *tx_buf; @@ -150,8 +159,6 @@ struct cfhsi { u8 *rx_flip_buf; spinlock_t lock; int flow_off_sent; - u32 q_low_mark; - u32 q_high_mark; struct list_head list; struct work_struct wake_up_work; struct work_struct wake_down_work; @@ -164,7 +171,6 @@ struct cfhsi { struct timer_list rx_slowpath_timer; /* TX aggregation */ - unsigned long aggregation_timeout; int aggregation_len; struct timer_list aggregation_timer; -- cgit v1.2.3 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') 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 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') 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 From a051661ca6d134c18599498b185b667859d4339b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 26 Jun 2012 15:05:44 -0700 Subject: blkcg: implement per-blkg request allocation Currently, request_queue has one request_list to allocate requests from regardless of blkcg of the IO being issued. When the unified request pool is used up, cfq proportional IO limits become meaningless - whoever grabs the next request being freed wins the race regardless of the configured weights. This can be easily demonstrated by creating a blkio cgroup w/ very low weight, put a program which can issue a lot of random direct IOs there and running a sequential IO from a different cgroup. As soon as the request pool is used up, the sequential IO bandwidth crashes. This patch implements per-blkg request_list. Each blkg has its own request_list and any IO allocates its request from the matching blkg making blkcgs completely isolated in terms of request allocation. * Root blkcg uses the request_list embedded in each request_queue, which was renamed to @q->root_rl from @q->rq. While making blkcg rl handling a bit harier, this enables avoiding most overhead for root blkcg. * Queue fullness is properly per request_list but bdi isn't blkcg aware yet, so congestion state currently just follows the root blkcg. As writeback isn't aware of blkcg yet, this works okay for async congestion but readahead may get the wrong signals. It's better than blkcg completely collapsing with shared request_list but needs to be improved with future changes. * After this change, each block cgroup gets a full request pool making resource consumption of each cgroup higher. This makes allowing non-root users to create cgroups less desirable; however, note that allowing non-root users to directly manage cgroups is already severely broken regardless of this patch - each block cgroup consumes kernel memory and skews IO weight (IO weights are not hierarchical). v2: queue-sysfs.txt updated and patch description udpated as suggested by Vivek. v3: blk_get_rl() wasn't checking error return from blkg_lookup_create() and may cause oops on lookup failure. Fix it by falling back to root_rl on blkg lookup failures. This problem was spotted by Rakesh Iyer . v4: Updated to accomodate 458f27a982 "block: Avoid missed wakeup in request waitqueue". blk_drain_queue() now wakes up waiters on all blkg->rl on the target queue. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal Cc: Wu Fengguang Signed-off-by: Jens Axboe --- Documentation/block/queue-sysfs.txt | 7 +++ block/blk-cgroup.c | 51 ++++++++++++++++-- block/blk-cgroup.h | 102 ++++++++++++++++++++++++++++++++++++ block/blk-core.c | 42 +++++++++++---- block/blk-sysfs.c | 32 ++++++----- include/linux/blkdev.h | 12 +++-- 6 files changed, 216 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/Documentation/block/queue-sysfs.txt b/Documentation/block/queue-sysfs.txt index d8147b336c35..6518a55273e7 100644 --- a/Documentation/block/queue-sysfs.txt +++ b/Documentation/block/queue-sysfs.txt @@ -38,6 +38,13 @@ read or write requests. Note that the total allocated number may be twice this amount, since it applies only to reads or writes (not the accumulated sum). +To avoid priority inversion through request starvation, a request +queue maintains a separate request pool per each cgroup when +CONFIG_BLK_CGROUP is enabled, and this parameter applies to each such +per-block-cgroup request pool. IOW, if there are N block cgroups, +each request queue may have upto N request pools, each independently +regulated by nr_requests. + read_ahead_kb (RW) ------------------ Maximum number of kilobytes to read-ahead for filesystems on this block diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 63b31ebae6e2..f3b44a65fc7a 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -63,6 +63,7 @@ static void blkg_free(struct blkcg_gq *blkg) kfree(pd); } + blk_exit_rl(&blkg->rl); kfree(blkg); } @@ -90,6 +91,13 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q, blkg->blkcg = blkcg; blkg->refcnt = 1; + /* root blkg uses @q->root_rl, init rl only for !root blkgs */ + if (blkcg != &blkcg_root) { + if (blk_init_rl(&blkg->rl, q, gfp_mask)) + goto err_free; + blkg->rl.blkg = blkg; + } + for (i = 0; i < BLKCG_MAX_POLS; i++) { struct blkcg_policy *pol = blkcg_policy[i]; struct blkg_policy_data *pd; @@ -99,10 +107,8 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q, /* alloc per-policy data and attach it to blkg */ pd = kzalloc_node(pol->pd_size, gfp_mask, q->node); - if (!pd) { - blkg_free(blkg); - return NULL; - } + if (!pd) + goto err_free; blkg->pd[i] = pd; pd->blkg = blkg; @@ -113,6 +119,10 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q, } return blkg; + +err_free: + blkg_free(blkg); + return NULL; } static struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, @@ -300,6 +310,38 @@ void __blkg_release(struct blkcg_gq *blkg) } EXPORT_SYMBOL_GPL(__blkg_release); +/* + * The next function used by blk_queue_for_each_rl(). It's a bit tricky + * because the root blkg uses @q->root_rl instead of its own rl. + */ +struct request_list *__blk_queue_next_rl(struct request_list *rl, + struct request_queue *q) +{ + struct list_head *ent; + struct blkcg_gq *blkg; + + /* + * Determine the current blkg list_head. The first entry is + * root_rl which is off @q->blkg_list and mapped to the head. + */ + if (rl == &q->root_rl) { + ent = &q->blkg_list; + } else { + blkg = container_of(rl, struct blkcg_gq, rl); + ent = &blkg->q_node; + } + + /* walk to the next list_head, skip root blkcg */ + ent = ent->next; + if (ent == &q->root_blkg->q_node) + ent = ent->next; + if (ent == &q->blkg_list) + return NULL; + + blkg = container_of(ent, struct blkcg_gq, q_node); + return &blkg->rl; +} + static int blkcg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, u64 val) { @@ -750,6 +792,7 @@ int blkcg_activate_policy(struct request_queue *q, goto out_unlock; } q->root_blkg = blkg; + q->root_rl.blkg = blkg; list_for_each_entry(blkg, &q->blkg_list, q_node) cnt++; diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index e74cce1fbac9..24597309e23d 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -17,6 +17,7 @@ #include #include #include +#include /* Max limits for throttle policy */ #define THROTL_IOPS_MAX UINT_MAX @@ -93,6 +94,8 @@ struct blkcg_gq { struct list_head q_node; struct hlist_node blkcg_node; struct blkcg *blkcg; + /* request allocation list for this blkcg-q pair */ + struct request_list rl; /* reference count */ int refcnt; @@ -250,6 +253,95 @@ static inline void blkg_put(struct blkcg_gq *blkg) __blkg_release(blkg); } +/** + * blk_get_rl - get request_list to use + * @q: request_queue of interest + * @bio: bio which will be attached to the allocated request (may be %NULL) + * + * The caller wants to allocate a request from @q to use for @bio. Find + * the request_list to use and obtain a reference on it. Should be called + * under queue_lock. This function is guaranteed to return non-%NULL + * request_list. + */ +static inline struct request_list *blk_get_rl(struct request_queue *q, + struct bio *bio) +{ + struct blkcg *blkcg; + struct blkcg_gq *blkg; + + rcu_read_lock(); + + blkcg = bio_blkcg(bio); + + /* bypass blkg lookup and use @q->root_rl directly for root */ + if (blkcg == &blkcg_root) + goto root_rl; + + /* + * Try to use blkg->rl. blkg lookup may fail under memory pressure + * or if either the blkcg or queue is going away. Fall back to + * root_rl in such cases. + */ + blkg = blkg_lookup_create(blkcg, q); + if (unlikely(IS_ERR(blkg))) + goto root_rl; + + blkg_get(blkg); + rcu_read_unlock(); + return &blkg->rl; +root_rl: + rcu_read_unlock(); + return &q->root_rl; +} + +/** + * blk_put_rl - put request_list + * @rl: request_list to put + * + * Put the reference acquired by blk_get_rl(). Should be called under + * queue_lock. + */ +static inline void blk_put_rl(struct request_list *rl) +{ + /* root_rl may not have blkg set */ + if (rl->blkg && rl->blkg->blkcg != &blkcg_root) + blkg_put(rl->blkg); +} + +/** + * blk_rq_set_rl - associate a request with a request_list + * @rq: request of interest + * @rl: target request_list + * + * Associate @rq with @rl so that accounting and freeing can know the + * request_list @rq came from. + */ +static inline void blk_rq_set_rl(struct request *rq, struct request_list *rl) +{ + rq->rl = rl; +} + +/** + * blk_rq_rl - return the request_list a request came from + * @rq: request of interest + * + * Return the request_list @rq is allocated from. + */ +static inline struct request_list *blk_rq_rl(struct request *rq) +{ + return rq->rl; +} + +struct request_list *__blk_queue_next_rl(struct request_list *rl, + struct request_queue *q); +/** + * blk_queue_for_each_rl - iterate through all request_lists of a request_queue + * + * Should be used under queue_lock. + */ +#define blk_queue_for_each_rl(rl, q) \ + for ((rl) = &(q)->root_rl; (rl); (rl) = __blk_queue_next_rl((rl), (q))) + /** * blkg_stat_add - add a value to a blkg_stat * @stat: target blkg_stat @@ -392,6 +484,7 @@ static inline void blkcg_deactivate_policy(struct request_queue *q, static inline struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup) { return NULL; } static inline struct blkcg *bio_blkcg(struct bio *bio) { return NULL; } + static inline struct blkg_policy_data *blkg_to_pd(struct blkcg_gq *blkg, struct blkcg_policy *pol) { return NULL; } static inline struct blkcg_gq *pd_to_blkg(struct blkg_policy_data *pd) { return NULL; } @@ -399,5 +492,14 @@ static inline char *blkg_path(struct blkcg_gq *blkg) { return NULL; } static inline void blkg_get(struct blkcg_gq *blkg) { } static inline void blkg_put(struct blkcg_gq *blkg) { } +static inline struct request_list *blk_get_rl(struct request_queue *q, + struct bio *bio) { return &q->root_rl; } +static inline void blk_put_rl(struct request_list *rl) { } +static inline void blk_rq_set_rl(struct request *rq, struct request_list *rl) { } +static inline struct request_list *blk_rq_rl(struct request *rq) { return &rq->q->root_rl; } + +#define blk_queue_for_each_rl(rl, q) \ + for ((rl) = &(q)->root_rl; (rl); (rl) = NULL) + #endif /* CONFIG_BLK_CGROUP */ #endif /* _BLK_CGROUP_H */ diff --git a/block/blk-core.c b/block/blk-core.c index f392a2edf462..dd134d834d58 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -416,9 +416,14 @@ void blk_drain_queue(struct request_queue *q, bool drain_all) * left with hung waiters. We need to wake up those waiters. */ if (q->request_fn) { + struct request_list *rl; + spin_lock_irq(q->queue_lock); - for (i = 0; i < ARRAY_SIZE(q->rq.wait); i++) - wake_up_all(&q->rq.wait[i]); + + blk_queue_for_each_rl(rl, q) + for (i = 0; i < ARRAY_SIZE(rl->wait); i++) + wake_up_all(&rl->wait[i]); + spin_unlock_irq(q->queue_lock); } } @@ -685,7 +690,7 @@ blk_init_allocated_queue(struct request_queue *q, request_fn_proc *rfn, if (!q) return NULL; - if (blk_init_rl(&q->rq, q, GFP_KERNEL)) + if (blk_init_rl(&q->root_rl, q, GFP_KERNEL)) return NULL; q->request_fn = rfn; @@ -776,7 +781,12 @@ static void __freed_request(struct request_list *rl, int sync) { struct request_queue *q = rl->q; - if (rl->count[sync] < queue_congestion_off_threshold(q)) + /* + * bdi isn't aware of blkcg yet. As all async IOs end up root + * blkcg anyway, just use root blkcg state. + */ + if (rl == &q->root_rl && + rl->count[sync] < queue_congestion_off_threshold(q)) blk_clear_queue_congested(q, sync); if (rl->count[sync] + 1 <= q->nr_requests) { @@ -897,7 +907,12 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, } } } - blk_set_queue_congested(q, is_sync); + /* + * bdi isn't aware of blkcg yet. As all async IOs end up + * root blkcg anyway, just use root blkcg state. + */ + if (rl == &q->root_rl) + blk_set_queue_congested(q, is_sync); } /* @@ -939,6 +954,7 @@ static struct request *__get_request(struct request_list *rl, int rw_flags, goto fail_alloc; blk_rq_init(q, rq); + blk_rq_set_rl(rq, rl); rq->cmd_flags = rw_flags | REQ_ALLOCED; /* init elvpriv */ @@ -1032,15 +1048,19 @@ static struct request *get_request(struct request_queue *q, int rw_flags, { const bool is_sync = rw_is_sync(rw_flags) != 0; DEFINE_WAIT(wait); - struct request_list *rl = &q->rq; + struct request_list *rl; struct request *rq; + + rl = blk_get_rl(q, bio); /* transferred to @rq on success */ retry: - rq = __get_request(&q->rq, rw_flags, bio, gfp_mask); + rq = __get_request(rl, rw_flags, bio, gfp_mask); if (rq) return rq; - if (!(gfp_mask & __GFP_WAIT) || unlikely(blk_queue_dead(q))) + if (!(gfp_mask & __GFP_WAIT) || unlikely(blk_queue_dead(q))) { + blk_put_rl(rl); return NULL; + } /* wait on @rl and retry */ prepare_to_wait_exclusive(&rl->wait[is_sync], &wait, @@ -1231,12 +1251,14 @@ void __blk_put_request(struct request_queue *q, struct request *req) */ if (req->cmd_flags & REQ_ALLOCED) { unsigned int flags = req->cmd_flags; + struct request_list *rl = blk_rq_rl(req); BUG_ON(!list_empty(&req->queuelist)); BUG_ON(!hlist_unhashed(&req->hash)); - blk_free_request(&q->rq, req); - freed_request(&q->rq, flags); + blk_free_request(rl, req); + freed_request(rl, flags); + blk_put_rl(rl); } } EXPORT_SYMBOL_GPL(__blk_put_request); diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 234ce7c082fa..9628b291f960 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -40,7 +40,7 @@ static ssize_t queue_requests_show(struct request_queue *q, char *page) static ssize_t queue_requests_store(struct request_queue *q, const char *page, size_t count) { - struct request_list *rl = &q->rq; + struct request_list *rl; unsigned long nr; int ret; @@ -55,6 +55,9 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count) q->nr_requests = nr; blk_queue_congestion_threshold(q); + /* congestion isn't cgroup aware and follows root blkcg for now */ + rl = &q->root_rl; + if (rl->count[BLK_RW_SYNC] >= queue_congestion_on_threshold(q)) blk_set_queue_congested(q, BLK_RW_SYNC); else if (rl->count[BLK_RW_SYNC] < queue_congestion_off_threshold(q)) @@ -65,19 +68,22 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count) else if (rl->count[BLK_RW_ASYNC] < queue_congestion_off_threshold(q)) blk_clear_queue_congested(q, BLK_RW_ASYNC); - if (rl->count[BLK_RW_SYNC] >= q->nr_requests) { - blk_set_rl_full(rl, BLK_RW_SYNC); - } else { - blk_clear_rl_full(rl, BLK_RW_SYNC); - wake_up(&rl->wait[BLK_RW_SYNC]); + blk_queue_for_each_rl(rl, q) { + if (rl->count[BLK_RW_SYNC] >= q->nr_requests) { + blk_set_rl_full(rl, BLK_RW_SYNC); + } else { + blk_clear_rl_full(rl, BLK_RW_SYNC); + wake_up(&rl->wait[BLK_RW_SYNC]); + } + + if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) { + blk_set_rl_full(rl, BLK_RW_ASYNC); + } else { + blk_clear_rl_full(rl, BLK_RW_ASYNC); + wake_up(&rl->wait[BLK_RW_ASYNC]); + } } - if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) { - blk_set_rl_full(rl, BLK_RW_ASYNC); - } else { - blk_clear_rl_full(rl, BLK_RW_ASYNC); - wake_up(&rl->wait[BLK_RW_ASYNC]); - } spin_unlock_irq(q->queue_lock); return ret; } @@ -488,7 +494,7 @@ static void blk_release_queue(struct kobject *kobj) elevator_exit(q->elevator); } - blk_exit_rl(&q->rq); + blk_exit_rl(&q->root_rl); if (q->queue_tags) __blk_queue_free_tags(q); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index f2385ee7c7b2..3816ce8a08fc 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -51,7 +51,9 @@ typedef void (rq_end_io_fn)(struct request *, int); struct request_list { struct request_queue *q; /* the queue this rl belongs to */ - +#ifdef CONFIG_BLK_CGROUP + struct blkcg_gq *blkg; /* blkg this request pool belongs to */ +#endif /* * count[], starved[], and wait[] are indexed by * BLK_RW_SYNC/BLK_RW_ASYNC @@ -143,6 +145,7 @@ struct request { struct hd_struct *part; unsigned long start_time; #ifdef CONFIG_BLK_CGROUP + struct request_list *rl; /* rl this rq is alloced from */ unsigned long long start_time_ns; unsigned long long io_start_time_ns; /* when passed to hardware */ #endif @@ -291,9 +294,12 @@ struct request_queue { int nr_rqs_elvpriv; /* # allocated rqs w/ elvpriv */ /* - * the queue request freelist, one for reads and one for writes + * If blkcg is not used, @q->root_rl serves all requests. If blkcg + * is used, root blkg allocates from @q->root_rl and all other + * blkgs from their own blkg->rl. Which one to use should be + * determined using bio_request_list(). */ - struct request_list rq; + struct request_list root_rl; request_fn_proc *request_fn; make_request_fn *make_request_fn; -- cgit v1.2.3 From a46af4ebf9ffec35eea0390e89935197b833dc61 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 25 Jun 2012 12:19:03 -0400 Subject: USB: EHCI: define extension registers like normal ones This patch (as1562) cleans up the definitions of the EHCI extended registers to be consistent with the definitions of the standard registers. This makes the code look a lot nicer, with no functional change. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 15 +++++---------- drivers/usb/host/ehci-hub.c | 26 ++++++++------------------ include/linux/usb/ehci_def.h | 28 +++++++++++++++++++++------- 3 files changed, 34 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 800be38c78b4..c49fc1e7895d 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -203,11 +203,9 @@ static int handshake (struct ehci_hcd *ehci, void __iomem *ptr, /* check TDI/ARC silicon is in host mode */ static int tdi_in_host_mode (struct ehci_hcd *ehci) { - u32 __iomem *reg_ptr; u32 tmp; - reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE); - tmp = ehci_readl(ehci, reg_ptr); + tmp = ehci_readl(ehci, &ehci->regs->usbmode); return (tmp & 3) == USBMODE_CM_HC; } @@ -303,11 +301,9 @@ static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr, /* put TDI/ARC silicon into EHCI mode */ static void tdi_reset (struct ehci_hcd *ehci) { - u32 __iomem *reg_ptr; u32 tmp; - reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE); - tmp = ehci_readl(ehci, reg_ptr); + tmp = ehci_readl(ehci, &ehci->regs->usbmode); tmp |= USBMODE_CM_HC; /* The default byte access to MMR space is LE after * controller reset. Set the required endian mode @@ -315,7 +311,7 @@ static void tdi_reset (struct ehci_hcd *ehci) */ if (ehci_big_endian_mmio(ehci)) tmp |= USBMODE_BE; - ehci_writel(ehci, tmp, reg_ptr); + ehci_writel(ehci, tmp, &ehci->regs->usbmode); } /* reset a non-running (STS_HALT == 1) controller */ @@ -339,9 +335,8 @@ static int ehci_reset (struct ehci_hcd *ehci) if (ehci->has_hostpc) { ehci_writel(ehci, USBMODE_EX_HC | USBMODE_EX_VBPS, - (u32 __iomem *)(((u8 *)ehci->regs) + USBMODE_EX)); - ehci_writel(ehci, TXFIFO_DEFAULT, - (u32 __iomem *)(((u8 *)ehci->regs) + TXFILLTUNING)); + &ehci->regs->usbmode_ex); + ehci_writel(ehci, TXFIFO_DEFAULT, &ehci->regs->txfill_tuning); } if (retval) return retval; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index dd5eef6af6df..db05e358677a 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -149,10 +149,8 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, if (ehci->has_hostpc) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * port); temp = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg); } @@ -185,10 +183,8 @@ static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, if (ehci->has_hostpc) { port = HCS_N_PORTS(ehci->hcs_params); while (port--) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * port); temp = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg); } @@ -285,11 +281,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) port = HCS_N_PORTS(ehci->hcs_params); while (port--) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port]; u32 t3; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * port); t3 = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg); t3 = ehci_readl(ehci, hostpc_reg); @@ -388,10 +382,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd) i = HCS_N_PORTS(ehci->hcs_params); while (i--) { if (test_bit(i, &ehci->bus_suspended)) { - u32 __iomem *hostpc_reg; + u32 __iomem *hostpc_reg = + &ehci->regs->hostpc[i]; - hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs - + HOSTPC0 + 4 * i); temp = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg); @@ -667,7 +660,7 @@ static int ehci_hub_control ( int ports = HCS_N_PORTS (ehci->hcs_params); u32 __iomem *status_reg = &ehci->regs->port_status[ (wIndex & 0xff) - 1]; - u32 __iomem *hostpc_reg = NULL; + u32 __iomem *hostpc_reg = &ehci->regs->hostpc[(wIndex & 0xff) - 1]; u32 temp, temp1, status; unsigned long flags; int retval = 0; @@ -680,9 +673,6 @@ static int ehci_hub_control ( * power, "this is the one", etc. EHCI spec supports this. */ - if (ehci->has_hostpc) - hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs - + HOSTPC0 + 4 * ((wIndex & 0xff) - 1)); spin_lock_irqsave (&ehci->lock, flags); switch (typeReq) { case ClearHubFeature: @@ -734,7 +724,7 @@ static int ehci_hub_control ( goto error; /* clear phy low-power mode before resume */ - if (hostpc_reg) { + if (ehci->has_hostpc) { temp1 = ehci_readl(ehci, hostpc_reg); ehci_writel(ehci, temp1 & ~HOSTPC_PHCD, hostpc_reg); @@ -984,7 +974,7 @@ static int ehci_hub_control ( temp &= ~PORT_WKCONN_E; temp |= PORT_WKDISC_E | PORT_WKOC_E; ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); - if (hostpc_reg) { + if (ehci->has_hostpc) { spin_unlock_irqrestore(&ehci->lock, flags); msleep(5);/* 5ms for HCD enter low pwr mode */ spin_lock_irqsave(&ehci->lock, flags); diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index 7cc95ee3606b..de4b9ed5d5dd 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -111,7 +111,13 @@ struct ehci_regs { /* ASYNCLISTADDR: offset 0x18 */ u32 async_next; /* address of next async queue head */ - u32 reserved[9]; + u32 reserved1[2]; + + /* TXFILLTUNING: offset 0x24 */ + u32 txfill_tuning; /* TX FIFO Tuning register */ +#define TXFIFO_DEFAULT (8<<16) /* FIFO burst threshold 8 */ + + u32 reserved2[6]; /* CONFIGFLAG: offset 0x40 */ u32 configured_flag; @@ -155,26 +161,34 @@ struct ehci_regs { #define PORT_CSC (1<<1) /* connect status change */ #define PORT_CONNECT (1<<0) /* device connected */ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) -}; -#define USBMODE 0x68 /* USB Device mode */ + u32 reserved3[9]; + + /* USBMODE: offset 0x68 */ + u32 usbmode; /* USB Device mode */ #define USBMODE_SDIS (1<<3) /* Stream disable */ #define USBMODE_BE (1<<2) /* BE/LE endianness select */ #define USBMODE_CM_HC (3<<0) /* host controller mode */ #define USBMODE_CM_IDLE (0<<0) /* idle state */ + u32 reserved4[7]; + /* Moorestown has some non-standard registers, partially due to the fact that * its EHCI controller has both TT and LPM support. HOSTPCx are extensions to * PORTSCx */ -#define HOSTPC0 0x84 /* HOSTPC extension */ + /* HOSTPC: offset 0x84 */ + u32 hostpc[0]; /* HOSTPC extension */ #define HOSTPC_PHCD (1<<22) /* Phy clock disable */ #define HOSTPC_PSPD (3<<25) /* Port speed detection */ -#define USBMODE_EX 0xc8 /* USB Device mode extension */ + + u32 reserved5[17]; + + /* USBMODE_EX: offset 0xc8 */ + u32 usbmode_ex; /* USB Device mode extension */ #define USBMODE_EX_VBPS (1<<5) /* VBus Power Select On */ #define USBMODE_EX_HC (3<<0) /* host controller mode */ -#define TXFILLTUNING 0x24 /* TX FIFO Tuning register */ -#define TXFIFO_DEFAULT (8<<16) /* FIFO burst threshold 8 */ +}; /* Appendix C, Debug port ... intended for use with special "debug devices" * that can help if there's no serial console. (nonstandard enumeration.) -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 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') 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 From 38ced28b21efff18fd5e5c98a92830e8f0031cee Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Jun 2012 10:55:57 -0300 Subject: edac: allow specifying the error count with fake_inject In order to test if the error counters are properly incremented, add a way to specify how many errors were generated by a trace. Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_mc_sysfs.c | 15 +++++++++++++-- include/linux/edac.h | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index a2bf7e9dd6de..ed0bc07b8503 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -812,18 +812,24 @@ static ssize_t edac_fake_inject_write(struct file *file, struct device *dev = file->private_data; struct mem_ctl_info *mci = to_mci(dev); static enum hw_event_mc_err_type type; + u16 errcount = mci->fake_inject_count; + + if (!errcount) + errcount = 1; type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED : HW_EVENT_ERR_CORRECTED; printk(KERN_DEBUG - "Generating a %s fake error to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n", + "Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n", + errcount, (type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE", + errcount > 1 ? "s" : "", mci->fake_inject_layer[0], mci->fake_inject_layer[1], mci->fake_inject_layer[2] ); - edac_mc_handle_error(type, mci, 1, 0, 0, 0, + edac_mc_handle_error(type, mci, errcount, 0, 0, 0, mci->fake_inject_layer[0], mci->fake_inject_layer[1], mci->fake_inject_layer[2], @@ -944,6 +950,11 @@ int edac_create_debug_nodes(struct mem_ctl_info *mci) if (!d) goto nomem; + d = debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent, + &mci->fake_inject_count); + if (!d) + goto nomem; + d = debugfs_create_file("fake_inject", S_IWUSR, parent, &mci->dev, &debug_fake_inject_fops); diff --git a/include/linux/edac.h b/include/linux/edac.h index 6677af853e30..bab9f8473dc1 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -665,6 +665,7 @@ struct mem_ctl_info { struct dentry *debugfs; u8 fake_inject_layer[EDAC_MAX_LAYERS]; u32 fake_inject_ue; + u16 fake_inject_count; #endif }; -- cgit v1.2.3 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') 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 From dfb89c56add259b72a9c68d6b2846c1cd8c4e4b6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 27 Jun 2012 09:23:48 +0200 Subject: cfg80211: don't allow WoWLAN support without CONFIG_PM When CONFIG_PM is disabled, no device can possibly support WoWLAN since it can't go to sleep to start with. Due to this, mac80211 had even rejected the hardware registration. By making all the code and data for WoWLAN depend on CONFIG_PM we can promote this runtime error to a compile-time error. Add #ifdef around all WoWLAN code to remove it in systems that don't need it as they never suspend. Cc: Kalle Valo Acked-by: Luciano Coelho Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 ++ drivers/net/wireless/ti/wlcore/main.c | 2 ++ include/net/cfg80211.h | 2 ++ net/mac80211/main.c | 7 +++---- net/wireless/core.c | 4 ++++ net/wireless/nl80211.c | 6 ++++++ 6 files changed, 19 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index f27e9732951d..b8fce0d4d72e 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3487,6 +3487,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) wiphy->cipher_suites = cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); +#ifdef CONFIG_PM wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT | WIPHY_WOWLAN_GTK_REKEY_FAILURE | @@ -3496,6 +3497,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST; wiphy->wowlan.pattern_min_len = 1; wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE; +#endif wiphy->max_sched_scan_ssids = MAX_PROBED_SSID_INDEX; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 1156e3f578c1..747a997bc608 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5321,6 +5321,7 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev) goto out_free_hw; } +#ifdef CONFIG_PM ret = enable_irq_wake(wl->irq); if (!ret) { wl->irq_wake_enabled = true; @@ -5334,6 +5335,7 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev) WL1271_RX_FILTER_MAX_PATTERN_SIZE; } } +#endif disable_irq(wl->irq); ret = wl12xx_get_hw_info(wl); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 061c01957e54..7d3cd3ce9a26 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2153,7 +2153,9 @@ struct wiphy { char fw_version[ETHTOOL_BUSINFO_LEN]; u32 hw_version; +#ifdef CONFIG_PM struct wiphy_wowlan_support wowlan; +#endif u16 max_remain_on_channel_duration; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 0b040fb73673..aded0018f6f3 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -706,12 +706,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) local->hw.offchannel_tx_hw_queue >= local->hw.queues)) return -EINVAL; - if ((hw->wiphy->wowlan.flags || hw->wiphy->wowlan.n_patterns) #ifdef CONFIG_PM - && (!local->ops->suspend || !local->ops->resume) -#endif - ) + if ((hw->wiphy->wowlan.flags || hw->wiphy->wowlan.n_patterns) && + (!local->ops->suspend || !local->ops->resume)) return -EINVAL; +#endif if ((hw->flags & IEEE80211_HW_SCAN_WHILE_IDLE) && !local->ops->hw_scan) return -EINVAL; diff --git a/net/wireless/core.c b/net/wireless/core.c index 907f62c80e28..ddd32afa5f0a 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -421,9 +421,11 @@ int wiphy_register(struct wiphy *wiphy) int i; u16 ifmodes = wiphy->interface_modes; +#ifdef CONFIG_PM if (WARN_ON((wiphy->wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY))) return -EINVAL; +#endif if (WARN_ON(wiphy->ap_sme_capa && !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) @@ -500,12 +502,14 @@ int wiphy_register(struct wiphy *wiphy) return -EINVAL; } +#ifdef CONFIG_PM if (rdev->wiphy.wowlan.n_patterns) { if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len || rdev->wiphy.wowlan.pattern_min_len > rdev->wiphy.wowlan.pattern_max_len)) return -EINVAL; } +#endif /* check and set up bitrates */ ieee80211_set_bitrate_flags(wiphy); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 234ff3bbd104..067c9fe02a7f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1112,6 +1112,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, nla_nest_end(msg, nl_ifs); } +#ifdef CONFIG_PM if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) { struct nlattr *nl_wowlan; @@ -1152,6 +1153,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, nla_nest_end(msg, nl_wowlan); } +#endif if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, dev->wiphy.software_iftypes)) @@ -6276,6 +6278,7 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info) return cfg80211_leave_mesh(rdev, dev); } +#ifdef CONFIG_PM static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -6504,6 +6507,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) kfree(new_triggers.patterns); return err; } +#endif static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) { @@ -7158,6 +7162,7 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, +#ifdef CONFIG_PM { .cmd = NL80211_CMD_GET_WOWLAN, .doit = nl80211_get_wowlan, @@ -7174,6 +7179,7 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_WIPHY | NL80211_FLAG_NEED_RTNL, }, +#endif { .cmd = NL80211_CMD_SET_REKEY_OFFLOAD, .doit = nl80211_set_rekey_data, -- cgit v1.2.3 From f1caad274515ffd9841ac57ce9a7b5fc35bbf689 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Thu, 21 Jun 2012 04:36:39 +0000 Subject: netfilter: nf_conntrack: prepare l4proto->init_net cleanup l4proto->init contain quite redundant code. We can simplify this by adding a new parameter l3proto. This patch prepares that code simplification. Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 2 +- net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 2 +- net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 2 +- net/netfilter/nf_conntrack_proto.c | 5 +++-- net/netfilter/nf_conntrack_proto_dccp.c | 2 +- net/netfilter/nf_conntrack_proto_generic.c | 2 +- net/netfilter/nf_conntrack_proto_gre.c | 2 +- net/netfilter/nf_conntrack_proto_sctp.c | 4 ++-- net/netfilter/nf_conntrack_proto_tcp.c | 4 ++-- net/netfilter/nf_conntrack_proto_udp.c | 4 ++-- net/netfilter/nf_conntrack_proto_udplite.c | 2 +- 11 files changed, 16 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 81c52b5205f2..5dd60f2d02a1 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -97,7 +97,7 @@ struct nf_conntrack_l4proto { #endif int *net_id; /* Init l4proto pernet data */ - int (*init_net)(struct net *net); + int (*init_net)(struct net *net, u_int16_t proto); /* Protocol name */ const char *name; diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index 041923cb67ad..76f7a2f657fe 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -337,7 +337,7 @@ static struct ctl_table icmp_compat_sysctl_table[] = { #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ #endif /* CONFIG_SYSCTL */ -static int icmp_init_net(struct net *net) +static int icmp_init_net(struct net *net, u_int16_t proto) { struct nf_icmp_net *in = icmp_pernet(net); struct nf_proto_net *pn = (struct nf_proto_net *)in; diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index 63ed0121836c..807ae09df0ca 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -333,7 +333,7 @@ static struct ctl_table icmpv6_sysctl_table[] = { }; #endif /* CONFIG_SYSCTL */ -static int icmpv6_init_net(struct net *net) +static int icmpv6_init_net(struct net *net, u_int16_t proto) { struct nf_icmp_net *in = icmpv6_pernet(net); struct nf_proto_net *pn = (struct nf_proto_net *)in; diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 9bd88aa3c74f..6f4b6f3deee5 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -461,7 +461,7 @@ int nf_conntrack_l4proto_register(struct net *net, int ret = 0; if (l4proto->init_net) { - ret = l4proto->init_net(net); + ret = l4proto->init_net(net, l4proto->l3proto); if (ret < 0) return ret; } @@ -515,7 +515,8 @@ int nf_conntrack_proto_init(struct net *net) { unsigned int i; int err; - err = nf_conntrack_l4proto_generic.init_net(net); + err = nf_conntrack_l4proto_generic.init_net(net, + nf_conntrack_l4proto_generic.l3proto); if (err < 0) return err; err = nf_ct_l4proto_register_sysctl(net, diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index c33f76af913f..52da8f0293b5 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -815,7 +815,7 @@ static struct ctl_table dccp_sysctl_table[] = { }; #endif /* CONFIG_SYSCTL */ -static int dccp_init_net(struct net *net) +static int dccp_init_net(struct net *net, u_int16_t proto) { struct dccp_net *dn = dccp_pernet(net); struct nf_proto_net *pn = (struct nf_proto_net *)dn; diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index bb0e74fe0fae..d1ed7b44e079 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -135,7 +135,7 @@ static struct ctl_table generic_compat_sysctl_table[] = { #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ #endif /* CONFIG_SYSCTL */ -static int generic_init_net(struct net *net) +static int generic_init_net(struct net *net, u_int16_t proto) { struct nf_generic_net *gn = generic_pernet(net); struct nf_proto_net *pn = (struct nf_proto_net *)gn; diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 5cac41c2fa09..b09b7af7f6f8 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -348,7 +348,7 @@ gre_timeout_nla_policy[CTA_TIMEOUT_GRE_MAX+1] = { }; #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ -static int gre_init_net(struct net *net) +static int gre_init_net(struct net *net, u_int16_t proto) { struct netns_proto_gre *net_gre = gre_pernet(net); int i; diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 8fb0582ad397..1e7836cead74 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -767,7 +767,7 @@ static int sctp_kmemdup_compat_sysctl_table(struct nf_proto_net *pn) return 0; } -static int sctpv4_init_net(struct net *net) +static int sctpv4_init_net(struct net *net, u_int16_t proto) { int ret; struct sctp_net *sn = sctp_pernet(net); @@ -793,7 +793,7 @@ static int sctpv4_init_net(struct net *net) return ret; } -static int sctpv6_init_net(struct net *net) +static int sctpv6_init_net(struct net *net, u_int16_t proto) { struct sctp_net *sn = sctp_pernet(net); struct nf_proto_net *pn = (struct nf_proto_net *)sn; diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 99caa1304477..6db9d3c44820 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -1593,7 +1593,7 @@ static int tcp_kmemdup_compat_sysctl_table(struct nf_proto_net *pn) return 0; } -static int tcpv4_init_net(struct net *net) +static int tcpv4_init_net(struct net *net, u_int16_t proto) { int i; int ret = 0; @@ -1631,7 +1631,7 @@ static int tcpv4_init_net(struct net *net) return ret; } -static int tcpv6_init_net(struct net *net) +static int tcpv6_init_net(struct net *net, u_int16_t proto) { int i; struct nf_tcp_net *tn = tcp_pernet(net); diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index a83cf93545cd..2b978e6fd1c2 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -283,7 +283,7 @@ static void udp_init_net_data(struct nf_udp_net *un) } } -static int udpv4_init_net(struct net *net) +static int udpv4_init_net(struct net *net, u_int16_t proto) { int ret; struct nf_udp_net *un = udp_pernet(net); @@ -307,7 +307,7 @@ static int udpv4_init_net(struct net *net) return ret; } -static int udpv6_init_net(struct net *net) +static int udpv6_init_net(struct net *net, u_int16_t proto) { struct nf_udp_net *un = udp_pernet(net); struct nf_proto_net *pn = (struct nf_proto_net *)un; diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c index b32e700f8dde..d33e51158039 100644 --- a/net/netfilter/nf_conntrack_proto_udplite.c +++ b/net/netfilter/nf_conntrack_proto_udplite.c @@ -234,7 +234,7 @@ static struct ctl_table udplite_sysctl_table[] = { }; #endif /* CONFIG_SYSCTL */ -static int udplite_init_net(struct net *net) +static int udplite_init_net(struct net *net, u_int16_t proto) { int i; struct udplite_net *un = udplite_pernet(net); -- cgit v1.2.3 From f28997e27a03abc679f13824a0574b09112eea37 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Thu, 21 Jun 2012 04:36:40 +0000 Subject: netfilter: nf_conntrack: add nf_ct_kfree_compat_sysctl_table This patch is a cleanup. It adds nf_ct_kfree_compat_sysctl_table to release l4proto's compat sysctl table and set the compat sysctl table point to NULL. This new function will be used by follow-up patches. Signed-off-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 8 ++++++++ net/netfilter/nf_conntrack_proto.c | 3 +-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 5dd60f2d02a1..08bb571b7abd 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -124,6 +124,14 @@ extern int nf_conntrack_l4proto_register(struct net *net, extern void nf_conntrack_l4proto_unregister(struct net *net, struct nf_conntrack_l4proto *proto); +static inline void nf_ct_kfree_compat_sysctl_table(struct nf_proto_net *pn) +{ +#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) + kfree(pn->ctl_compat_table); + pn->ctl_compat_table = NULL; +#endif +} + /* Generic netlink helpers */ extern int nf_ct_port_tuple_to_nlattr(struct sk_buff *skb, const struct nf_conntrack_tuple *tuple); diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 6f4b6f3deee5..9d6b6ab193a9 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -361,8 +361,7 @@ int nf_ct_l4proto_register_sysctl(struct net *net, if (err == 0) goto out; - kfree(pn->ctl_compat_table); - pn->ctl_compat_table = NULL; + nf_ct_kfree_compat_sysctl_table(pn); nf_ct_unregister_sysctl(&pn->ctl_table_header, &pn->ctl_table, &pn->users); -- cgit v1.2.3 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') 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 From c074da2810c118b3812f32d6754bd9ead2f169e7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 26 Jun 2012 23:14:15 +0000 Subject: ipv4: tcp: dont cache unconfirmed intput dst DDOS synflood attacks hit badly IP route cache. On typical machines, this cache is allowed to hold up to 8 Millions dst entries, 256 bytes for each, for a total of 2GB of memory. rt_garbage_collect() triggers and tries to cleanup things. Eventually route cache is disabled but machine is under fire and might OOM and crash. This patch exploits the new TCP early demux, to set a nocache boolean in case incoming TCP frame is for a not yet ESTABLISHED or TIMEWAIT socket. This 'nocache' boolean is then used in case dst entry is not found in route cache, to create an unhashed dst entry (DST_NOCACHE) SYN-cookie-ACK sent use a similar mechanism (ipv4: tcp: dont cache output dst for syncookies), so after this patch, a machine is able to absorb a DDOS synflood attack without polluting its IP route cache. Signed-off-by: Eric Dumazet Cc: Hans Schillstrom Signed-off-by: David S. Miller --- include/net/protocol.h | 2 +- include/net/route.h | 8 ++++---- include/net/tcp.h | 2 +- net/ipv4/arp.c | 2 +- net/ipv4/ip_fragment.c | 2 +- net/ipv4/ip_input.c | 5 +++-- net/ipv4/route.c | 8 +++++--- net/ipv4/tcp_ipv4.c | 4 +++- net/ipv4/xfrm4_input.c | 2 +- 9 files changed, 20 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/protocol.h b/include/net/protocol.h index 967b926cbfb1..7cfc8f76914d 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -37,7 +37,7 @@ /* This is used to register protocols. */ struct net_protocol { - int (*early_demux)(struct sk_buff *skb); + int (*early_demux)(struct sk_buff *skb, bool *nocache); int (*handler)(struct sk_buff *skb); void (*err_handler)(struct sk_buff *skb, u32 info); int (*gso_send_check)(struct sk_buff *skb); diff --git a/include/net/route.h b/include/net/route.h index 47eb25ac1f7f..6361f9335774 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -201,18 +201,18 @@ static inline struct rtable *ip_route_output_gre(struct net *net, struct flowi4 } extern int ip_route_input_common(struct sk_buff *skb, __be32 dst, __be32 src, - u8 tos, struct net_device *devin, bool noref); + u8 tos, struct net_device *devin, bool noref, bool nocache); static inline int ip_route_input(struct sk_buff *skb, __be32 dst, __be32 src, u8 tos, struct net_device *devin) { - return ip_route_input_common(skb, dst, src, tos, devin, false); + return ip_route_input_common(skb, dst, src, tos, devin, false, false); } static inline int ip_route_input_noref(struct sk_buff *skb, __be32 dst, __be32 src, - u8 tos, struct net_device *devin) + u8 tos, struct net_device *devin, bool nocache) { - return ip_route_input_common(skb, dst, src, tos, devin, true); + return ip_route_input_common(skb, dst, src, tos, devin, true, nocache); } extern void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu, diff --git a/include/net/tcp.h b/include/net/tcp.h index 6660ffc4963d..917ed2e55e8c 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -325,7 +325,7 @@ extern void tcp_v4_err(struct sk_buff *skb, u32); extern void tcp_shutdown (struct sock *sk, int how); -extern int tcp_v4_early_demux(struct sk_buff *skb); +extern int tcp_v4_early_demux(struct sk_buff *skb, bool *nocache); extern int tcp_v4_rcv(struct sk_buff *skb); extern struct inet_peer *tcp_v4_get_peer(struct sock *sk); diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 2e560f0c757d..6a9795944369 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -828,7 +828,7 @@ static int arp_process(struct sk_buff *skb) } if (arp->ar_op == htons(ARPOP_REQUEST) && - ip_route_input_noref(skb, tip, sip, 0, dev) == 0) { + ip_route_input_noref(skb, tip, sip, 0, dev, false) == 0) { rt = skb_rtable(skb); addr_type = rt->rt_type; diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 8d07c973409c..978d55f256ea 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -259,7 +259,7 @@ static void ip_expire(unsigned long arg) skb_dst_drop(head); iph = ip_hdr(head); err = ip_route_input_noref(head, iph->daddr, iph->saddr, - iph->tos, head->dev); + iph->tos, head->dev, false); if (err) goto out_rcu_unlock; diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 2a39204de5bc..7be54c8dcbe2 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -326,6 +326,7 @@ static int ip_rcv_finish(struct sk_buff *skb) */ if (skb_dst(skb) == NULL) { int err = -ENOENT; + bool nocache = false; if (sysctl_ip_early_demux) { const struct net_protocol *ipprot; @@ -334,13 +335,13 @@ static int ip_rcv_finish(struct sk_buff *skb) rcu_read_lock(); ipprot = rcu_dereference(inet_protos[protocol]); if (ipprot && ipprot->early_demux) - err = ipprot->early_demux(skb); + err = ipprot->early_demux(skb, &nocache); rcu_read_unlock(); } if (err) { err = ip_route_input_noref(skb, iph->daddr, iph->saddr, - iph->tos, skb->dev); + iph->tos, skb->dev, nocache); if (unlikely(err)) { if (err == -EXDEV) NET_INC_STATS_BH(dev_net(skb->dev), diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 81533e3a23d1..fdc7900f9d7a 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2214,7 +2214,7 @@ static int ip_mkroute_input(struct sk_buff *skb, */ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, - u8 tos, struct net_device *dev) + u8 tos, struct net_device *dev, bool nocache) { struct fib_result res; struct in_device *in_dev = __in_dev_get_rcu(dev); @@ -2353,6 +2353,8 @@ local_input: rth->dst.error= -err; rth->rt_flags &= ~RTCF_LOCAL; } + if (nocache) + rth->dst.flags |= DST_NOCACHE; hash = rt_hash(daddr, saddr, fl4.flowi4_iif, rt_genid(net)); rth = rt_intern_hash(hash, rth, skb, fl4.flowi4_iif); err = 0; @@ -2395,7 +2397,7 @@ martian_source_keep_err: } int ip_route_input_common(struct sk_buff *skb, __be32 daddr, __be32 saddr, - u8 tos, struct net_device *dev, bool noref) + u8 tos, struct net_device *dev, bool noref, bool nocache) { struct rtable *rth; unsigned int hash; @@ -2471,7 +2473,7 @@ skip_cache: rcu_read_unlock(); return -EINVAL; } - res = ip_route_input_slow(skb, daddr, saddr, tos, dev); + res = ip_route_input_slow(skb, daddr, saddr, tos, dev, nocache); rcu_read_unlock(); return res; } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 1781dc650b9d..33aabd4fc20f 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1673,7 +1673,7 @@ csum_err: } EXPORT_SYMBOL(tcp_v4_do_rcv); -int tcp_v4_early_demux(struct sk_buff *skb) +int tcp_v4_early_demux(struct sk_buff *skb, bool *no_dst_cache) { struct net *net = dev_net(skb->dev); const struct iphdr *iph; @@ -1719,6 +1719,8 @@ int tcp_v4_early_demux(struct sk_buff *skb) } } } + } else { + *no_dst_cache = true; } out_err: diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index 06814b6216dc..eee636b191b9 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -28,7 +28,7 @@ static inline int xfrm4_rcv_encap_finish(struct sk_buff *skb) const struct iphdr *iph = ip_hdr(skb); if (ip_route_input_noref(skb, iph->daddr, iph->saddr, - iph->tos, skb->dev)) + iph->tos, skb->dev, false)) goto drop; } return dst_input(skb); -- cgit v1.2.3 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') 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 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') 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 From c10237e077cef50e925f052e49f3b4fead9d71f9 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 27 Jun 2012 17:05:06 -0700 Subject: Revert "ipv4: tcp: dont cache unconfirmed intput dst" This reverts commit c074da2810c118b3812f32d6754bd9ead2f169e7. This change has several unwanted side effects: 1) Sockets will cache the DST_NOCACHE route in sk->sk_rx_dst and we'll thus never create a real cached route. 2) All TCP traffic will use DST_NOCACHE and never use the routing cache at all. Signed-off-by: David S. Miller --- include/net/protocol.h | 2 +- include/net/route.h | 8 ++++---- include/net/tcp.h | 2 +- net/ipv4/arp.c | 2 +- net/ipv4/ip_fragment.c | 2 +- net/ipv4/ip_input.c | 5 ++--- net/ipv4/route.c | 8 +++----- net/ipv4/tcp_ipv4.c | 4 +--- net/ipv4/xfrm4_input.c | 2 +- 9 files changed, 15 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/net/protocol.h b/include/net/protocol.h index 7cfc8f76914d..967b926cbfb1 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -37,7 +37,7 @@ /* This is used to register protocols. */ struct net_protocol { - int (*early_demux)(struct sk_buff *skb, bool *nocache); + int (*early_demux)(struct sk_buff *skb); int (*handler)(struct sk_buff *skb); void (*err_handler)(struct sk_buff *skb, u32 info); int (*gso_send_check)(struct sk_buff *skb); diff --git a/include/net/route.h b/include/net/route.h index 6361f9335774..47eb25ac1f7f 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -201,18 +201,18 @@ static inline struct rtable *ip_route_output_gre(struct net *net, struct flowi4 } extern int ip_route_input_common(struct sk_buff *skb, __be32 dst, __be32 src, - u8 tos, struct net_device *devin, bool noref, bool nocache); + u8 tos, struct net_device *devin, bool noref); static inline int ip_route_input(struct sk_buff *skb, __be32 dst, __be32 src, u8 tos, struct net_device *devin) { - return ip_route_input_common(skb, dst, src, tos, devin, false, false); + return ip_route_input_common(skb, dst, src, tos, devin, false); } static inline int ip_route_input_noref(struct sk_buff *skb, __be32 dst, __be32 src, - u8 tos, struct net_device *devin, bool nocache) + u8 tos, struct net_device *devin) { - return ip_route_input_common(skb, dst, src, tos, devin, true, nocache); + return ip_route_input_common(skb, dst, src, tos, devin, true); } extern void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu, diff --git a/include/net/tcp.h b/include/net/tcp.h index 917ed2e55e8c..6660ffc4963d 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -325,7 +325,7 @@ extern void tcp_v4_err(struct sk_buff *skb, u32); extern void tcp_shutdown (struct sock *sk, int how); -extern int tcp_v4_early_demux(struct sk_buff *skb, bool *nocache); +extern int tcp_v4_early_demux(struct sk_buff *skb); extern int tcp_v4_rcv(struct sk_buff *skb); extern struct inet_peer *tcp_v4_get_peer(struct sock *sk); diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 6a9795944369..2e560f0c757d 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -828,7 +828,7 @@ static int arp_process(struct sk_buff *skb) } if (arp->ar_op == htons(ARPOP_REQUEST) && - ip_route_input_noref(skb, tip, sip, 0, dev, false) == 0) { + ip_route_input_noref(skb, tip, sip, 0, dev) == 0) { rt = skb_rtable(skb); addr_type = rt->rt_type; diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 978d55f256ea..8d07c973409c 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -259,7 +259,7 @@ static void ip_expire(unsigned long arg) skb_dst_drop(head); iph = ip_hdr(head); err = ip_route_input_noref(head, iph->daddr, iph->saddr, - iph->tos, head->dev, false); + iph->tos, head->dev); if (err) goto out_rcu_unlock; diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 7be54c8dcbe2..2a39204de5bc 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -326,7 +326,6 @@ static int ip_rcv_finish(struct sk_buff *skb) */ if (skb_dst(skb) == NULL) { int err = -ENOENT; - bool nocache = false; if (sysctl_ip_early_demux) { const struct net_protocol *ipprot; @@ -335,13 +334,13 @@ static int ip_rcv_finish(struct sk_buff *skb) rcu_read_lock(); ipprot = rcu_dereference(inet_protos[protocol]); if (ipprot && ipprot->early_demux) - err = ipprot->early_demux(skb, &nocache); + err = ipprot->early_demux(skb); rcu_read_unlock(); } if (err) { err = ip_route_input_noref(skb, iph->daddr, iph->saddr, - iph->tos, skb->dev, nocache); + iph->tos, skb->dev); if (unlikely(err)) { if (err == -EXDEV) NET_INC_STATS_BH(dev_net(skb->dev), diff --git a/net/ipv4/route.c b/net/ipv4/route.c index fdc7900f9d7a..81533e3a23d1 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2214,7 +2214,7 @@ static int ip_mkroute_input(struct sk_buff *skb, */ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, - u8 tos, struct net_device *dev, bool nocache) + u8 tos, struct net_device *dev) { struct fib_result res; struct in_device *in_dev = __in_dev_get_rcu(dev); @@ -2353,8 +2353,6 @@ local_input: rth->dst.error= -err; rth->rt_flags &= ~RTCF_LOCAL; } - if (nocache) - rth->dst.flags |= DST_NOCACHE; hash = rt_hash(daddr, saddr, fl4.flowi4_iif, rt_genid(net)); rth = rt_intern_hash(hash, rth, skb, fl4.flowi4_iif); err = 0; @@ -2397,7 +2395,7 @@ martian_source_keep_err: } int ip_route_input_common(struct sk_buff *skb, __be32 daddr, __be32 saddr, - u8 tos, struct net_device *dev, bool noref, bool nocache) + u8 tos, struct net_device *dev, bool noref) { struct rtable *rth; unsigned int hash; @@ -2473,7 +2471,7 @@ skip_cache: rcu_read_unlock(); return -EINVAL; } - res = ip_route_input_slow(skb, daddr, saddr, tos, dev, nocache); + res = ip_route_input_slow(skb, daddr, saddr, tos, dev); rcu_read_unlock(); return res; } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 33aabd4fc20f..1781dc650b9d 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1673,7 +1673,7 @@ csum_err: } EXPORT_SYMBOL(tcp_v4_do_rcv); -int tcp_v4_early_demux(struct sk_buff *skb, bool *no_dst_cache) +int tcp_v4_early_demux(struct sk_buff *skb) { struct net *net = dev_net(skb->dev); const struct iphdr *iph; @@ -1719,8 +1719,6 @@ int tcp_v4_early_demux(struct sk_buff *skb, bool *no_dst_cache) } } } - } else { - *no_dst_cache = true; } out_err: diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index eee636b191b9..06814b6216dc 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -28,7 +28,7 @@ static inline int xfrm4_rcv_encap_finish(struct sk_buff *skb) const struct iphdr *iph = ip_hdr(skb); if (ip_route_input_noref(skb, iph->daddr, iph->saddr, - iph->tos, skb->dev, false)) + iph->tos, skb->dev)) goto drop; } return dst_input(skb); -- cgit v1.2.3 From e7b52ffd45a6d834473f43b349e7d86593d763c7 Mon Sep 17 00:00:00 2001 From: Alex Shi Date: Thu, 28 Jun 2012 09:02:17 +0800 Subject: x86/flush_tlb: try flush_tlb_single one by one in flush_tlb_range x86 has no flush_tlb_range support in instruction level. Currently the flush_tlb_range just implemented by flushing all page table. That is not the best solution for all scenarios. In fact, if we just use 'invlpg' to flush few lines from TLB, we can get the performance gain from later remain TLB lines accessing. But the 'invlpg' instruction costs much of time. Its execution time can compete with cr3 rewriting, and even a bit more on SNB CPU. So, on a 512 4KB TLB entries CPU, the balance points is at: (512 - X) * 100ns(assumed TLB refill cost) = X(TLB flush entries) * 100ns(assumed invlpg cost) Here, X is 256, that is 1/2 of 512 entries. But with the mysterious CPU pre-fetcher and page miss handler Unit, the assumed TLB refill cost is far lower then 100ns in sequential access. And 2 HT siblings in one core makes the memory access more faster if they are accessing the same memory. So, in the patch, I just do the change when the target entries is less than 1/16 of whole active tlb entries. Actually, I have no data support for the percentage '1/16', so any suggestions are welcomed. As to hugetlb, guess due to smaller page table, and smaller active TLB entries, I didn't see benefit via my benchmark, so no optimizing now. My micro benchmark show in ideal scenarios, the performance improves 70 percent in reading. And in worst scenario, the reading/writing performance is similar with unpatched 3.4-rc4 kernel. Here is the reading data on my 2P * 4cores *HT NHM EP machine, with THP 'always': multi thread testing, '-t' paramter is thread number: with patch unpatched 3.4-rc4 ./mprotect -t 1 14ns 24ns ./mprotect -t 2 13ns 22ns ./mprotect -t 4 12ns 19ns ./mprotect -t 8 14ns 16ns ./mprotect -t 16 28ns 26ns ./mprotect -t 32 54ns 51ns ./mprotect -t 128 200ns 199ns Single process with sequencial flushing and memory accessing: with patch unpatched 3.4-rc4 ./mprotect 7ns 11ns ./mprotect -p 4096 -l 8 -n 10240 21ns 21ns [ hpa: http://lkml.kernel.org/r/1B4B44D9196EFF41AE41FDA404FC0A100BFF94@SHSMSX101.ccr.corp.intel.com has additional performance numbers. ] Signed-off-by: Alex Shi Link: http://lkml.kernel.org/r/1340845344-27557-3-git-send-email-alex.shi@intel.com Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/paravirt.h | 5 +- arch/x86/include/asm/paravirt_types.h | 3 +- arch/x86/include/asm/tlbflush.h | 23 ++++----- arch/x86/include/asm/uv/uv.h | 5 +- arch/x86/mm/tlb.c | 97 +++++++++++++++++++++++++++++------ arch/x86/platform/uv/tlb_uv.c | 6 +-- arch/x86/xen/mmu.c | 12 ++--- include/trace/events/xen.h | 12 +++-- 8 files changed, 114 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h index 6cbbabf52707..7e2c2a635737 100644 --- a/arch/x86/include/asm/paravirt.h +++ b/arch/x86/include/asm/paravirt.h @@ -397,9 +397,10 @@ static inline void __flush_tlb_single(unsigned long addr) static inline void flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, - unsigned long va) + unsigned long start, + unsigned long end) { - PVOP_VCALL3(pv_mmu_ops.flush_tlb_others, cpumask, mm, va); + PVOP_VCALL4(pv_mmu_ops.flush_tlb_others, cpumask, mm, start, end); } static inline int paravirt_pgd_alloc(struct mm_struct *mm) diff --git a/arch/x86/include/asm/paravirt_types.h b/arch/x86/include/asm/paravirt_types.h index 8e8b9a4987ee..600a5fcac9cd 100644 --- a/arch/x86/include/asm/paravirt_types.h +++ b/arch/x86/include/asm/paravirt_types.h @@ -250,7 +250,8 @@ struct pv_mmu_ops { void (*flush_tlb_single)(unsigned long addr); void (*flush_tlb_others)(const struct cpumask *cpus, struct mm_struct *mm, - unsigned long va); + unsigned long start, + unsigned long end); /* Hooks for allocating and freeing a pagetable top-level */ int (*pgd_alloc)(struct mm_struct *mm); diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h index 36a1a2ab87d2..33608d96d68b 100644 --- a/arch/x86/include/asm/tlbflush.h +++ b/arch/x86/include/asm/tlbflush.h @@ -73,14 +73,10 @@ static inline void __flush_tlb_one(unsigned long addr) * - flush_tlb_page(vma, vmaddr) flushes one page * - flush_tlb_range(vma, start, end) flushes a range of pages * - flush_tlb_kernel_range(start, end) flushes a range of kernel pages - * - flush_tlb_others(cpumask, mm, va) flushes TLBs on other cpus + * - flush_tlb_others(cpumask, mm, start, end) flushes TLBs on other cpus * * ..but the i386 has somewhat limited tlb flushing capabilities, * and page-granular flushes are available only on i486 and up. - * - * x86-64 can only flush individual pages or full VMs. For a range flush - * we always do the full VM. Might be worth trying if for a small - * range a few INVLPGs in a row are a win. */ #ifndef CONFIG_SMP @@ -111,7 +107,8 @@ static inline void flush_tlb_range(struct vm_area_struct *vma, static inline void native_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, - unsigned long va) + unsigned long start, + unsigned long end) { } @@ -129,17 +126,14 @@ extern void flush_tlb_all(void); extern void flush_tlb_current_task(void); extern void flush_tlb_mm(struct mm_struct *); extern void flush_tlb_page(struct vm_area_struct *, unsigned long); +extern void flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end); #define flush_tlb() flush_tlb_current_task() -static inline void flush_tlb_range(struct vm_area_struct *vma, - unsigned long start, unsigned long end) -{ - flush_tlb_mm(vma->vm_mm); -} - void native_flush_tlb_others(const struct cpumask *cpumask, - struct mm_struct *mm, unsigned long va); + struct mm_struct *mm, + unsigned long start, unsigned long end); #define TLBSTATE_OK 1 #define TLBSTATE_LAZY 2 @@ -159,7 +153,8 @@ static inline void reset_lazy_tlbstate(void) #endif /* SMP */ #ifndef CONFIG_PARAVIRT -#define flush_tlb_others(mask, mm, va) native_flush_tlb_others(mask, mm, va) +#define flush_tlb_others(mask, mm, start, end) \ + native_flush_tlb_others(mask, mm, start, end) #endif static inline void flush_tlb_kernel_range(unsigned long start, diff --git a/arch/x86/include/asm/uv/uv.h b/arch/x86/include/asm/uv/uv.h index 3bb9491b7659..b47c2a82ff15 100644 --- a/arch/x86/include/asm/uv/uv.h +++ b/arch/x86/include/asm/uv/uv.h @@ -15,7 +15,8 @@ extern void uv_nmi_init(void); extern void uv_system_init(void); extern const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, - unsigned long va, + unsigned long start, + unsigned end, unsigned int cpu); #else /* X86_UV */ @@ -26,7 +27,7 @@ static inline void uv_cpu_init(void) { } static inline void uv_system_init(void) { } static inline const struct cpumask * uv_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, - unsigned long va, unsigned int cpu) + unsigned long start, unsigned long end, unsigned int cpu) { return cpumask; } #endif /* X86_UV */ diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c index 5e57e113b72c..3b91c981a27f 100644 --- a/arch/x86/mm/tlb.c +++ b/arch/x86/mm/tlb.c @@ -41,7 +41,8 @@ DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) union smp_flush_state { struct { struct mm_struct *flush_mm; - unsigned long flush_va; + unsigned long flush_start; + unsigned long flush_end; raw_spinlock_t tlbstate_lock; DECLARE_BITMAP(flush_cpumask, NR_CPUS); }; @@ -156,10 +157,19 @@ void smp_invalidate_interrupt(struct pt_regs *regs) if (f->flush_mm == this_cpu_read(cpu_tlbstate.active_mm)) { if (this_cpu_read(cpu_tlbstate.state) == TLBSTATE_OK) { - if (f->flush_va == TLB_FLUSH_ALL) + if (f->flush_end == TLB_FLUSH_ALL + || !cpu_has_invlpg) local_flush_tlb(); - else - __flush_tlb_one(f->flush_va); + else if (!f->flush_end) + __flush_tlb_single(f->flush_start); + else { + unsigned long addr; + addr = f->flush_start; + while (addr < f->flush_end) { + __flush_tlb_single(addr); + addr += PAGE_SIZE; + } + } } else leave_mm(cpu); } @@ -172,7 +182,8 @@ out: } static void flush_tlb_others_ipi(const struct cpumask *cpumask, - struct mm_struct *mm, unsigned long va) + struct mm_struct *mm, unsigned long start, + unsigned long end) { unsigned int sender; union smp_flush_state *f; @@ -185,7 +196,8 @@ static void flush_tlb_others_ipi(const struct cpumask *cpumask, raw_spin_lock(&f->tlbstate_lock); f->flush_mm = mm; - f->flush_va = va; + f->flush_start = start; + f->flush_end = end; if (cpumask_andnot(to_cpumask(f->flush_cpumask), cpumask, cpumask_of(smp_processor_id()))) { /* * We have to send the IPI only to @@ -199,24 +211,26 @@ static void flush_tlb_others_ipi(const struct cpumask *cpumask, } f->flush_mm = NULL; - f->flush_va = 0; + f->flush_start = 0; + f->flush_end = 0; if (nr_cpu_ids > NUM_INVALIDATE_TLB_VECTORS) raw_spin_unlock(&f->tlbstate_lock); } void native_flush_tlb_others(const struct cpumask *cpumask, - struct mm_struct *mm, unsigned long va) + struct mm_struct *mm, unsigned long start, + unsigned long end) { if (is_uv_system()) { unsigned int cpu; cpu = smp_processor_id(); - cpumask = uv_flush_tlb_others(cpumask, mm, va, cpu); + cpumask = uv_flush_tlb_others(cpumask, mm, start, end, cpu); if (cpumask) - flush_tlb_others_ipi(cpumask, mm, va); + flush_tlb_others_ipi(cpumask, mm, start, end); return; } - flush_tlb_others_ipi(cpumask, mm, va); + flush_tlb_others_ipi(cpumask, mm, start, end); } static void __cpuinit calculate_tlb_offset(void) @@ -282,7 +296,7 @@ void flush_tlb_current_task(void) local_flush_tlb(); if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids) - flush_tlb_others(mm_cpumask(mm), mm, TLB_FLUSH_ALL); + flush_tlb_others(mm_cpumask(mm), mm, 0UL, TLB_FLUSH_ALL); preempt_enable(); } @@ -297,12 +311,63 @@ void flush_tlb_mm(struct mm_struct *mm) leave_mm(smp_processor_id()); } if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids) - flush_tlb_others(mm_cpumask(mm), mm, TLB_FLUSH_ALL); + flush_tlb_others(mm_cpumask(mm), mm, 0UL, TLB_FLUSH_ALL); + + preempt_enable(); +} + +#define FLUSHALL_BAR 16 + +void flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end) +{ + struct mm_struct *mm; + + if (!cpu_has_invlpg || vma->vm_flags & VM_HUGETLB) { + flush_tlb_mm(vma->vm_mm); + return; + } + + preempt_disable(); + mm = vma->vm_mm; + if (current->active_mm == mm) { + if (current->mm) { + unsigned long addr, vmflag = vma->vm_flags; + unsigned act_entries, tlb_entries = 0; + + if (vmflag & VM_EXEC) + tlb_entries = tlb_lli_4k[ENTRIES]; + else + tlb_entries = tlb_lld_4k[ENTRIES]; + + act_entries = tlb_entries > mm->total_vm ? + mm->total_vm : tlb_entries; + if ((end - start)/PAGE_SIZE > act_entries/FLUSHALL_BAR) + local_flush_tlb(); + else { + for (addr = start; addr < end; + addr += PAGE_SIZE) + __flush_tlb_single(addr); + + if (cpumask_any_but(mm_cpumask(mm), + smp_processor_id()) < nr_cpu_ids) + flush_tlb_others(mm_cpumask(mm), mm, + start, end); + preempt_enable(); + return; + } + } else { + leave_mm(smp_processor_id()); + } + } + if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids) + flush_tlb_others(mm_cpumask(mm), mm, 0UL, TLB_FLUSH_ALL); preempt_enable(); } -void flush_tlb_page(struct vm_area_struct *vma, unsigned long va) + +void flush_tlb_page(struct vm_area_struct *vma, unsigned long start) { struct mm_struct *mm = vma->vm_mm; @@ -310,13 +375,13 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long va) if (current->active_mm == mm) { if (current->mm) - __flush_tlb_one(va); + __flush_tlb_one(start); else leave_mm(smp_processor_id()); } if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids) - flush_tlb_others(mm_cpumask(mm), mm, va); + flush_tlb_others(mm_cpumask(mm), mm, start, 0UL); preempt_enable(); } diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 59880afa851f..f1bef8e1d633 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -1068,8 +1068,8 @@ static int set_distrib_bits(struct cpumask *flush_mask, struct bau_control *bcp, * done. The returned pointer is valid till preemption is re-enabled. */ const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, - struct mm_struct *mm, unsigned long va, - unsigned int cpu) + struct mm_struct *mm, unsigned long start, + unsigned end, unsigned int cpu) { int locals = 0; int remotes = 0; @@ -1112,7 +1112,7 @@ const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, record_send_statistics(stat, locals, hubs, remotes, bau_desc); - bau_desc->payload.address = va; + bau_desc->payload.address = start; bau_desc->payload.sending_cpu = cpu; /* * uv_flush_send_and_wait returns 0 if all cpu's were messaged, diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index 3a73785631ce..39ed56789f68 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -1244,7 +1244,8 @@ static void xen_flush_tlb_single(unsigned long addr) } static void xen_flush_tlb_others(const struct cpumask *cpus, - struct mm_struct *mm, unsigned long va) + struct mm_struct *mm, unsigned long start, + unsigned long end) { struct { struct mmuext_op op; @@ -1256,7 +1257,7 @@ static void xen_flush_tlb_others(const struct cpumask *cpus, } *args; struct multicall_space mcs; - trace_xen_mmu_flush_tlb_others(cpus, mm, va); + trace_xen_mmu_flush_tlb_others(cpus, mm, start, end); if (cpumask_empty(cpus)) return; /* nothing to do */ @@ -1269,11 +1270,10 @@ static void xen_flush_tlb_others(const struct cpumask *cpus, cpumask_and(to_cpumask(args->mask), cpus, cpu_online_mask); cpumask_clear_cpu(smp_processor_id(), to_cpumask(args->mask)); - if (va == TLB_FLUSH_ALL) { - args->op.cmd = MMUEXT_TLB_FLUSH_MULTI; - } else { + args->op.cmd = MMUEXT_TLB_FLUSH_MULTI; + if (start != TLB_FLUSH_ALL && (end - start) <= PAGE_SIZE) { args->op.cmd = MMUEXT_INVLPG_MULTI; - args->op.arg1.linear_addr = va; + args->op.arg1.linear_addr = start; } MULTI_mmuext_op(mcs.mc, &args->op, 1, NULL, DOMID_SELF); diff --git a/include/trace/events/xen.h b/include/trace/events/xen.h index 92f1a796829e..15ba03bdd7c6 100644 --- a/include/trace/events/xen.h +++ b/include/trace/events/xen.h @@ -397,18 +397,20 @@ TRACE_EVENT(xen_mmu_flush_tlb_single, TRACE_EVENT(xen_mmu_flush_tlb_others, TP_PROTO(const struct cpumask *cpus, struct mm_struct *mm, - unsigned long addr), - TP_ARGS(cpus, mm, addr), + unsigned long addr, unsigned long end), + TP_ARGS(cpus, mm, addr, end), TP_STRUCT__entry( __field(unsigned, ncpus) __field(struct mm_struct *, mm) __field(unsigned long, addr) + __field(unsigned long, end) ), TP_fast_assign(__entry->ncpus = cpumask_weight(cpus); __entry->mm = mm; - __entry->addr = addr), - TP_printk("ncpus %d mm %p addr %lx", - __entry->ncpus, __entry->mm, __entry->addr) + __entry->addr = addr, + __entry->end = end), + TP_printk("ncpus %d mm %p addr %lx, end %lx", + __entry->ncpus, __entry->mm, __entry->addr, __entry->end) ); TRACE_EVENT(xen_mmu_write_cr3, -- cgit v1.2.3 From c4211f42d3e66875298a5e26a75109878c80f15b Mon Sep 17 00:00:00 2001 From: Alex Shi Date: Thu, 28 Jun 2012 09:02:19 +0800 Subject: x86/tlb: add tlb_flushall_shift for specific CPU Testing show different CPU type(micro architectures and NUMA mode) has different balance points between the TLB flush all and multiple invlpg. And there also has cases the tlb flush change has no any help. This patch give a interface to let x86 vendor developers have a chance to set different shift for different CPU type. like some machine in my hands, balance points is 16 entries on Romely-EP; while it is at 8 entries on Bloomfield NHM-EP; and is 256 on IVB mobile CPU. but on model 15 core2 Xeon using invlpg has nothing help. For untested machine, do a conservative optimization, same as NHM CPU. Signed-off-by: Alex Shi Link: http://lkml.kernel.org/r/1340845344-27557-5-git-send-email-alex.shi@intel.com Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/processor.h | 2 ++ arch/x86/kernel/cpu/common.c | 14 ++++++++++++-- arch/x86/kernel/cpu/intel.c | 34 ++++++++++++++++++++++++++++++++++ arch/x86/mm/tlb.c | 7 +++---- include/asm-generic/tlb.h | 3 ++- 5 files changed, 53 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 39b2bd48dfbc..d048cad9bcad 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -72,6 +72,8 @@ extern u16 __read_mostly tlb_lli_4m[NR_INFO]; extern u16 __read_mostly tlb_lld_4k[NR_INFO]; extern u16 __read_mostly tlb_lld_2m[NR_INFO]; extern u16 __read_mostly tlb_lld_4m[NR_INFO]; +extern s8 __read_mostly tlb_flushall_shift; + /* * CPU type and hardware bug flags. Kept separately for each CPU. * Members of this structure are referenced in head.S, so think twice diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index b2016df00813..7595552600b8 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -459,16 +459,26 @@ u16 __read_mostly tlb_lld_4k[NR_INFO]; u16 __read_mostly tlb_lld_2m[NR_INFO]; u16 __read_mostly tlb_lld_4m[NR_INFO]; +/* + * tlb_flushall_shift shows the balance point in replacing cr3 write + * with multiple 'invlpg'. It will do this replacement when + * flush_tlb_lines <= active_lines/2^tlb_flushall_shift. + * If tlb_flushall_shift is -1, means the replacement will be disabled. + */ +s8 __read_mostly tlb_flushall_shift = -1; + void __cpuinit cpu_detect_tlb(struct cpuinfo_x86 *c) { if (this_cpu->c_detect_tlb) this_cpu->c_detect_tlb(c); printk(KERN_INFO "Last level iTLB entries: 4KB %d, 2MB %d, 4MB %d\n" \ - "Last level dTLB entries: 4KB %d, 2MB %d, 4MB %d\n", + "Last level dTLB entries: 4KB %d, 2MB %d, 4MB %d\n" \ + "tlb_flushall_shift is 0x%x\n", tlb_lli_4k[ENTRIES], tlb_lli_2m[ENTRIES], tlb_lli_4m[ENTRIES], tlb_lld_4k[ENTRIES], - tlb_lld_2m[ENTRIES], tlb_lld_4m[ENTRIES]); + tlb_lld_2m[ENTRIES], tlb_lld_4m[ENTRIES], + tlb_flushall_shift); } void __cpuinit detect_ht(struct cpuinfo_x86 *c) diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index ed0d512cf51b..0a4ce2980a5a 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -610,6 +610,39 @@ static void __cpuinit intel_tlb_lookup(const unsigned char desc) } } +static void __cpuinit intel_tlb_flushall_shift_set(struct cpuinfo_x86 *c) +{ + if (!cpu_has_invlpg) { + tlb_flushall_shift = -1; + return; + } + switch ((c->x86 << 8) + c->x86_model) { + case 0x60f: /* original 65 nm celeron/pentium/core2/xeon, "Merom"/"Conroe" */ + case 0x616: /* single-core 65 nm celeron/core2solo "Merom-L"/"Conroe-L" */ + case 0x617: /* current 45 nm celeron/core2/xeon "Penryn"/"Wolfdale" */ + case 0x61d: /* six-core 45 nm xeon "Dunnington" */ + tlb_flushall_shift = -1; + break; + case 0x61a: /* 45 nm nehalem, "Bloomfield" */ + case 0x61e: /* 45 nm nehalem, "Lynnfield" */ + case 0x625: /* 32 nm nehalem, "Clarkdale" */ + case 0x62c: /* 32 nm nehalem, "Gulftown" */ + case 0x62e: /* 45 nm nehalem-ex, "Beckton" */ + case 0x62f: /* 32 nm Xeon E7 */ + tlb_flushall_shift = 6; + break; + case 0x62a: /* SandyBridge */ + case 0x62d: /* SandyBridge, "Romely-EP" */ + tlb_flushall_shift = 5; + break; + case 0x63a: /* Ivybridge */ + tlb_flushall_shift = 1; + break; + default: + tlb_flushall_shift = 6; + } +} + static void __cpuinit intel_detect_tlb(struct cpuinfo_x86 *c) { int i, j, n; @@ -630,6 +663,7 @@ static void __cpuinit intel_detect_tlb(struct cpuinfo_x86 *c) for (j = 1 ; j < 16 ; j++) intel_tlb_lookup(desc[j]); } + intel_tlb_flushall_shift_set(c); } static const struct cpu_dev __cpuinitconst intel_cpu_dev = { diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c index 184a02a4d871..2939f2f9edbb 100644 --- a/arch/x86/mm/tlb.c +++ b/arch/x86/mm/tlb.c @@ -316,8 +316,6 @@ void flush_tlb_mm(struct mm_struct *mm) preempt_enable(); } -#define FLUSHALL_BAR 16 - #ifdef CONFIG_TRANSPARENT_HUGEPAGE static inline unsigned long has_large_page(struct mm_struct *mm, unsigned long start, unsigned long end) @@ -352,7 +350,7 @@ void flush_tlb_range(struct vm_area_struct *vma, { struct mm_struct *mm; - if (!cpu_has_invlpg || vma->vm_flags & VM_HUGETLB) { + if (vma->vm_flags & VM_HUGETLB || tlb_flushall_shift == -1) { flush_all: flush_tlb_mm(vma->vm_mm); return; @@ -373,7 +371,8 @@ flush_all: act_entries = tlb_entries > mm->total_vm ? mm->total_vm : tlb_entries; - if ((end - start)/PAGE_SIZE > act_entries/FLUSHALL_BAR) + if ((end - start) >> PAGE_SHIFT > + act_entries >> tlb_flushall_shift) local_flush_tlb(); else { if (has_large_page(mm, start, end)) { diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h index f96a5b58a975..75e888b3cfd2 100644 --- a/include/asm-generic/tlb.h +++ b/include/asm-generic/tlb.h @@ -113,7 +113,8 @@ static inline int tlb_fast_mode(struct mmu_gather *tlb) void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, bool fullmm); void tlb_flush_mmu(struct mmu_gather *tlb); -void tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end); +void tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, + unsigned long end); int __tlb_remove_page(struct mmu_gather *tlb, struct page *page); /* tlb_remove_page -- cgit v1.2.3 From 597e1c3580b7cfd95bb0f3167e2b297bf8a5a3ae Mon Sep 17 00:00:00 2001 From: Alex Shi Date: Thu, 28 Jun 2012 09:02:21 +0800 Subject: mm/mmu_gather: enable tlb flush range in generic mmu_gather This patch enabled the tlb flush range support in generic mmu layer. Most of arch has self tlb flush range support, like ARM/IA64 etc. X86 arch has no this support in hardware yet. But another instruction 'invlpg' can implement this function in some degree. So, enable this feather in generic layer for x86 now. and maybe useful for other archs in further. Generic mmu_gather struct is protected by micro HAVE_GENERIC_MMU_GATHER. Other archs that has flush range supported own self mmu_gather struct. So, now this change is safe for them. In future we may unify this struct and related functions on multiple archs. Thanks for Peter Zijlstra time and time reminder for multiple architecture code safe! Signed-off-by: Alex Shi Link: http://lkml.kernel.org/r/1340845344-27557-7-git-send-email-alex.shi@intel.com Signed-off-by: H. Peter Anvin --- include/asm-generic/tlb.h | 2 ++ mm/memory.c | 9 +++++++++ 2 files changed, 11 insertions(+) (limited to 'include') diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h index 75e888b3cfd2..ed6642ad03e0 100644 --- a/include/asm-generic/tlb.h +++ b/include/asm-generic/tlb.h @@ -86,6 +86,8 @@ struct mmu_gather { #ifdef CONFIG_HAVE_RCU_TABLE_FREE struct mmu_table_batch *batch; #endif + unsigned long start; + unsigned long end; unsigned int need_flush : 1, /* Did free PTEs */ fast_mode : 1; /* No batching */ diff --git a/mm/memory.c b/mm/memory.c index 1b7dc662bf9f..32c99433cfdf 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -206,6 +206,8 @@ void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, bool fullmm) tlb->mm = mm; tlb->fullmm = fullmm; + tlb->start = -1UL; + tlb->end = 0; tlb->need_flush = 0; tlb->fast_mode = (num_possible_cpus() == 1); tlb->local.next = NULL; @@ -248,6 +250,8 @@ void tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long e { struct mmu_gather_batch *batch, *next; + tlb->start = start; + tlb->end = end; tlb_flush_mmu(tlb); /* keep the page table cache within bounds */ @@ -1204,6 +1208,11 @@ again: */ if (force_flush) { force_flush = 0; + +#ifdef HAVE_GENERIC_MMU_GATHER + tlb->start = addr; + tlb->end = end; +#endif tlb_flush_mmu(tlb); if (addr != end) goto again; -- cgit v1.2.3 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') 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 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') 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 From 1d1e34ddd48d27def2f324c1e3be16d460b16436 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 27 Jun 2012 21:57:03 -0700 Subject: xfrm_user: Propagate netlink error codes properly. Instead of using a fixed value of "-1" or "-EMSGSIZE", propagate what the nla_*() interfaces actually return. Signed-off-by: David S. Miller --- include/net/xfrm.h | 10 +- net/xfrm/xfrm_user.c | 394 ++++++++++++++++++++++++++------------------------- 2 files changed, 208 insertions(+), 196 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index e0a55df5bde8..17acbc92476d 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1682,13 +1682,11 @@ static inline int xfrm_mark_get(struct nlattr **attrs, struct xfrm_mark *m) static inline int xfrm_mark_put(struct sk_buff *skb, const struct xfrm_mark *m) { - if ((m->m | m->v) && - nla_put(skb, XFRMA_MARK, sizeof(struct xfrm_mark), m)) - goto nla_put_failure; - return 0; + int ret = 0; -nla_put_failure: - return -1; + if (m->m | m->v) + ret = nla_put(skb, XFRMA_MARK, sizeof(struct xfrm_mark), m); + return ret; } #endif /* _NET_XFRM_H */ diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 44293b3fd6a1..540762726aaf 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -754,58 +754,67 @@ static int copy_to_user_state_extra(struct xfrm_state *x, struct xfrm_usersa_info *p, struct sk_buff *skb) { - copy_to_user_state(x, p); - - if (x->coaddr && - nla_put(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr)) - goto nla_put_failure; - - if (x->lastused && - nla_put_u64(skb, XFRMA_LASTUSED, x->lastused)) - goto nla_put_failure; - - if (x->aead && - nla_put(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead)) - goto nla_put_failure; - - if (x->aalg && - (copy_to_user_auth(x->aalg, skb) || - nla_put(skb, XFRMA_ALG_AUTH_TRUNC, - xfrm_alg_auth_len(x->aalg), x->aalg))) - goto nla_put_failure; - - if (x->ealg && - nla_put(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg)) - goto nla_put_failure; - - if (x->calg && - nla_put(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg)) - goto nla_put_failure; - - if (x->encap && - nla_put(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap)) - goto nla_put_failure; + int ret = 0; - if (x->tfcpad && - nla_put_u32(skb, XFRMA_TFCPAD, x->tfcpad)) - goto nla_put_failure; - - if (xfrm_mark_put(skb, &x->mark)) - goto nla_put_failure; - - if (x->replay_esn && - nla_put(skb, XFRMA_REPLAY_ESN_VAL, - xfrm_replay_state_esn_len(x->replay_esn), - x->replay_esn)) - goto nla_put_failure; - - if (x->security && copy_sec_ctx(x->security, skb)) - goto nla_put_failure; - - return 0; + copy_to_user_state(x, p); -nla_put_failure: - return -EMSGSIZE; + if (x->coaddr) { + ret = nla_put(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr); + if (ret) + goto out; + } + if (x->lastused) { + ret = nla_put_u64(skb, XFRMA_LASTUSED, x->lastused); + if (ret) + goto out; + } + if (x->aead) { + ret = nla_put(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead); + if (ret) + goto out; + } + if (x->aalg) { + ret = copy_to_user_auth(x->aalg, skb); + if (!ret) + ret = nla_put(skb, XFRMA_ALG_AUTH_TRUNC, + xfrm_alg_auth_len(x->aalg), x->aalg); + if (ret) + goto out; + } + if (x->ealg) { + ret = nla_put(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg); + if (ret) + goto out; + } + if (x->calg) { + ret = nla_put(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg); + if (ret) + goto out; + } + if (x->encap) { + ret = nla_put(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap); + if (ret) + goto out; + } + if (x->tfcpad) { + ret = nla_put_u32(skb, XFRMA_TFCPAD, x->tfcpad); + if (ret) + goto out; + } + ret = xfrm_mark_put(skb, &x->mark); + if (ret) + goto out; + if (x->replay_esn) { + ret = nla_put(skb, XFRMA_REPLAY_ESN_VAL, + xfrm_replay_state_esn_len(x->replay_esn), + x->replay_esn); + if (ret) + goto out; + } + if (x->security) + ret = copy_sec_ctx(x->security, skb); +out: + return ret; } static int dump_one_state(struct xfrm_state *x, int count, void *ptr) @@ -825,15 +834,12 @@ static int dump_one_state(struct xfrm_state *x, int count, void *ptr) p = nlmsg_data(nlh); err = copy_to_user_state_extra(x, p, skb); - if (err) - goto nla_put_failure; - + if (err) { + nlmsg_cancel(skb, nlh); + return err; + } nlmsg_end(skb, nlh); return 0; - -nla_put_failure: - nlmsg_cancel(skb, nlh); - return err; } static int xfrm_dump_sa_done(struct netlink_callback *cb) @@ -904,6 +910,7 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net, struct xfrmu_spdinfo spc; struct xfrmu_spdhinfo sph; struct nlmsghdr *nlh; + int err; u32 *f; nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0); @@ -922,15 +929,15 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net, sph.spdhcnt = si.spdhcnt; sph.spdhmcnt = si.spdhmcnt; - if (nla_put(skb, XFRMA_SPD_INFO, sizeof(spc), &spc) || - nla_put(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph)) - goto nla_put_failure; + err = nla_put(skb, XFRMA_SPD_INFO, sizeof(spc), &spc); + if (!err) + err = nla_put(skb, XFRMA_SPD_HINFO, sizeof(sph), &sph); + if (err) { + nlmsg_cancel(skb, nlh); + return err; + } return nlmsg_end(skb, nlh); - -nla_put_failure: - nlmsg_cancel(skb, nlh); - return -EMSGSIZE; } static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh, @@ -965,6 +972,7 @@ static int build_sadinfo(struct sk_buff *skb, struct net *net, struct xfrmk_sadinfo si; struct xfrmu_sadhinfo sh; struct nlmsghdr *nlh; + int err; u32 *f; nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSADINFO, sizeof(u32), 0); @@ -978,15 +986,15 @@ static int build_sadinfo(struct sk_buff *skb, struct net *net, sh.sadhmcnt = si.sadhmcnt; sh.sadhcnt = si.sadhcnt; - if (nla_put_u32(skb, XFRMA_SAD_CNT, si.sadcnt) || - nla_put(skb, XFRMA_SAD_HINFO, sizeof(sh), &sh)) - goto nla_put_failure; + err = nla_put_u32(skb, XFRMA_SAD_CNT, si.sadcnt); + if (!err) + err = nla_put(skb, XFRMA_SAD_HINFO, sizeof(sh), &sh); + if (err) { + nlmsg_cancel(skb, nlh); + return err; + } return nlmsg_end(skb, nlh); - -nla_put_failure: - nlmsg_cancel(skb, nlh); - return -EMSGSIZE; } static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh, @@ -1439,9 +1447,8 @@ static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buf static inline int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb) { - if (xp->security) { + if (xp->security) return copy_sec_ctx(xp->security, skb); - } return 0; } static inline size_t userpolicy_type_attrsize(void) @@ -1477,6 +1484,7 @@ static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr struct sk_buff *in_skb = sp->in_skb; struct sk_buff *skb = sp->out_skb; struct nlmsghdr *nlh; + int err; nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq, XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags); @@ -1485,22 +1493,19 @@ static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr p = nlmsg_data(nlh); copy_to_user_policy(xp, p, dir); - if (copy_to_user_tmpl(xp, skb) < 0) - goto nlmsg_failure; - if (copy_to_user_sec_ctx(xp, skb)) - goto nlmsg_failure; - if (copy_to_user_policy_type(xp->type, skb) < 0) - goto nlmsg_failure; - if (xfrm_mark_put(skb, &xp->mark)) - goto nla_put_failure; - + err = copy_to_user_tmpl(xp, skb); + if (!err) + err = copy_to_user_sec_ctx(xp, skb); + if (!err) + err = copy_to_user_policy_type(xp->type, skb); + if (!err) + err = xfrm_mark_put(skb, &xp->mark); + if (err) { + nlmsg_cancel(skb, nlh); + return err; + } nlmsg_end(skb, nlh); return 0; - -nla_put_failure: -nlmsg_failure: - nlmsg_cancel(skb, nlh); - return -EMSGSIZE; } static int xfrm_dump_policy_done(struct netlink_callback *cb) @@ -1688,6 +1693,7 @@ static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct { struct xfrm_aevent_id *id; struct nlmsghdr *nlh; + int err; nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_NEWAE, sizeof(*id), 0); if (nlh == NULL) @@ -1703,35 +1709,39 @@ static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct id->flags = c->data.aevent; if (x->replay_esn) { - if (nla_put(skb, XFRMA_REPLAY_ESN_VAL, - xfrm_replay_state_esn_len(x->replay_esn), - x->replay_esn)) - goto nla_put_failure; + err = nla_put(skb, XFRMA_REPLAY_ESN_VAL, + xfrm_replay_state_esn_len(x->replay_esn), + x->replay_esn); } else { - if (nla_put(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), - &x->replay)) - goto nla_put_failure; + err = nla_put(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), + &x->replay); } - if (nla_put(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft)) - goto nla_put_failure; - - if ((id->flags & XFRM_AE_RTHR) && - nla_put_u32(skb, XFRMA_REPLAY_THRESH, x->replay_maxdiff)) - goto nla_put_failure; - - if ((id->flags & XFRM_AE_ETHR) && - nla_put_u32(skb, XFRMA_ETIMER_THRESH, - x->replay_maxage * 10 / HZ)) - goto nla_put_failure; + if (err) + goto out_cancel; + err = nla_put(skb, XFRMA_LTIME_VAL, sizeof(x->curlft), &x->curlft); + if (err) + goto out_cancel; - if (xfrm_mark_put(skb, &x->mark)) - goto nla_put_failure; + if (id->flags & XFRM_AE_RTHR) { + err = nla_put_u32(skb, XFRMA_REPLAY_THRESH, x->replay_maxdiff); + if (err) + goto out_cancel; + } + if (id->flags & XFRM_AE_ETHR) { + err = nla_put_u32(skb, XFRMA_ETIMER_THRESH, + x->replay_maxage * 10 / HZ); + if (err) + goto out_cancel; + } + err = xfrm_mark_put(skb, &x->mark); + if (err) + goto out_cancel; return nlmsg_end(skb, nlh); -nla_put_failure: +out_cancel: nlmsg_cancel(skb, nlh); - return -EMSGSIZE; + return err; } static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh, @@ -2155,7 +2165,7 @@ static int build_migrate(struct sk_buff *skb, const struct xfrm_migrate *m, const struct xfrm_migrate *mp; struct xfrm_userpolicy_id *pol_id; struct nlmsghdr *nlh; - int i; + int i, err; nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MIGRATE, sizeof(*pol_id), 0); if (nlh == NULL) @@ -2167,21 +2177,25 @@ static int build_migrate(struct sk_buff *skb, const struct xfrm_migrate *m, memcpy(&pol_id->sel, sel, sizeof(pol_id->sel)); pol_id->dir = dir; - if (k != NULL && (copy_to_user_kmaddress(k, skb) < 0)) - goto nlmsg_failure; - - if (copy_to_user_policy_type(type, skb) < 0) - goto nlmsg_failure; - + if (k != NULL) { + err = copy_to_user_kmaddress(k, skb); + if (err) + goto out_cancel; + } + err = copy_to_user_policy_type(type, skb); + if (err) + goto out_cancel; for (i = 0, mp = m ; i < num_migrate; i++, mp++) { - if (copy_to_user_migrate(mp, skb) < 0) - goto nlmsg_failure; + err = copy_to_user_migrate(mp, skb); + if (err) + goto out_cancel; } return nlmsg_end(skb, nlh); -nlmsg_failure: + +out_cancel: nlmsg_cancel(skb, nlh); - return -EMSGSIZE; + return err; } static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, @@ -2354,6 +2368,7 @@ static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct { struct xfrm_user_expire *ue; struct nlmsghdr *nlh; + int err; nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0); if (nlh == NULL) @@ -2363,13 +2378,11 @@ static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct copy_to_user_state(x, &ue->state); ue->hard = (c->data.hard != 0) ? 1 : 0; - if (xfrm_mark_put(skb, &x->mark)) - goto nla_put_failure; + err = xfrm_mark_put(skb, &x->mark); + if (err) + return err; return nlmsg_end(skb, nlh); - -nla_put_failure: - return -EMSGSIZE; } static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c) @@ -2470,7 +2483,7 @@ static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c) struct nlmsghdr *nlh; struct sk_buff *skb; int len = xfrm_sa_len(x); - int headlen; + int headlen, err; headlen = sizeof(*p); if (c->event == XFRM_MSG_DELSA) { @@ -2485,8 +2498,9 @@ static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c) return -ENOMEM; nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); + err = -EMSGSIZE; if (nlh == NULL) - goto nla_put_failure; + goto out_free_skb; p = nlmsg_data(nlh); if (c->event == XFRM_MSG_DELSA) { @@ -2499,24 +2513,23 @@ static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c) id->proto = x->id.proto; attr = nla_reserve(skb, XFRMA_SA, sizeof(*p)); + err = -EMSGSIZE; if (attr == NULL) - goto nla_put_failure; + goto out_free_skb; p = nla_data(attr); } - - if (copy_to_user_state_extra(x, p, skb)) - goto nla_put_failure; + err = copy_to_user_state_extra(x, p, skb); + if (err) + goto out_free_skb; nlmsg_end(skb, nlh); return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC); -nla_put_failure: - /* Somebody screwed up with xfrm_sa_len! */ - WARN_ON(1); +out_free_skb: kfree_skb(skb); - return -1; + return err; } static int xfrm_send_state_notify(struct xfrm_state *x, const struct km_event *c) @@ -2557,9 +2570,10 @@ static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, struct xfrm_tmpl *xt, struct xfrm_policy *xp, int dir) { + __u32 seq = xfrm_get_acqseq(); struct xfrm_user_acquire *ua; struct nlmsghdr *nlh; - __u32 seq = xfrm_get_acqseq(); + int err; nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0); if (nlh == NULL) @@ -2575,21 +2589,19 @@ static int build_acquire(struct sk_buff *skb, struct xfrm_state *x, ua->calgos = xt->calgos; ua->seq = x->km.seq = seq; - if (copy_to_user_tmpl(xp, skb) < 0) - goto nlmsg_failure; - if (copy_to_user_state_sec_ctx(x, skb)) - goto nlmsg_failure; - if (copy_to_user_policy_type(xp->type, skb) < 0) - goto nlmsg_failure; - if (xfrm_mark_put(skb, &xp->mark)) - goto nla_put_failure; + err = copy_to_user_tmpl(xp, skb); + if (!err) + err = copy_to_user_state_sec_ctx(x, skb); + if (!err) + err = copy_to_user_policy_type(xp->type, skb); + if (!err) + err = xfrm_mark_put(skb, &xp->mark); + if (err) { + nlmsg_cancel(skb, nlh); + return err; + } return nlmsg_end(skb, nlh); - -nla_put_failure: -nlmsg_failure: - nlmsg_cancel(skb, nlh); - return -EMSGSIZE; } static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt, @@ -2681,8 +2693,9 @@ static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, int dir, const struct km_event *c) { struct xfrm_user_polexpire *upe; - struct nlmsghdr *nlh; int hard = c->data.hard; + struct nlmsghdr *nlh; + int err; nlh = nlmsg_put(skb, c->pid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0); if (nlh == NULL) @@ -2690,22 +2703,20 @@ static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp, upe = nlmsg_data(nlh); copy_to_user_policy(xp, &upe->pol, dir); - if (copy_to_user_tmpl(xp, skb) < 0) - goto nlmsg_failure; - if (copy_to_user_sec_ctx(xp, skb)) - goto nlmsg_failure; - if (copy_to_user_policy_type(xp->type, skb) < 0) - goto nlmsg_failure; - if (xfrm_mark_put(skb, &xp->mark)) - goto nla_put_failure; + err = copy_to_user_tmpl(xp, skb); + if (!err) + err = copy_to_user_sec_ctx(xp, skb); + if (!err) + err = copy_to_user_policy_type(xp->type, skb); + if (!err) + err = xfrm_mark_put(skb, &xp->mark); + if (err) { + nlmsg_cancel(skb, nlh); + return err; + } upe->hard = !!hard; return nlmsg_end(skb, nlh); - -nla_put_failure: -nlmsg_failure: - nlmsg_cancel(skb, nlh); - return -EMSGSIZE; } static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) @@ -2725,13 +2736,13 @@ static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c) { + int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr); struct net *net = xp_net(xp); struct xfrm_userpolicy_info *p; struct xfrm_userpolicy_id *id; struct nlmsghdr *nlh; struct sk_buff *skb; - int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr); - int headlen; + int headlen, err; headlen = sizeof(*p); if (c->event == XFRM_MSG_DELPOLICY) { @@ -2747,8 +2758,9 @@ static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_e return -ENOMEM; nlh = nlmsg_put(skb, c->pid, c->seq, c->event, headlen, 0); + err = -EMSGSIZE; if (nlh == NULL) - goto nlmsg_failure; + goto out_free_skb; p = nlmsg_data(nlh); if (c->event == XFRM_MSG_DELPOLICY) { @@ -2763,29 +2775,29 @@ static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_e memcpy(&id->sel, &xp->selector, sizeof(id->sel)); attr = nla_reserve(skb, XFRMA_POLICY, sizeof(*p)); + err = -EMSGSIZE; if (attr == NULL) - goto nlmsg_failure; + goto out_free_skb; p = nla_data(attr); } copy_to_user_policy(xp, p, dir); - if (copy_to_user_tmpl(xp, skb) < 0) - goto nlmsg_failure; - if (copy_to_user_policy_type(xp->type, skb) < 0) - goto nlmsg_failure; - - if (xfrm_mark_put(skb, &xp->mark)) - goto nla_put_failure; + err = copy_to_user_tmpl(xp, skb); + if (!err) + err = copy_to_user_policy_type(xp->type, skb); + if (!err) + err = xfrm_mark_put(skb, &xp->mark); + if (err) + goto out_free_skb; nlmsg_end(skb, nlh); return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); -nla_put_failure: -nlmsg_failure: +out_free_skb: kfree_skb(skb); - return -1; + return err; } static int xfrm_notify_policy_flush(const struct km_event *c) @@ -2793,24 +2805,27 @@ static int xfrm_notify_policy_flush(const struct km_event *c) struct net *net = c->net; struct nlmsghdr *nlh; struct sk_buff *skb; + int err; skb = nlmsg_new(userpolicy_type_attrsize(), GFP_ATOMIC); if (skb == NULL) return -ENOMEM; nlh = nlmsg_put(skb, c->pid, c->seq, XFRM_MSG_FLUSHPOLICY, 0, 0); + err = -EMSGSIZE; if (nlh == NULL) - goto nlmsg_failure; - if (copy_to_user_policy_type(c->data.type, skb) < 0) - goto nlmsg_failure; + goto out_free_skb; + err = copy_to_user_policy_type(c->data.type, skb); + if (err) + goto out_free_skb; nlmsg_end(skb, nlh); return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC); -nlmsg_failure: +out_free_skb: kfree_skb(skb); - return -1; + return err; } static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c) @@ -2853,15 +2868,14 @@ static int build_report(struct sk_buff *skb, u8 proto, ur->proto = proto; memcpy(&ur->sel, sel, sizeof(ur->sel)); - if (addr && - nla_put(skb, XFRMA_COADDR, sizeof(*addr), addr)) - goto nla_put_failure; - + if (addr) { + int err = nla_put(skb, XFRMA_COADDR, sizeof(*addr), addr); + if (err) { + nlmsg_cancel(skb, nlh); + return err; + } + } return nlmsg_end(skb, nlh); - -nla_put_failure: - nlmsg_cancel(skb, nlh); - return -EMSGSIZE; } static int xfrm_send_report(struct net *net, u8 proto, -- cgit v1.2.3 From 160eb5a6b14ca2eab5c598bdbbb24c24624bad34 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 27 Jun 2012 22:01:22 -0700 Subject: ipv4: Kill early demux method return value. It's completely unnecessary. Signed-off-by: David S. Miller --- include/net/protocol.h | 2 +- include/net/tcp.h | 2 +- net/ipv4/ip_input.c | 42 +++++++++++++++++++----------------------- net/ipv4/tcp_ipv4.c | 19 ++++++------------- 4 files changed, 27 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/include/net/protocol.h b/include/net/protocol.h index 967b926cbfb1..057f2d315567 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -37,7 +37,7 @@ /* This is used to register protocols. */ struct net_protocol { - int (*early_demux)(struct sk_buff *skb); + void (*early_demux)(struct sk_buff *skb); int (*handler)(struct sk_buff *skb); void (*err_handler)(struct sk_buff *skb, u32 info); int (*gso_send_check)(struct sk_buff *skb); diff --git a/include/net/tcp.h b/include/net/tcp.h index 6660ffc4963d..53fb7d814170 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -325,7 +325,7 @@ extern void tcp_v4_err(struct sk_buff *skb, u32); extern void tcp_shutdown (struct sock *sk, int how); -extern int tcp_v4_early_demux(struct sk_buff *skb); +extern void tcp_v4_early_demux(struct sk_buff *skb); extern int tcp_v4_rcv(struct sk_buff *skb); extern struct inet_peer *tcp_v4_get_peer(struct sock *sk); diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 2a39204de5bc..b27d4440f523 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -320,33 +320,29 @@ static int ip_rcv_finish(struct sk_buff *skb) const struct iphdr *iph = ip_hdr(skb); struct rtable *rt; + if (sysctl_ip_early_demux && !skb_dst(skb)) { + const struct net_protocol *ipprot; + int protocol = iph->protocol; + + rcu_read_lock(); + ipprot = rcu_dereference(inet_protos[protocol]); + if (ipprot && ipprot->early_demux) + ipprot->early_demux(skb); + rcu_read_unlock(); + } + /* * Initialise the virtual path cache for the packet. It describes * how the packet travels inside Linux networking. */ - if (skb_dst(skb) == NULL) { - int err = -ENOENT; - - 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, - iph->tos, skb->dev); - if (unlikely(err)) { - if (err == -EXDEV) - NET_INC_STATS_BH(dev_net(skb->dev), - LINUX_MIB_IPRPFILTER); - goto drop; - } + if (!skb_dst(skb)) { + int err = ip_route_input_noref(skb, iph->daddr, iph->saddr, + iph->tos, skb->dev); + if (unlikely(err)) { + if (err == -EXDEV) + NET_INC_STATS_BH(dev_net(skb->dev), + LINUX_MIB_IPRPFILTER); + goto drop; } } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 1781dc650b9d..b4ae1c199f3e 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1673,30 +1673,28 @@ csum_err: } EXPORT_SYMBOL(tcp_v4_do_rcv); -int tcp_v4_early_demux(struct sk_buff *skb) +void tcp_v4_early_demux(struct sk_buff *skb) { struct net *net = dev_net(skb->dev); const struct iphdr *iph; const struct tcphdr *th; struct net_device *dev; struct sock *sk; - int err; - err = -ENOENT; if (skb->pkt_type != PACKET_HOST) - goto out_err; + return; if (!pskb_may_pull(skb, ip_hdrlen(skb) + sizeof(struct tcphdr))) - goto out_err; + return; iph = ip_hdr(skb); th = (struct tcphdr *) ((char *)iph + ip_hdrlen(skb)); if (th->doff < sizeof(struct tcphdr) / 4) - goto out_err; + return; if (!pskb_may_pull(skb, ip_hdrlen(skb) + th->doff * 4)) - goto out_err; + return; dev = skb->dev; sk = __inet_lookup_established(net, &tcp_hashinfo, @@ -1713,16 +1711,11 @@ int tcp_v4_early_demux(struct sk_buff *skb) if (dst) { struct rtable *rt = (struct rtable *) dst; - if (rt->rt_iif == dev->ifindex) { + if (rt->rt_iif == dev->ifindex) skb_dst_set_noref(skb, dst); - err = 0; - } } } } - -out_err: - return err; } /* -- cgit v1.2.3 From 0df8ad71a281893bf0bb46b715397ec4f106d394 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 25 Jun 2012 11:20:13 +0300 Subject: OMAPDSS: remove enum omap_dss_overlay_managers We have two almost the same enums: omap_channel and omap_dss_overlay_managers. omap_channel is used almost everywhere, and omap_channel assigns explicit values to the enum values which are needed for proper operation. omap_dss_overlay_managers is only used in one place, so it's easy to remove it, which is what this patch does. Signed-off-by: Tomi Valkeinen --- drivers/video/omap2/dss/overlay.c | 6 +++--- include/video/omapdss.h | 7 ------- 2 files changed, 3 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index b0ba60f88dd2..c492bb074d0f 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -530,10 +530,10 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) struct omap_overlay_manager *lcd2_mgr = NULL; struct omap_overlay_manager *mgr = NULL; - lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD); - tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV); + lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD); + tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_DIGIT); if (dss_has_feature(FEAT_MGR_LCD2)) - lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD2); + lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD2); if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) { if (!lcd2_mgr->device || force) { diff --git a/include/video/omapdss.h b/include/video/omapdss.h index c8e59b4a3364..ae6954836ef3 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -167,13 +167,6 @@ enum omap_dss_audio_state { OMAP_DSS_AUDIO_PLAYING, }; -/* XXX perhaps this should be removed */ -enum omap_dss_overlay_managers { - OMAP_DSS_OVL_MGR_LCD, - OMAP_DSS_OVL_MGR_TV, - OMAP_DSS_OVL_MGR_LCD2, -}; - enum omap_dss_rotation_type { OMAP_DSS_ROT_DMA = 1 << 0, OMAP_DSS_ROT_VRFB = 1 << 1, -- cgit v1.2.3 From fc8a7321d3d68af759a369a9ad3e2426688742d3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 28 Jun 2012 10:33:25 +0200 Subject: mac80211: don't expose ieee80211_add_srates_ie() This and ieee80211_add_ext_srates_ie() aren't exported, so can't be used by drivers anyway, but there's also no reason that they should be so make them private to mac80211 and use sdata instead of vif arguments. Acked-by: Arik Nemtsov Signed-off-by: Johannes Berg --- include/net/mac80211.h | 6 ------ net/mac80211/cfg.c | 12 ++++++------ net/mac80211/ieee80211_i.h | 4 ++++ net/mac80211/mesh_plink.c | 4 ++-- net/mac80211/tx.c | 4 ++-- net/mac80211/util.c | 10 ++++------ 6 files changed, 18 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 510d852d5222..5e67020b1702 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3826,12 +3826,6 @@ void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif); -int ieee80211_add_srates_ie(struct ieee80211_vif *vif, - struct sk_buff *skb, bool need_basic); - -int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif, - struct sk_buff *skb, bool need_basic); - /** * ieee80211_ave_rssi - report the average rssi for the specified interface * diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 7722a7336a58..ebc353ef6902 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2665,8 +2665,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, tf->u.setup_req.capability = cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - ieee80211_add_srates_ie(&sdata->vif, skb, false); - ieee80211_add_ext_srates_ie(&sdata->vif, skb, false); + ieee80211_add_srates_ie(sdata, skb, false); + ieee80211_add_ext_srates_ie(sdata, skb, false); ieee80211_tdls_add_ext_capab(skb); break; case WLAN_TDLS_SETUP_RESPONSE: @@ -2679,8 +2679,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, tf->u.setup_resp.capability = cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - ieee80211_add_srates_ie(&sdata->vif, skb, false); - ieee80211_add_ext_srates_ie(&sdata->vif, skb, false); + ieee80211_add_srates_ie(sdata, skb, false); + ieee80211_add_ext_srates_ie(sdata, skb, false); ieee80211_tdls_add_ext_capab(skb); break; case WLAN_TDLS_SETUP_CONFIRM: @@ -2740,8 +2740,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, mgmt->u.action.u.tdls_discover_resp.capability = cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - ieee80211_add_srates_ie(&sdata->vif, skb, false); - ieee80211_add_ext_srates_ie(&sdata->vif, skb, false); + ieee80211_add_srates_ie(sdata, skb, false); + ieee80211_add_ext_srates_ie(sdata, skb, false); ieee80211_tdls_add_ext_capab(skb); break; default: diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 341d77d472d2..6b7157d20507 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1480,6 +1480,10 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, struct ieee80211_channel *channel, enum nl80211_channel_type channel_type, u16 prot_mode); +int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, bool need_basic); +int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, bool need_basic); /* channel management */ enum ieee80211_chan_mode { diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index a1dbd1540276..425685914d7d 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -258,8 +258,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, pos = skb_put(skb, 2); memcpy(pos + 2, &plid, 2); } - if (ieee80211_add_srates_ie(&sdata->vif, skb, true) || - ieee80211_add_ext_srates_ie(&sdata->vif, skb, true) || + if (ieee80211_add_srates_ie(sdata, skb, true) || + ieee80211_add_ext_srates_ie(sdata, skb, true) || mesh_add_rsn_ie(skb, sdata) || mesh_add_meshid_ie(skb, sdata) || mesh_add_meshconf_ie(skb, sdata)) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ec8f53467374..4e753032e48d 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2420,9 +2420,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, *pos++ = WLAN_EID_SSID; *pos++ = 0x0; - if (ieee80211_add_srates_ie(&sdata->vif, skb, true) || + if (ieee80211_add_srates_ie(sdata, skb, true) || mesh_add_ds_params_ie(skb, sdata) || - ieee80211_add_ext_srates_ie(&sdata->vif, skb, true) || + ieee80211_add_ext_srates_ie(sdata, skb, true) || mesh_add_rsn_ie(skb, sdata) || mesh_add_ht_cap_ie(skb, sdata) || mesh_add_ht_oper_ie(skb, sdata) || diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 242ecde381f6..c4245695afc3 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1764,15 +1764,14 @@ ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper) return channel_type; } -int ieee80211_add_srates_ie(struct ieee80211_vif *vif, +int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, bool need_basic) { - struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; int rate; u8 i, rates, *pos; - u32 basic_rates = vif->bss_conf.basic_rates; + u32 basic_rates = sdata->vif.bss_conf.basic_rates; sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; rates = sband->n_bitrates; @@ -1796,15 +1795,14 @@ int ieee80211_add_srates_ie(struct ieee80211_vif *vif, return 0; } -int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif, +int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, bool need_basic) { - struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; int rate; u8 i, exrates, *pos; - u32 basic_rates = vif->bss_conf.basic_rates; + u32 basic_rates = sdata->vif.bss_conf.basic_rates; sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; exrates = sband->n_bitrates; -- cgit v1.2.3 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') 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 From 70e7341673a47fb1525cfc7d6651cc98b5348928 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 28 Jun 2012 03:21:41 -0700 Subject: ipv4: Show that ip_send_reply() is purely unicast routine. Rename it to ip_send_unicast_reply() and add explicit 'saddr' argument. This removed one of the few users of rt->rt_spec_dst. Signed-off-by: David S. Miller --- include/net/ip.h | 5 +++-- net/ipv4/ip_output.c | 9 +++++---- net/ipv4/tcp_ipv4.c | 8 ++++---- 3 files changed, 12 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/ip.h b/include/net/ip.h index 50841bd6f10e..ec5cfde85e9a 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -158,8 +158,9 @@ static inline __u8 ip_reply_arg_flowi_flags(const struct ip_reply_arg *arg) return (arg->flags & IP_REPLY_ARG_NOSRCCHECK) ? FLOWI_FLAG_ANYSRC : 0; } -void ip_send_reply(struct sock *sk, struct sk_buff *skb, __be32 daddr, - const struct ip_reply_arg *arg, unsigned int len); +void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb, __be32 daddr, + __be32 saddr, const struct ip_reply_arg *arg, + unsigned int len); struct ipv4_config { int log_martians; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 0f3185a662c3..2630900e480a 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1459,13 +1459,14 @@ static int ip_reply_glue_bits(void *dptr, char *to, int offset, /* * Generic function to send a packet as reply to another packet. - * Used to send TCP resets so far. ICMP should use this function too. + * Used to send TCP resets so far. * * Should run single threaded per socket because it uses the sock * structure to pass arguments. */ -void ip_send_reply(struct sock *sk, struct sk_buff *skb, __be32 daddr, - const struct ip_reply_arg *arg, unsigned int len) +void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb, __be32 daddr, + __be32 saddr, const struct ip_reply_arg *arg, + unsigned int len) { struct inet_sock *inet = inet_sk(sk); struct ip_options_data replyopts; @@ -1491,7 +1492,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, __be32 daddr, RT_TOS(arg->tos), RT_SCOPE_UNIVERSE, sk->sk_protocol, ip_reply_arg_flowi_flags(arg), - daddr, rt->rt_spec_dst, + daddr, saddr, tcp_hdr(skb)->source, tcp_hdr(skb)->dest); security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(sock_net(sk), &fl4); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index b4ae1c199f3e..64568fa21d05 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -698,8 +698,8 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) net = dev_net(skb_dst(skb)->dev); arg.tos = ip_hdr(skb)->tos; - ip_send_reply(net->ipv4.tcp_sock, skb, ip_hdr(skb)->saddr, - &arg, arg.iov[0].iov_len); + ip_send_unicast_reply(net->ipv4.tcp_sock, skb, ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, &arg, arg.iov[0].iov_len); TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS); TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS); @@ -781,8 +781,8 @@ static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack, if (oif) arg.bound_dev_if = oif; arg.tos = tos; - ip_send_reply(net->ipv4.tcp_sock, skb, ip_hdr(skb)->saddr, - &arg, arg.iov[0].iov_len); + ip_send_unicast_reply(net->ipv4.tcp_sock, skb, ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, &arg, arg.iov[0].iov_len); TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS); } -- cgit v1.2.3 From 35ebf65e851c6d9731abc6362b189858eb59f4d3 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 28 Jun 2012 03:59:11 -0700 Subject: ipv4: Create and use fib_compute_spec_dst() helper. The specific destination is the host we direct unicast replies to. Usually this is the original packet source address, but if we are responding to a multicast or broadcast packet we have to use something different. Specifically we must use the source address we would use if we were to send a packet to the unicast source of the original packet. The routing cache precomputes this value, but we want to remove that precomputation because it creates a hard dependency on the expensive rpfilter source address validation which we'd like to make cheaper. There are only three places where this matters: 1) ICMP replies. 2) pktinfo CMSG 3) IP options Now there will be no real users of rt->rt_spec_dst and we can simply remove it altogether. Signed-off-by: David S. Miller --- include/net/ip_fib.h | 1 + net/ipv4/fib_frontend.c | 29 +++++++++++++++++++++++++++++ net/ipv4/icmp.c | 6 ++++-- net/ipv4/ip_options.c | 22 +++++++++++----------- net/ipv4/ip_sockglue.c | 7 ++++--- 5 files changed, 49 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 4b347c0ca094..1687b3de54ff 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -230,6 +230,7 @@ extern struct fib_table *fib_get_table(struct net *net, u32 id); /* Exported by fib_frontend.c */ extern const struct nla_policy rtm_ipv4_policy[]; extern void ip_fib_init(void); +extern __be32 fib_compute_spec_dst(struct sk_buff *skb); extern int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, u8 tos, int oif, struct net_device *dev, __be32 *spec_dst, u32 *itag); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 3854411fa37c..451939b60c54 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -180,6 +180,35 @@ unsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev, } EXPORT_SYMBOL(inet_dev_addr_type); +__be32 fib_compute_spec_dst(struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + struct in_device *in_dev; + struct fib_result res; + struct flowi4 fl4; + struct net *net; + + if (skb->pkt_type != PACKET_BROADCAST && + skb->pkt_type != PACKET_MULTICAST) + return ip_hdr(skb)->daddr; + + in_dev = __in_dev_get_rcu(dev); + BUG_ON(!in_dev); + fl4.flowi4_oif = 0; + fl4.flowi4_iif = 0; + fl4.daddr = ip_hdr(skb)->saddr; + fl4.saddr = ip_hdr(skb)->daddr; + fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); + fl4.flowi4_scope = RT_SCOPE_UNIVERSE; + fl4.flowi4_mark = IN_DEV_SRC_VMARK(in_dev) ? skb->mark : 0; + + net = dev_net(dev); + if (!fib_lookup(net, &fl4, &res)) + return FIB_RES_PREFSRC(net, res); + else + return inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE); +} + /* Given (packet source, input interface) and optional (dst, oif, tos): * - (main) check, that source is valid i.e. not broadcast or our local * address. diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 49a74cc79dc8..4bce5a2830aa 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -95,6 +95,7 @@ #include #include #include +#include /* * Build xmit assembly blocks @@ -333,7 +334,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) struct flowi4 fl4; struct sock *sk; struct inet_sock *inet; - __be32 daddr; + __be32 daddr, saddr; if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb)) return; @@ -347,6 +348,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) inet->tos = ip_hdr(skb)->tos; daddr = ipc.addr = ip_hdr(skb)->saddr; + saddr = fib_compute_spec_dst(skb); ipc.opt = NULL; ipc.tx_flags = 0; if (icmp_param->replyopts.opt.opt.optlen) { @@ -356,7 +358,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) } memset(&fl4, 0, sizeof(fl4)); fl4.daddr = daddr; - fl4.saddr = rt->rt_spec_dst; + fl4.saddr = saddr; fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); fl4.flowi4_proto = IPPROTO_ICMP; security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 708b99494e23..766dfe56885a 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -27,6 +27,7 @@ #include #include #include +#include /* * Write options to IP header, record destination address to @@ -104,7 +105,7 @@ int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb) sptr = skb_network_header(skb); dptr = dopt->__data; - daddr = skb_rtable(skb)->rt_spec_dst; + daddr = fib_compute_spec_dst(skb); if (sopt->rr) { optlen = sptr[sopt->rr+1]; @@ -250,15 +251,14 @@ void ip_options_fragment(struct sk_buff *skb) int ip_options_compile(struct net *net, struct ip_options *opt, struct sk_buff *skb) { - int l; - unsigned char *iph; - unsigned char *optptr; - int optlen; + __be32 spec_dst = (__force __be32) 0; unsigned char *pp_ptr = NULL; - struct rtable *rt = NULL; + unsigned char *optptr; + unsigned char *iph; + int optlen, l; if (skb != NULL) { - rt = skb_rtable(skb); + spec_dst = fib_compute_spec_dst(skb); optptr = (unsigned char *)&(ip_hdr(skb)[1]); } else optptr = opt->__data; @@ -330,8 +330,8 @@ int ip_options_compile(struct net *net, pp_ptr = optptr + 2; goto error; } - if (rt) { - memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4); + if (skb) { + memcpy(&optptr[optptr[2]-1], &spec_dst, 4); opt->is_changed = 1; } optptr[2] += 4; @@ -372,8 +372,8 @@ int ip_options_compile(struct net *net, goto error; } opt->ts = optptr - iph; - if (rt) { - memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4); + if (skb) { + memcpy(&optptr[optptr[2]-1], &spec_dst, 4); timeptr = &optptr[optptr[2]+3]; } opt->ts_needaddr = 1; diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 0d11f234d615..de29f46f68b0 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -40,6 +40,7 @@ #if IS_ENABLED(CONFIG_IPV6) #include #endif +#include #include #include @@ -1019,8 +1020,8 @@ e_inval: * @sk: socket * @skb: buffer * - * To support IP_CMSG_PKTINFO option, we store rt_iif and rt_spec_dst - * in skb->cb[] before dst drop. + * To support IP_CMSG_PKTINFO option, we store rt_iif and specific + * destination in skb->cb[] before dst drop. * This way, receiver doesnt make cache line misses to read rtable. */ void ipv4_pktinfo_prepare(struct sk_buff *skb) @@ -1030,7 +1031,7 @@ void ipv4_pktinfo_prepare(struct sk_buff *skb) if (rt) { pktinfo->ipi_ifindex = rt->rt_iif; - pktinfo->ipi_spec_dst.s_addr = rt->rt_spec_dst; + pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb); } else { pktinfo->ipi_ifindex = 0; pktinfo->ipi_spec_dst.s_addr = 0; -- cgit v1.2.3 From 41347dcdd81988b8e60853257b2875285cc17a4e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 28 Jun 2012 04:05:27 -0700 Subject: ipv4: Kill rt->rt_spec_dst, no longer used. Signed-off-by: David S. Miller --- include/net/ip_fib.h | 2 +- include/net/route.h | 1 - net/ipv4/fib_frontend.c | 9 ++------- net/ipv4/route.c | 38 +++++++++----------------------------- net/ipv4/xfrm4_policy.c | 1 - 5 files changed, 12 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 1687b3de54ff..9e6c26d4ba4c 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -233,7 +233,7 @@ extern void ip_fib_init(void); extern __be32 fib_compute_spec_dst(struct sk_buff *skb); extern int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, u8 tos, int oif, struct net_device *dev, - __be32 *spec_dst, u32 *itag); + u32 *itag); extern void fib_select_default(struct fib_result *res); /* Exported by fib_semantics.c */ diff --git a/include/net/route.h b/include/net/route.h index 47eb25ac1f7f..211e2665139b 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -65,7 +65,6 @@ struct rtable { __be32 rt_gateway; /* Miscellaneous cached information */ - __be32 rt_spec_dst; /* RFC1122 specific destination */ u32 rt_peer_genid; unsigned long _peer; /* long-living peer info */ struct fib_info *fi; /* for client ref to shared metrics */ diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 451939b60c54..63b11ca54d95 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -218,8 +218,7 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb) * called with rcu_read_lock() */ int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, u8 tos, - int oif, struct net_device *dev, __be32 *spec_dst, - u32 *itag) + int oif, struct net_device *dev, u32 *itag) { struct in_device *in_dev; struct flowi4 fl4; @@ -258,7 +257,6 @@ int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, u8 tos, if (res.type != RTN_LOCAL || !accept_local) goto e_inval; } - *spec_dst = FIB_RES_PREFSRC(net, res); fib_combine_itag(itag, &res); dev_match = false; @@ -287,17 +285,14 @@ int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, u8 tos, ret = 0; if (fib_lookup(net, &fl4, &res) == 0) { - if (res.type == RTN_UNICAST) { - *spec_dst = FIB_RES_PREFSRC(net, res); + if (res.type == RTN_UNICAST) ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST; - } } return ret; last_resort: if (rpf) goto e_rpf; - *spec_dst = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE); *itag = 0; return 0; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 81533e3a23d1..83d56a016625 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -440,7 +440,7 @@ static int rt_cache_seq_show(struct seq_file *seq, void *v) r->rt_key_tos, -1, HHUptod, - r->rt_spec_dst, &len); + 0, &len); seq_printf(seq, "%*s\n", 127 - len, ""); } @@ -1978,7 +1978,6 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, { unsigned int hash; struct rtable *rth; - __be32 spec_dst; struct in_device *in_dev = __in_dev_get_rcu(dev); u32 itag = 0; int err; @@ -1999,10 +1998,8 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, if (ipv4_is_zeronet(saddr)) { if (!ipv4_is_local_multicast(daddr)) goto e_inval; - spec_dst = inet_select_addr(dev, 0, RT_SCOPE_LINK); } else { - err = fib_validate_source(skb, saddr, 0, tos, 0, dev, &spec_dst, - &itag); + err = fib_validate_source(skb, saddr, 0, tos, 0, dev, &itag); if (err < 0) goto e_err; } @@ -2029,7 +2026,6 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, rth->rt_oif = 0; rth->rt_mark = skb->mark; rth->rt_gateway = daddr; - rth->rt_spec_dst= spec_dst; rth->rt_peer_genid = 0; rt_init_peer(rth, dev_net(dev)->ipv4.peers); rth->fi = NULL; @@ -2093,7 +2089,6 @@ static int __mkroute_input(struct sk_buff *skb, int err; struct in_device *out_dev; unsigned int flags = 0; - __be32 spec_dst; u32 itag; /* get a working reference to the output device */ @@ -2105,7 +2100,7 @@ static int __mkroute_input(struct sk_buff *skb, err = fib_validate_source(skb, saddr, daddr, tos, FIB_RES_OIF(*res), - in_dev->dev, &spec_dst, &itag); + in_dev->dev, &itag); if (err < 0) { ip_handle_martian_source(in_dev->dev, in_dev, skb, daddr, saddr); @@ -2157,7 +2152,6 @@ static int __mkroute_input(struct sk_buff *skb, rth->rt_oif = 0; rth->rt_mark = skb->mark; rth->rt_gateway = daddr; - rth->rt_spec_dst= spec_dst; rth->rt_peer_genid = 0; rt_init_peer(rth, &res->table->tb_peers); rth->fi = NULL; @@ -2223,7 +2217,6 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, u32 itag = 0; struct rtable *rth; unsigned int hash; - __be32 spec_dst; int err = -EINVAL; struct net *net = dev_net(dev); @@ -2281,12 +2274,11 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, if (res.type == RTN_LOCAL) { err = fib_validate_source(skb, saddr, daddr, tos, net->loopback_dev->ifindex, - dev, &spec_dst, &itag); + dev, &itag); if (err < 0) goto martian_source_keep_err; if (err) flags |= RTCF_DIRECTSRC; - spec_dst = daddr; goto local_input; } @@ -2302,11 +2294,8 @@ brd_input: if (skb->protocol != htons(ETH_P_IP)) goto e_inval; - if (ipv4_is_zeronet(saddr)) - spec_dst = inet_select_addr(dev, 0, RT_SCOPE_LINK); - else { - err = fib_validate_source(skb, saddr, 0, tos, 0, dev, &spec_dst, - &itag); + if (!ipv4_is_zeronet(saddr)) { + err = fib_validate_source(skb, saddr, 0, tos, 0, dev, &itag); if (err < 0) goto martian_source_keep_err; if (err) @@ -2344,7 +2333,6 @@ local_input: rth->rt_oif = 0; rth->rt_mark = skb->mark; rth->rt_gateway = daddr; - rth->rt_spec_dst= spec_dst; rth->rt_peer_genid = 0; rt_init_peer(rth, net->ipv4.peers); rth->fi = NULL; @@ -2362,7 +2350,6 @@ local_input: no_route: RT_CACHE_STAT_INC(in_no_route); - spec_dst = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE); res.type = RTN_UNREACHABLE; if (err == -ESRCH) err = -ENETUNREACH; @@ -2545,7 +2532,6 @@ static struct rtable *__mkroute_output(const struct fib_result *res, rth->rt_oif = orig_oif; rth->rt_mark = fl4->flowi4_mark; rth->rt_gateway = fl4->daddr; - rth->rt_spec_dst= fl4->saddr; rth->rt_peer_genid = 0; rt_init_peer(rth, (res->table ? &res->table->tb_peers : @@ -2554,12 +2540,9 @@ static struct rtable *__mkroute_output(const struct fib_result *res, RT_CACHE_STAT_INC(out_slow_tot); - if (flags & RTCF_LOCAL) { + if (flags & RTCF_LOCAL) rth->dst.input = ip_local_deliver; - rth->rt_spec_dst = fl4->daddr; - } if (flags & (RTCF_BROADCAST | RTCF_MULTICAST)) { - rth->rt_spec_dst = fl4->saddr; if (flags & RTCF_LOCAL && !(dev_out->flags & IFF_LOOPBACK)) { rth->dst.output = ip_mc_output; @@ -2890,7 +2873,6 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or rt->rt_dst = ort->rt_dst; rt->rt_src = ort->rt_src; rt->rt_gateway = ort->rt_gateway; - rt->rt_spec_dst = ort->rt_spec_dst; rt_transfer_peer(rt, ort); rt->fi = ort->fi; if (rt->fi) @@ -2965,10 +2947,8 @@ static int rt_fill_info(struct net *net, nla_put_u32(skb, RTA_FLOW, rt->dst.tclassid)) goto nla_put_failure; #endif - if (rt_is_input_route(rt)) { - if (nla_put_be32(skb, RTA_PREFSRC, rt->rt_spec_dst)) - goto nla_put_failure; - } else if (rt->rt_src != rt->rt_key_src) { + if (!rt_is_input_route(rt) && + rt->rt_src != rt->rt_key_src) { if (nla_put_be32(skb, RTA_PREFSRC, rt->rt_src)) goto nla_put_failure; } diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 8855d8268552..9815ea0bca7f 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -100,7 +100,6 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, xdst->u.rt.rt_src = rt->rt_src; xdst->u.rt.rt_dst = rt->rt_dst; xdst->u.rt.rt_gateway = rt->rt_gateway; - xdst->u.rt.rt_spec_dst = rt->rt_spec_dst; return 0; } -- cgit v1.2.3 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') 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 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') 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 From dccd2304cc907c4b4d2920eeb24b055320fe942e Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Sun, 24 Jun 2012 12:46:05 +0100 Subject: ARM: 7430/1: sizes.h: move from asm-generic to sizes.h is used throughout the AMBA code and drivers, so the header should be available to everyone in order to driver AMBA/PrimeCell peripherals behind a PCI bridge where the host can be any platform (I'm doing it under x86). At this step includes , to allow a grace period for both in-tree and out-of-tree drivers. Signed-off-by: Alessandro Rubini Acked-by: Giancarlo Asnaghi Acked-by: Linus Walleij Cc: Alan Cox Signed-off-by: Russell King --- include/asm-generic/sizes.h | 49 ++------------------------------------------- include/linux/sizes.h | 47 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 47 deletions(-) create mode 100644 include/linux/sizes.h (limited to 'include') diff --git a/include/asm-generic/sizes.h b/include/asm-generic/sizes.h index ea5d4ef81061..1dcfad9629ef 100644 --- a/include/asm-generic/sizes.h +++ b/include/asm-generic/sizes.h @@ -1,47 +1,2 @@ -/* - * linux/include/asm-generic/sizes.h - * - * 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_GENERIC_SIZES_H__ -#define __ASM_GENERIC_SIZES_H__ - -#define SZ_1 0x00000001 -#define SZ_2 0x00000002 -#define SZ_4 0x00000004 -#define SZ_8 0x00000008 -#define SZ_16 0x00000010 -#define SZ_32 0x00000020 -#define SZ_64 0x00000040 -#define SZ_128 0x00000080 -#define SZ_256 0x00000100 -#define SZ_512 0x00000200 - -#define SZ_1K 0x00000400 -#define SZ_2K 0x00000800 -#define SZ_4K 0x00001000 -#define SZ_8K 0x00002000 -#define SZ_16K 0x00004000 -#define SZ_32K 0x00008000 -#define SZ_64K 0x00010000 -#define SZ_128K 0x00020000 -#define SZ_256K 0x00040000 -#define SZ_512K 0x00080000 - -#define SZ_1M 0x00100000 -#define SZ_2M 0x00200000 -#define SZ_4M 0x00400000 -#define SZ_8M 0x00800000 -#define SZ_16M 0x01000000 -#define SZ_32M 0x02000000 -#define SZ_64M 0x04000000 -#define SZ_128M 0x08000000 -#define SZ_256M 0x10000000 -#define SZ_512M 0x20000000 - -#define SZ_1G 0x40000000 -#define SZ_2G 0x80000000 - -#endif /* __ASM_GENERIC_SIZES_H__ */ +/* This is a placeholder, to be removed over time */ +#include diff --git a/include/linux/sizes.h b/include/linux/sizes.h new file mode 100644 index 000000000000..ce3e8150c174 --- /dev/null +++ b/include/linux/sizes.h @@ -0,0 +1,47 @@ +/* + * include/linux/sizes.h + * + * 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 __LINUX_SIZES_H__ +#define __LINUX_SIZES_H__ + +#define SZ_1 0x00000001 +#define SZ_2 0x00000002 +#define SZ_4 0x00000004 +#define SZ_8 0x00000008 +#define SZ_16 0x00000010 +#define SZ_32 0x00000020 +#define SZ_64 0x00000040 +#define SZ_128 0x00000080 +#define SZ_256 0x00000100 +#define SZ_512 0x00000200 + +#define SZ_1K 0x00000400 +#define SZ_2K 0x00000800 +#define SZ_4K 0x00001000 +#define SZ_8K 0x00002000 +#define SZ_16K 0x00004000 +#define SZ_32K 0x00008000 +#define SZ_64K 0x00010000 +#define SZ_128K 0x00020000 +#define SZ_256K 0x00040000 +#define SZ_512K 0x00080000 + +#define SZ_1M 0x00100000 +#define SZ_2M 0x00200000 +#define SZ_4M 0x00400000 +#define SZ_8M 0x00800000 +#define SZ_16M 0x01000000 +#define SZ_32M 0x02000000 +#define SZ_64M 0x04000000 +#define SZ_128M 0x08000000 +#define SZ_256M 0x10000000 +#define SZ_512M 0x20000000 + +#define SZ_1G 0x40000000 +#define SZ_2G 0x80000000 + +#endif /* __LINUX_SIZES_H__ */ -- cgit v1.2.3 From b102f1d0f1cd0bb5ec82e5aeb1e33502d6ad6710 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 27 Jun 2012 09:41:39 +0900 Subject: tracing/kvm: Use __print_hex() for kvm_emulate_insn tracepoint The kvm_emulate_insn tracepoint used __print_insn() for printing its instructions. However it makes the format of the event hard to parse as it reveals TP internals. Fortunately, kernel provides __print_hex for almost same purpose, we can use it instead of open coding it. The user-space can be changed to parse it later. That means raw kernel tracing will not be affected by this change: # cd /sys/kernel/debug/tracing/ # cat events/kvm/kvm_emulate_insn/format name: kvm_emulate_insn ID: 29 format: ... print fmt: "%x:%llx:%s (%s)%s", REC->csbase, REC->rip, __print_hex(REC->insn, REC->len), \ __print_symbolic(REC->flags, { 0, "real" }, { (1 << 0) | (1 << 1), "vm16" }, \ { (1 << 0), "prot16" }, { (1 << 0) | (1 << 2), "prot32" }, { (1 << 0) | (1 << 3), "prot64" }), \ REC->failed ? " failed" : "" # echo 1 > events/kvm/kvm_emulate_insn/enable # cat trace # tracer: nop # # entries-in-buffer/entries-written: 2183/2183 #P:12 # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | qemu-kvm-1782 [002] ...1 140.931636: kvm_emulate_insn: 0:c102fa25:89 10 (prot32) qemu-kvm-1781 [004] ...1 140.931637: kvm_emulate_insn: 0:c102fa25:89 10 (prot32) Link: http://lkml.kernel.org/n/tip-wfw6y3b9ugtey8snaow9nmg5@git.kernel.org Link: http://lkml.kernel.org/r/1340757701-10711-2-git-send-email-namhyung@kernel.org Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Namhyung Kim Cc: kvm@vger.kernel.org Acked-by: Avi Kivity Signed-off-by: Namhyung Kim Signed-off-by: Steven Rostedt --- arch/x86/kvm/trace.h | 12 +----------- include/trace/ftrace.h | 1 + 2 files changed, 2 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h index 911d2641f14c..62d02e3c3ed6 100644 --- a/arch/x86/kvm/trace.h +++ b/arch/x86/kvm/trace.h @@ -710,16 +710,6 @@ TRACE_EVENT(kvm_skinit, __entry->rip, __entry->slb) ); -#define __print_insn(insn, ilen) ({ \ - int i; \ - const char *ret = p->buffer + p->len; \ - \ - for (i = 0; i < ilen; ++i) \ - trace_seq_printf(p, " %02x", insn[i]); \ - trace_seq_printf(p, "%c", 0); \ - ret; \ - }) - #define KVM_EMUL_INSN_F_CR0_PE (1 << 0) #define KVM_EMUL_INSN_F_EFL_VM (1 << 1) #define KVM_EMUL_INSN_F_CS_D (1 << 2) @@ -786,7 +776,7 @@ TRACE_EVENT(kvm_emulate_insn, TP_printk("%x:%llx:%s (%s)%s", __entry->csbase, __entry->rip, - __print_insn(__entry->insn, __entry->len), + __print_hex(__entry->insn, __entry->len), __print_symbolic(__entry->flags, kvm_trace_symbol_emul_flags), __entry->failed ? " failed" : "" diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 769724944fc6..c6bc2faaf261 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -571,6 +571,7 @@ static inline void ftrace_test_probe_##call(void) \ #undef __print_flags #undef __print_symbolic +#undef __print_hex #undef __get_dynamic_array #undef __get_str -- cgit v1.2.3 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') 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 From bfeea1dc1c9238f9e5a17a265489ecd0d19f66bb Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 20 Jun 2012 09:58:35 -0400 Subject: SUNRPC: Don't decode beyond the end of the RPC reply message Now that xdr_inline_decode() will automatically cross into the page buffers, we need to ensure that it doesn't exceed the total reply message length. This patch sets up a counter that tracks the number of words remaining in the reply message, and ensures that xdr_inline_decode, xdr_read_pages and xdr_enter_page respect the end of message boundary. Signed-off-by: Trond Myklebust --- include/linux/sunrpc/xdr.h | 1 + net/sunrpc/xdr.c | 28 +++++++++++++++++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index af70af333546..f1e7f884d1c3 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -205,6 +205,7 @@ struct xdr_stream { struct kvec *iov; /* pointer to the current kvec */ struct kvec scratch; /* Scratch buffer */ struct page **page_ptr; /* pointer to the current page */ + unsigned int nwords; /* Remaining decode buffer length */ }; /* diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 21d041cf7f65..5643feb6c645 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -630,12 +630,15 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) xdr->buf = buf; xdr->scratch.iov_base = NULL; xdr->scratch.iov_len = 0; + xdr->nwords = XDR_QUADLEN(buf->len); if (buf->head[0].iov_len != 0) xdr_set_iov(xdr, buf->head, buf->len); else if (buf->page_len != 0) xdr_set_page_base(xdr, 0, buf->len); - if (p != NULL && p > xdr->p && xdr->end >= p) + if (p != NULL && p > xdr->p && xdr->end >= p) { + xdr->nwords -= p - xdr->p; xdr->p = p; + } } EXPORT_SYMBOL_GPL(xdr_init_decode); @@ -660,12 +663,14 @@ EXPORT_SYMBOL_GPL(xdr_init_decode_pages); static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) { + unsigned int nwords = XDR_QUADLEN(nbytes); __be32 *p = xdr->p; - __be32 *q = p + XDR_QUADLEN(nbytes); + __be32 *q = p + nwords; - if (unlikely(q > xdr->end || q < p)) + if (unlikely(nwords > xdr->nwords || q > xdr->end || q < p)) return NULL; xdr->p = q; + xdr->nwords -= nwords; return p; } @@ -746,9 +751,16 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) struct xdr_buf *buf = xdr->buf; struct kvec *iov; ssize_t shift; + unsigned int nwords = XDR_QUADLEN(len); unsigned int end; int padding; + if (xdr->nwords == 0) + return; + if (nwords > xdr->nwords) { + nwords = xdr->nwords; + len = nwords << 2; + } /* Realign pages to current pointer position */ iov = buf->head; shift = iov->iov_len + (char *)iov->iov_base - (char *)xdr->p; @@ -758,15 +770,15 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) /* Truncate page data and move it into the tail */ if (buf->page_len > len) xdr_shrink_pagelen(buf, buf->page_len - len); - padding = (XDR_QUADLEN(len) << 2) - len; + padding = (nwords << 2) - len; xdr->iov = iov = buf->tail; /* Compute remaining message length. */ end = iov->iov_len; shift = buf->buflen - buf->len; - if (shift < end) + if (end > shift + padding) end -= shift; - else if (shift > 0) - end = 0; + else + end = padding; /* * Position current pointer at beginning of tail, and * set remaining message length. @@ -774,6 +786,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) xdr->p = (__be32 *)((char *)iov->iov_base + padding); xdr->end = (__be32 *)((char *)iov->iov_base + end); xdr->page_ptr = NULL; + xdr->nwords = XDR_QUADLEN(end - padding); } EXPORT_SYMBOL_GPL(xdr_read_pages); @@ -795,6 +808,7 @@ void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) * set remaining message length. */ xdr_set_page_base(xdr, 0, len); + xdr->nwords += XDR_QUADLEN(xdr->buf->page_len); } EXPORT_SYMBOL_GPL(xdr_enter_page); -- cgit v1.2.3 From c337d3655ce85e12c7c476cb81406521926cacd2 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 21 Jun 2012 17:05:37 -0400 Subject: SUNRPC: xdr_read_pages should return the amount of XDR encoded page data Callers of xdr_read_pages() will want to know exactly how much XDR data is encoded in the pages after the data realignment. Signed-off-by: Trond Myklebust --- include/linux/sunrpc/xdr.h | 2 +- net/sunrpc/xdr.c | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index f1e7f884d1c3..2489bce1fb88 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -223,7 +223,7 @@ extern void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf, struct page **pages, unsigned int len); extern void xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen); extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes); -extern void xdr_read_pages(struct xdr_stream *xdr, unsigned int len); +extern unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len); extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len); extern int xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len, int (*actor)(struct scatterlist *, void *), void *data); diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 5643feb6c645..80d518644cf8 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -745,8 +745,10 @@ EXPORT_SYMBOL_GPL(xdr_inline_decode); * Moves data beyond the current pointer position from the XDR head[] buffer * into the page list. Any data that lies beyond current position + "len" * bytes is moved into the XDR tail[]. + * + * Returns the number of XDR encoded bytes now contained in the pages */ -void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) +unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len) { struct xdr_buf *buf = xdr->buf; struct kvec *iov; @@ -756,7 +758,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) int padding; if (xdr->nwords == 0) - return; + return 0; if (nwords > xdr->nwords) { nwords = xdr->nwords; len = nwords << 2; @@ -787,6 +789,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) xdr->end = (__be32 *)((char *)iov->iov_base + end); xdr->page_ptr = NULL; xdr->nwords = XDR_QUADLEN(end - padding); + return len; } EXPORT_SYMBOL_GPL(xdr_read_pages); @@ -802,7 +805,7 @@ EXPORT_SYMBOL_GPL(xdr_read_pages); */ void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) { - xdr_read_pages(xdr, len); + len = xdr_read_pages(xdr, len); /* * Position current pointer at beginning of tail, and * set remaining message length. -- cgit v1.2.3 From 4517d526c8aa31b5c14165ef180cc19518ff0a35 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 21 Jun 2012 17:14:46 -0400 Subject: SUNRPC: Add the helper xdr_stream_pos Add a helper to report the current offset from the start of the xdr_stream. Signed-off-by: Trond Myklebust --- include/linux/sunrpc/xdr.h | 1 + net/sunrpc/xdr.c | 10 ++++++++++ 2 files changed, 11 insertions(+) (limited to 'include') diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 2489bce1fb88..647faf2289a7 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -218,6 +218,7 @@ extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes); extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base, unsigned int len); +extern unsigned int xdr_stream_pos(const struct xdr_stream *xdr); extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); extern void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf, struct page **pages, unsigned int len); diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 80d518644cf8..95980a5cd0a8 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -454,6 +454,16 @@ xdr_shift_buf(struct xdr_buf *buf, size_t len) } EXPORT_SYMBOL_GPL(xdr_shift_buf); +/** + * xdr_stream_pos - Return the current offset from the start of the xdr_stream + * @xdr: pointer to struct xdr_stream + */ +unsigned int xdr_stream_pos(const struct xdr_stream *xdr) +{ + return (unsigned int)(XDR_QUADLEN(xdr->buf->len) - xdr->nwords) << 2; +} +EXPORT_SYMBOL_GPL(xdr_stream_pos); + /** * xdr_init_encode - Initialize a struct xdr_stream for sending data. * @xdr: pointer to xdr_stream struct -- cgit v1.2.3 From 140150dbb1f9cf3ef963fb55505f994d74ff3276 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 5 Jun 2012 15:20:25 -0400 Subject: SUNRPC: Remove unused function xdr_encode_pages Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 4 +--- include/linux/sunrpc/xdr.h | 2 -- net/sunrpc/xdr.c | 28 ---------------------------- 3 files changed, 1 insertion(+), 33 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 15fc7e4664ed..5a7b3723cc6f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2766,9 +2766,7 @@ static int nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry) * * In the case of WRITE, we also want to put the GETATTR after * the operation -- in this case because we want to make sure - * we get the post-operation mtime and size. This means that - * we can't use xdr_encode_pages() as written: we need a variant - * of it which would leave room in the 'tail' iovec. + * we get the post-operation mtime and size. * * Both of these changes to the XDR layer would in fact be quite * minor, but I decided to leave them for a subsequent patch. diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 647faf2289a7..63988990bd36 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -104,8 +104,6 @@ __be32 *xdr_decode_string_inplace(__be32 *p, char **sp, unsigned int *lenp, __be32 *xdr_encode_netobj(__be32 *p, const struct xdr_netobj *); __be32 *xdr_decode_netobj(__be32 *p, struct xdr_netobj *); -void xdr_encode_pages(struct xdr_buf *, struct page **, unsigned int, - unsigned int); void xdr_inline_pages(struct xdr_buf *, unsigned int, struct page **, unsigned int, unsigned int); void xdr_terminate_string(struct xdr_buf *, const u32); diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 2e3694eccd82..d65d380571bc 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -128,34 +128,6 @@ xdr_terminate_string(struct xdr_buf *buf, const u32 len) } EXPORT_SYMBOL_GPL(xdr_terminate_string); -void -xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base, - unsigned int len) -{ - struct kvec *tail = xdr->tail; - u32 *p; - - xdr->pages = pages; - xdr->page_base = base; - xdr->page_len = len; - - p = (u32 *)xdr->head[0].iov_base + XDR_QUADLEN(xdr->head[0].iov_len); - tail->iov_base = p; - tail->iov_len = 0; - - if (len & 3) { - unsigned int pad = 4 - (len & 3); - - *p = 0; - tail->iov_base = (char *)p + (len & 3); - tail->iov_len = pad; - len += pad; - } - xdr->buflen += len; - xdr->len += len; -} -EXPORT_SYMBOL_GPL(xdr_encode_pages); - void xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, struct page **pages, unsigned int base, unsigned int len) -- cgit v1.2.3 From 2f2c63bc221c5fcded24de2704575d0abf96b910 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 8 Jun 2012 11:56:09 -0400 Subject: NFS: Cleanup - only store the write verifier in struct nfs_page The 'committed' field is not needed once we have put the struct nfs_page on the right list. Also correct the type of the verifier: it is not an array of __be32, but simply an 8 byte long opaque array. Signed-off-by: Trond Myklebust --- fs/nfs/nfs3xdr.c | 12 +++++++----- fs/nfs/nfs4filelayout.c | 6 +++--- fs/nfs/nfs4xdr.c | 12 ++++++++---- fs/nfs/write.c | 4 ++-- include/linux/nfs_page.h | 2 +- include/linux/nfs_xdr.h | 6 +++++- 6 files changed, 26 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 5013bdd85ab9..6cbe89400dfc 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -325,14 +325,14 @@ static void encode_createverf3(struct xdr_stream *xdr, const __be32 *verifier) memcpy(p, verifier, NFS3_CREATEVERFSIZE); } -static int decode_writeverf3(struct xdr_stream *xdr, __be32 *verifier) +static int decode_writeverf3(struct xdr_stream *xdr, struct nfs_write_verifier *verifier) { __be32 *p; p = xdr_inline_decode(xdr, NFS3_WRITEVERFSIZE); if (unlikely(p == NULL)) goto out_overflow; - memcpy(verifier, p, NFS3_WRITEVERFSIZE); + memcpy(verifier->data, p, NFS3_WRITEVERFSIZE); return 0; out_overflow: print_overflow_msg(__func__, xdr); @@ -1668,20 +1668,22 @@ static int decode_write3resok(struct xdr_stream *xdr, { __be32 *p; - p = xdr_inline_decode(xdr, 4 + 4 + NFS3_WRITEVERFSIZE); + p = xdr_inline_decode(xdr, 4 + 4); if (unlikely(p == NULL)) goto out_overflow; result->count = be32_to_cpup(p++); result->verf->committed = be32_to_cpup(p++); if (unlikely(result->verf->committed > NFS_FILE_SYNC)) goto out_badvalue; - memcpy(result->verf->verifier, p, NFS3_WRITEVERFSIZE); + if (decode_writeverf3(xdr, &result->verf->verifier)) + goto out_eio; return result->count; out_badvalue: dprintk("NFS: bad stable_how value: %u\n", result->verf->committed); return -EIO; out_overflow: print_overflow_msg(__func__, xdr); +out_eio: return -EIO; } @@ -2314,7 +2316,7 @@ static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req, goto out; if (status != NFS3_OK) goto out_status; - error = decode_writeverf3(xdr, result->verf->verifier); + error = decode_writeverf3(xdr, &result->verf->verifier); out: return error; out_status: diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index e1340293872c..85b70639921b 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c @@ -351,9 +351,9 @@ static void prepare_to_resend_writes(struct nfs_commit_data *data) struct nfs_page *first = nfs_list_entry(data->pages.next); data->task.tk_status = 0; - memcpy(data->verf.verifier, first->wb_verf.verifier, - sizeof(first->wb_verf.verifier)); - data->verf.verifier[0]++; /* ensure verifier mismatch */ + memcpy(&data->verf.verifier, &first->wb_verf, + sizeof(data->verf.verifier)); + data->verf.verifier.data[0]++; /* ensure verifier mismatch */ } static int filelayout_commit_done_cb(struct rpc_task *task, diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 1e2c47b3889d..610ebccbde5d 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -4158,13 +4158,18 @@ static int decode_verifier(struct xdr_stream *xdr, void *verifier) return decode_opaque_fixed(xdr, verifier, NFS4_VERIFIER_SIZE); } +static int decode_write_verifier(struct xdr_stream *xdr, struct nfs_write_verifier *verifier) +{ + return decode_opaque_fixed(xdr, verifier->data, NFS4_VERIFIER_SIZE); +} + static int decode_commit(struct xdr_stream *xdr, struct nfs_commitres *res) { int status; status = decode_op_hdr(xdr, OP_COMMIT); if (!status) - status = decode_verifier(xdr, res->verf->verifier); + status = decode_write_verifier(xdr, &res->verf->verifier); return status; } @@ -5192,13 +5197,12 @@ static int decode_write(struct xdr_stream *xdr, struct nfs_writeres *res) if (status) return status; - p = xdr_inline_decode(xdr, 16); + p = xdr_inline_decode(xdr, 8); if (unlikely(!p)) goto out_overflow; res->count = be32_to_cpup(p++); res->verf->committed = be32_to_cpup(p++); - memcpy(res->verf->verifier, p, NFS4_VERIFIER_SIZE); - return 0; + return decode_write_verifier(xdr, &res->verf->verifier); out_overflow: print_overflow_msg(__func__, xdr); return -EIO; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 4d6861c0dc14..ee929e5e1f7b 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -620,7 +620,7 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr) goto next; } if (test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags)) { - memcpy(&req->wb_verf, hdr->verf, sizeof(req->wb_verf)); + memcpy(&req->wb_verf, &hdr->verf->verifier, sizeof(req->wb_verf)); nfs_mark_request_commit(req, hdr->lseg, &cinfo); goto next; } @@ -1547,7 +1547,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data) /* Okay, COMMIT succeeded, apparently. Check the verifier * returned by the server against all stored verfs. */ - if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) { + if (!memcmp(&req->wb_verf, &data->verf.verifier, sizeof(req->wb_verf))) { /* We have a match */ nfs_inode_remove_request(req); dprintk(" OK\n"); diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 88d166b555e8..880805774f9f 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -42,7 +42,7 @@ struct nfs_page { wb_bytes; /* Length of request */ struct kref wb_kref; /* reference count */ unsigned long wb_flags; - struct nfs_writeverf wb_verf; /* Commit cookie */ + struct nfs_write_verifier wb_verf; /* Commit cookie */ }; struct nfs_pageio_descriptor; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 8aadd90b808a..5c0014d1c969 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -514,9 +514,13 @@ struct nfs_writeargs { struct nfs4_sequence_args seq_args; }; +struct nfs_write_verifier { + char data[8]; +}; + struct nfs_writeverf { + struct nfs_write_verifier verifier; enum nfs3_stable_how committed; - __be32 verifier[2]; }; struct nfs_writeres { -- cgit v1.2.3 From 3840a06e6046aaee95f33a120499d2dc8c054b9d Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Thu, 28 Jun 2012 12:34:19 +0000 Subject: tcp: pass fl6 to inet6_csk_route_req() This commit changes inet_csk_route_req() so that it uses a pointer to a struct flowi6, rather than allocating its own on the stack. This brings its behavior in line with its IPv4 cousin, inet_csk_route_req(), and allows a follow-on patch to fix a dst leak. Signed-off-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/inet6_connection_sock.h | 1 + net/ipv6/inet6_connection_sock.c | 26 +++++++++++++------------- net/ipv6/tcp_ipv6.c | 9 ++++++--- 3 files changed, 20 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/inet6_connection_sock.h b/include/net/inet6_connection_sock.h index 1866a676c810..df2a857e853d 100644 --- a/include/net/inet6_connection_sock.h +++ b/include/net/inet6_connection_sock.h @@ -26,6 +26,7 @@ extern int inet6_csk_bind_conflict(const struct sock *sk, const struct inet_bind_bucket *tb, bool relax); extern struct dst_entry* inet6_csk_route_req(struct sock *sk, + struct flowi6 *fl6, const struct request_sock *req); extern struct request_sock *inet6_csk_search_req(const struct sock *sk, diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index e23d35424ca9..bceb14450a1d 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -55,26 +55,26 @@ int inet6_csk_bind_conflict(const struct sock *sk, EXPORT_SYMBOL_GPL(inet6_csk_bind_conflict); struct dst_entry *inet6_csk_route_req(struct sock *sk, + struct flowi6 *fl6, const struct request_sock *req) { struct inet6_request_sock *treq = inet6_rsk(req); struct ipv6_pinfo *np = inet6_sk(sk); struct in6_addr *final_p, final; struct dst_entry *dst; - struct flowi6 fl6; - - memset(&fl6, 0, sizeof(fl6)); - fl6.flowi6_proto = IPPROTO_TCP; - fl6.daddr = treq->rmt_addr; - final_p = fl6_update_dst(&fl6, np->opt, &final); - fl6.saddr = treq->loc_addr; - fl6.flowi6_oif = treq->iif; - fl6.flowi6_mark = sk->sk_mark; - fl6.fl6_dport = inet_rsk(req)->rmt_port; - fl6.fl6_sport = inet_rsk(req)->loc_port; - security_req_classify_flow(req, flowi6_to_flowi(&fl6)); - dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false); + memset(fl6, 0, sizeof(*fl6)); + fl6->flowi6_proto = IPPROTO_TCP; + fl6->daddr = treq->rmt_addr; + final_p = fl6_update_dst(fl6, np->opt, &final); + fl6->saddr = treq->loc_addr; + fl6->flowi6_oif = treq->iif; + fl6->flowi6_mark = sk->sk_mark; + fl6->fl6_dport = inet_rsk(req)->rmt_port; + fl6->fl6_sport = inet_rsk(req)->loc_port; + security_req_classify_flow(req, flowi6_to_flowi(fl6)); + + dst = ip6_dst_lookup_flow(sk, fl6, final_p, false); if (IS_ERR(dst)) return NULL; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index fc0b96bf9051..4e5fa5f6ec68 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -477,7 +477,8 @@ out: } -static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req, +static int tcp_v6_send_synack(struct sock *sk, + struct request_sock *req, struct request_values *rvp, u16 queue_mapping) { @@ -1058,6 +1059,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) struct tcp_sock *tp = tcp_sk(sk); __u32 isn = TCP_SKB_CB(skb)->when; struct dst_entry *dst = NULL; + struct flowi6 fl6; bool want_cookie = false; if (skb->protocol == htons(ETH_P_IP)) @@ -1177,7 +1179,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) */ if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle && - (dst = inet6_csk_route_req(sk, req)) != NULL && + (dst = inet6_csk_route_req(sk, &fl6, req)) != NULL && (peer = rt6_get_peer((struct rt6_info *)dst)) != NULL && ipv6_addr_equal((struct in6_addr *)peer->daddr.addr.a6, &treq->rmt_addr)) { @@ -1247,6 +1249,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, #ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *key; #endif + struct flowi6 fl6; if (skb->protocol == htons(ETH_P_IP)) { /* @@ -1309,7 +1312,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, goto out_overflow; if (!dst) { - dst = inet6_csk_route_req(sk, req); + dst = inet6_csk_route_req(sk, &fl6, req); if (!dst) goto out; } -- cgit v1.2.3 From 58050fce3530939372e6c2f4b4beb76fcb4caa65 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Thu, 28 Jun 2012 03:57:45 +0000 Subject: net: Use NLMSG_DEFAULT_SIZE in combination with nlmsg_new() Using NLMSG_GOODSIZE results in multiple pages being used as nlmsg_new() will automatically add the size of the netlink header to the payload thus exceeding the page limit. NLMSG_DEFAULT_SIZE takes this into account. Signed-off-by: Thomas Graf Cc: Jiri Pirko Cc: Dmitry Eremin-Solenikov Cc: Sergey Lapin Cc: Johannes Berg Cc: Lauro Ramos Venancio Cc: Aloisio Almeida Jr Cc: Samuel Ortiz Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 8 ++++---- drivers/net/wireless/mac80211_hwsim.c | 2 +- include/net/genetlink.h | 2 ++ net/ieee802154/netlink.c | 4 ++-- net/ieee802154/nl-mac.c | 2 +- net/ieee802154/nl-phy.c | 2 +- net/l2tp/l2tp_netlink.c | 6 +++--- net/nfc/netlink.c | 18 +++++++++--------- net/wireless/nl80211.c | 26 +++++++++++++------------- 9 files changed, 36 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 5350eeaa22ce..89853c31e7f4 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1476,7 +1476,7 @@ static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info) void *hdr; int err; - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -1538,7 +1538,7 @@ static int team_nl_send_generic(struct genl_info *info, struct team *team, struct sk_buff *skb; int err; - skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!skb) return -ENOMEM; @@ -1648,7 +1648,7 @@ static int __send_and_alloc_skb(struct sk_buff **pskb, if (err) return err; } - *pskb = genlmsg_new(NLMSG_DEFAULT_SIZE - GENL_HDRLEN, GFP_KERNEL); + *pskb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!*pskb) return -ENOMEM; return 0; @@ -2016,7 +2016,7 @@ static int team_nl_send_event_port_list_get(struct team *team) int err; struct net *net = dev_net(team->dev); - skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!skb) return -ENOMEM; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index a0b7cfd34685..a9ba3f7ea62b 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -571,7 +571,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, skb_dequeue(&data->pending); } - skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); + skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (skb == NULL) goto nla_put_failure; diff --git a/include/net/genetlink.h b/include/net/genetlink.h index ccb68880abf5..48905cd3884c 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -5,6 +5,8 @@ #include #include +#define GENLMSG_DEFAULT_SIZE (NLMSG_DEFAULT_SIZE - GENL_HDRLEN) + /** * struct genl_multicast_group - generic netlink multicast group * @name: name of the multicast group, names are per-family diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index c8097ae2482f..97351e1d07a4 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -44,7 +44,7 @@ struct genl_family nl802154_family = { struct sk_buff *ieee802154_nl_create(int flags, u8 req) { void *hdr; - struct sk_buff *msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); + struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); unsigned long f; if (!msg) @@ -80,7 +80,7 @@ struct sk_buff *ieee802154_nl_new_reply(struct genl_info *info, int flags, u8 req) { void *hdr; - struct sk_buff *msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); + struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (!msg) return NULL; diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c index ca92587720f4..1e9917124e75 100644 --- a/net/ieee802154/nl-mac.c +++ b/net/ieee802154/nl-mac.c @@ -530,7 +530,7 @@ static int ieee802154_list_iface(struct sk_buff *skb, if (!dev) return -ENODEV; - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) goto out_dev; diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c index eed291626da6..d54be34cca94 100644 --- a/net/ieee802154/nl-phy.c +++ b/net/ieee802154/nl-phy.c @@ -101,7 +101,7 @@ static int ieee802154_list_phy(struct sk_buff *skb, if (!phy) return -ENODEV; - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) goto out_dev; diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index ddc553e76671..d71cd9229a47 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -72,7 +72,7 @@ static int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info) void *hdr; int ret = -ENOBUFS; - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) { ret = -ENOMEM; goto out; @@ -353,7 +353,7 @@ static int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info) goto out; } - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) { ret = -ENOMEM; goto out; @@ -699,7 +699,7 @@ static int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info) goto out; } - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) { ret = -ENOMEM; goto out; diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 03c31db38f12..f4f07f9b61c0 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -167,7 +167,7 @@ int nfc_genl_targets_found(struct nfc_dev *dev) dev->genl_data.poll_req_pid = 0; - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (!msg) return -ENOMEM; @@ -195,7 +195,7 @@ int nfc_genl_target_lost(struct nfc_dev *dev, u32 target_idx) struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -226,7 +226,7 @@ int nfc_genl_tm_activated(struct nfc_dev *dev, u32 protocol) struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -258,7 +258,7 @@ int nfc_genl_tm_deactivated(struct nfc_dev *dev) struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -288,7 +288,7 @@ int nfc_genl_device_added(struct nfc_dev *dev) struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -321,7 +321,7 @@ int nfc_genl_device_removed(struct nfc_dev *dev) struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -434,7 +434,7 @@ int nfc_genl_dep_link_up_event(struct nfc_dev *dev, u32 target_idx, pr_debug("DEP link is up\n"); - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (!msg) return -ENOMEM; @@ -473,7 +473,7 @@ int nfc_genl_dep_link_down_event(struct nfc_dev *dev) pr_debug("DEP link is down\n"); - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (!msg) return -ENOMEM; @@ -514,7 +514,7 @@ static int nfc_genl_get_device(struct sk_buff *skb, struct genl_info *info) if (!dev) return -ENODEV; - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) { rc = -ENOMEM; goto out_putdev; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7ae54b82291f..cbdc0fd67a14 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7210,7 +7210,7 @@ void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, { struct sk_buff *msg; - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return; @@ -7286,7 +7286,7 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, { struct sk_buff *msg; - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return; @@ -7502,7 +7502,7 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; @@ -7542,7 +7542,7 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; @@ -7580,7 +7580,7 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return; @@ -7842,7 +7842,7 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, { struct sk_buff *msg; - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; @@ -7863,7 +7863,7 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; @@ -8026,7 +8026,7 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, struct nlattr *pinfoattr; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; @@ -8069,7 +8069,7 @@ void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, struct nlattr *rekey_attr; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; @@ -8113,7 +8113,7 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, struct nlattr *attr; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; @@ -8157,7 +8157,7 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, struct sk_buff *msg; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; @@ -8192,7 +8192,7 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, struct nlattr *pinfoattr; void *hdr; - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; @@ -8236,7 +8236,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, void *hdr; int err; - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; -- cgit v1.2.3 From 9e56e3800ea42e78b7c816bdd2d87d047be80541 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 28 Jun 2012 18:54:02 -0700 Subject: ipv4: Adjust in_dev handling in fib_validate_source() Checking for in_dev being NULL is pointless. In fact, all of our callers have in_dev precomputed already, so just pass it in and remove the NULL checking. Signed-off-by: David S. Miller --- include/net/ip_fib.h | 2 +- net/ipv4/fib_frontend.c | 27 ++++++++++----------------- net/ipv4/route.c | 10 ++++++---- 3 files changed, 17 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 9e6c26d4ba4c..619f68a7185c 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -233,7 +233,7 @@ extern void ip_fib_init(void); extern __be32 fib_compute_spec_dst(struct sk_buff *skb); extern int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, u8 tos, int oif, struct net_device *dev, - u32 *itag); + struct in_device *idev, u32 *itag); extern void fib_select_default(struct fib_result *res); /* Exported by fib_semantics.c */ diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 1d13217e01ff..c84cff52021e 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -226,15 +226,14 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb) * called with rcu_read_lock() */ int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, u8 tos, - int oif, struct net_device *dev, u32 *itag) + int oif, struct net_device *dev, struct in_device *idev, + u32 *itag) { - struct in_device *in_dev; - struct flowi4 fl4; + int ret, no_addr, rpf, accept_local; struct fib_result res; - int no_addr, rpf, accept_local; - bool dev_match; - int ret; + struct flowi4 fl4; struct net *net; + bool dev_match; fl4.flowi4_oif = 0; fl4.flowi4_iif = oif; @@ -244,19 +243,13 @@ int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, u8 tos, fl4.flowi4_scope = RT_SCOPE_UNIVERSE; no_addr = rpf = accept_local = 0; - in_dev = __in_dev_get_rcu(dev); - if (in_dev) { - no_addr = in_dev->ifa_list == NULL; - - /* Ignore rp_filter for packets protected by IPsec. */ - rpf = secpath_exists(skb) ? 0 : IN_DEV_RPFILTER(in_dev); + no_addr = idev->ifa_list == NULL; - accept_local = IN_DEV_ACCEPT_LOCAL(in_dev); - fl4.flowi4_mark = IN_DEV_SRC_VMARK(in_dev) ? skb->mark : 0; - } + /* Ignore rp_filter for packets protected by IPsec. */ + rpf = secpath_exists(skb) ? 0 : IN_DEV_RPFILTER(idev); - if (in_dev == NULL) - goto e_inval; + accept_local = IN_DEV_ACCEPT_LOCAL(idev); + fl4.flowi4_mark = IN_DEV_SRC_VMARK(idev) ? skb->mark : 0; net = dev_net(dev); if (fib_lookup(net, &fl4, &res)) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 83d56a016625..919d69e60bab 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1999,7 +1999,8 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, if (!ipv4_is_local_multicast(daddr)) goto e_inval; } else { - err = fib_validate_source(skb, saddr, 0, tos, 0, dev, &itag); + err = fib_validate_source(skb, saddr, 0, tos, 0, dev, + in_dev, &itag); if (err < 0) goto e_err; } @@ -2100,7 +2101,7 @@ static int __mkroute_input(struct sk_buff *skb, err = fib_validate_source(skb, saddr, daddr, tos, FIB_RES_OIF(*res), - in_dev->dev, &itag); + in_dev->dev, in_dev, &itag); if (err < 0) { ip_handle_martian_source(in_dev->dev, in_dev, skb, daddr, saddr); @@ -2274,7 +2275,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, if (res.type == RTN_LOCAL) { err = fib_validate_source(skb, saddr, daddr, tos, net->loopback_dev->ifindex, - dev, &itag); + dev, in_dev, &itag); if (err < 0) goto martian_source_keep_err; if (err) @@ -2295,7 +2296,8 @@ brd_input: goto e_inval; if (!ipv4_is_zeronet(saddr)) { - err = fib_validate_source(skb, saddr, 0, tos, 0, dev, &itag); + err = fib_validate_source(skb, saddr, 0, tos, 0, dev, + in_dev, &itag); if (err < 0) goto martian_source_keep_err; if (err) -- cgit v1.2.3 From ff6331e25e3e02de17deef9a1e96334dad29e097 Mon Sep 17 00:00:00 2001 From: Chandrabhanu Mahapatra Date: Tue, 19 Jun 2012 15:08:16 +0530 Subject: OMAPDSS: Add support for LCD3 channel OMAP5 Display Subsystem (DSS) architecture comes with a additional LCD3 channel with its own dedicated overlay manager. The current patch adds LCD3 channel and basic register support for LCD3 channel. It adds register addresses for various Display Controller (DISPC) registers like DISPC_DEFAULT_COLOR, DISPC_TIMING_H, DISPC_DIVISORo, etc. Signed-off-by: Chandrabhanu Mahapatra Signed-off-by: Tomi Valkeinen --- drivers/video/omap2/dss/dispc.h | 26 ++++++++++++++++++++++++++ include/video/omapdss.h | 1 + 2 files changed, 27 insertions(+) (limited to 'include') diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h index f278080e1063..420c980dcbdd 100644 --- a/drivers/video/omap2/dss/dispc.h +++ b/drivers/video/omap2/dss/dispc.h @@ -118,6 +118,8 @@ static inline u16 DISPC_DEFAULT_COLOR(enum omap_channel channel) return 0x0050; case OMAP_DSS_CHANNEL_LCD2: return 0x03AC; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0814; default: BUG(); return 0; @@ -133,6 +135,8 @@ static inline u16 DISPC_TRANS_COLOR(enum omap_channel channel) return 0x0058; case OMAP_DSS_CHANNEL_LCD2: return 0x03B0; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0818; default: BUG(); return 0; @@ -149,6 +153,8 @@ static inline u16 DISPC_TIMING_H(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x0400; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0840; default: BUG(); return 0; @@ -165,6 +171,8 @@ static inline u16 DISPC_TIMING_V(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x0404; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0844; default: BUG(); return 0; @@ -181,6 +189,8 @@ static inline u16 DISPC_POL_FREQ(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x0408; + case OMAP_DSS_CHANNEL_LCD3: + return 0x083C; default: BUG(); return 0; @@ -197,6 +207,8 @@ static inline u16 DISPC_DIVISORo(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x040C; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0838; default: BUG(); return 0; @@ -213,6 +225,8 @@ static inline u16 DISPC_SIZE_MGR(enum omap_channel channel) return 0x0078; case OMAP_DSS_CHANNEL_LCD2: return 0x03CC; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0834; default: BUG(); return 0; @@ -229,6 +243,8 @@ static inline u16 DISPC_DATA_CYCLE1(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03C0; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0828; default: BUG(); return 0; @@ -245,6 +261,8 @@ static inline u16 DISPC_DATA_CYCLE2(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03C4; + case OMAP_DSS_CHANNEL_LCD3: + return 0x082C; default: BUG(); return 0; @@ -261,6 +279,8 @@ static inline u16 DISPC_DATA_CYCLE3(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03C8; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0830; default: BUG(); return 0; @@ -277,6 +297,8 @@ static inline u16 DISPC_CPR_COEF_R(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03BC; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0824; default: BUG(); return 0; @@ -293,6 +315,8 @@ static inline u16 DISPC_CPR_COEF_G(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03B8; + case OMAP_DSS_CHANNEL_LCD3: + return 0x0820; default: BUG(); return 0; @@ -309,6 +333,8 @@ static inline u16 DISPC_CPR_COEF_B(enum omap_channel channel) return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03B4; + case OMAP_DSS_CHANNEL_LCD3: + return 0x081C; default: BUG(); return 0; diff --git a/include/video/omapdss.h b/include/video/omapdss.h index ae6954836ef3..23887b358a49 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -75,6 +75,7 @@ enum omap_channel { OMAP_DSS_CHANNEL_LCD = 0, OMAP_DSS_CHANNEL_DIGIT = 1, OMAP_DSS_CHANNEL_LCD2 = 2, + OMAP_DSS_CHANNEL_LCD3 = 3, }; enum omap_color_mode { -- cgit v1.2.3 From e86d456a23f3ecbb97704e63899ecfd6ec54b8d8 Mon Sep 17 00:00:00 2001 From: Chandrabhanu Mahapatra Date: Fri, 29 Jun 2012 10:43:13 +0530 Subject: OMAPDSS: Add LCD3 overlay manager and Clock and IRQ support The support for LCD3 manager has been added into the manager module. LCD3 panel has registers as DISPC_CONTROL3 and DISPC_CONFIG3 just like those in LCD and LCD2 panels. These registers control the Display Controller (DISPC) module for LCD3 output. The three LCDs support Display Serial Interface (DSI), Remote Frame Buffer Interface (RFBI) and Parallel CMOS Output Interface (DPI). These LCDs can be connected through parallel output interface using DISPC and RFBI or DPI. For serial interface DSS uses DSI. The LCD3 panel, just like LCD and LCD2 panels, has a clock switch in DSS_CTRL register which has been enabled. The clock switch chooses between DSS_CLK and DPLL_DSI1_C_CLK1 as source for LCD3_CLK. New IRQs as DISPC_IRQ_VSYNC3, DISPC_IRQ_FRAMEDONE3, DISPC_IRQ_ACBIAS_COUNT_STAT3 and DISPC_IRQ_SYNC_LOST3 have been added specific to the new manager. Signed-off-by: Chandrabhanu Mahapatra Signed-off-by: Tomi Valkeinen --- drivers/video/omap2/dss/dispc.c | 48 ++++++++++++++++++++++++++++++++-- drivers/video/omap2/dss/dispc.h | 2 ++ drivers/video/omap2/dss/dss.c | 12 ++++++--- drivers/video/omap2/dss/dss_features.h | 5 ++-- drivers/video/omap2/dss/manager.c | 4 +++ drivers/video/omap2/dss/overlay.c | 12 ++++++++- include/video/omapdss.h | 4 +++ 7 files changed, 78 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index eb1c9be20d0a..d19665e74e72 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -191,6 +191,23 @@ static const struct { [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG2, 16, 16 }, }, }, + [OMAP_DSS_CHANNEL_LCD3] = { + .name = "LCD3", + .vsync_irq = DISPC_IRQ_VSYNC3, + .framedone_irq = DISPC_IRQ_FRAMEDONE3, + .sync_lost_irq = DISPC_IRQ_SYNC_LOST3, + .reg_desc = { + [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL3, 0, 0 }, + [DISPC_MGR_FLD_STNTFT] = { DISPC_CONTROL3, 3, 3 }, + [DISPC_MGR_FLD_GO] = { DISPC_CONTROL3, 5, 5 }, + [DISPC_MGR_FLD_TFTDATALINES] = { DISPC_CONTROL3, 9, 8 }, + [DISPC_MGR_FLD_STALLMODE] = { DISPC_CONTROL3, 11, 11 }, + [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG3, 10, 10 }, + [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG3, 11, 11 }, + [DISPC_MGR_FLD_CPR] = { DISPC_CONFIG3, 15, 15 }, + [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG3, 16, 16 }, + }, + }, }; static void _omap_dispc_set_irqs(void); @@ -239,6 +256,10 @@ static void dispc_save_context(void) SR(CONTROL2); SR(CONFIG2); } + if (dss_has_feature(FEAT_MGR_LCD3)) { + SR(CONTROL3); + SR(CONFIG3); + } for (i = 0; i < dss_feat_get_num_mgrs(); i++) { SR(DEFAULT_COLOR(i)); @@ -352,6 +373,8 @@ static void dispc_restore_context(void) RR(GLOBAL_ALPHA); if (dss_has_feature(FEAT_MGR_LCD2)) RR(CONFIG2); + if (dss_has_feature(FEAT_MGR_LCD3)) + RR(CONFIG3); for (i = 0; i < dss_feat_get_num_mgrs(); i++) { RR(DEFAULT_COLOR(i)); @@ -437,6 +460,8 @@ static void dispc_restore_context(void) RR(CONTROL); if (dss_has_feature(FEAT_MGR_LCD2)) RR(CONTROL2); + if (dss_has_feature(FEAT_MGR_LCD3)) + RR(CONTROL3); /* clear spurious SYNC_LOST_DIGIT interrupts */ dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT); @@ -476,7 +501,8 @@ void dispc_runtime_put(void) static inline bool dispc_mgr_is_lcd(enum omap_channel channel) { if (channel == OMAP_DSS_CHANNEL_LCD || - channel == OMAP_DSS_CHANNEL_LCD2) + channel == OMAP_DSS_CHANNEL_LCD2 || + channel == OMAP_DSS_CHANNEL_LCD3) return true; else return false; @@ -867,6 +893,15 @@ void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel) chan = 0; chan2 = 1; break; + case OMAP_DSS_CHANNEL_LCD3: + if (dss_has_feature(FEAT_MGR_LCD3)) { + chan = 0; + chan2 = 2; + } else { + BUG(); + return; + } + break; default: BUG(); return; @@ -902,7 +937,14 @@ static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane) val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); - if (dss_has_feature(FEAT_MGR_LCD2)) { + if (dss_has_feature(FEAT_MGR_LCD3)) { + if (FLD_GET(val, 31, 30) == 0) + channel = FLD_GET(val, shift, shift); + else if (FLD_GET(val, 31, 30) == 1) + channel = OMAP_DSS_CHANNEL_LCD2; + else + channel = OMAP_DSS_CHANNEL_LCD3; + } else if (dss_has_feature(FEAT_MGR_LCD2)) { if (FLD_GET(val, 31, 30) == 0) channel = FLD_GET(val, shift, shift); else @@ -3587,6 +3629,8 @@ static void _omap_dispc_initialize_irq(void) dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR; if (dss_has_feature(FEAT_MGR_LCD2)) dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST2; + if (dss_has_feature(FEAT_MGR_LCD3)) + dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST3; if (dss_feat_get_num_ovls() > 3) dispc.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW; diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h index 420c980dcbdd..92d8a9be86fc 100644 --- a/drivers/video/omap2/dss/dispc.h +++ b/drivers/video/omap2/dss/dispc.h @@ -36,6 +36,8 @@ #define DISPC_CONTROL2 0x0238 #define DISPC_CONFIG2 0x0620 #define DISPC_DIVISOR 0x0804 +#define DISPC_CONTROL3 0x0848 +#define DISPC_CONFIG3 0x084C /* DISPC overlay registers */ #define DISPC_OVL_BA0(n) (DISPC_OVL_BASE(n) + \ diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index d2b57197b292..fc0c3ce802e1 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -388,7 +388,8 @@ void dss_select_lcd_clk_source(enum omap_channel channel, dsi_wait_pll_hsdiv_dispc_active(dsidev); break; case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: - BUG_ON(channel != OMAP_DSS_CHANNEL_LCD2); + BUG_ON(channel != OMAP_DSS_CHANNEL_LCD2 && + channel != OMAP_DSS_CHANNEL_LCD3); b = 1; dsidev = dsi_get_dsidev_from_id(1); dsi_wait_pll_hsdiv_dispc_active(dsidev); @@ -398,10 +399,12 @@ void dss_select_lcd_clk_source(enum omap_channel channel, return; } - pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 12; + pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 : + (channel == OMAP_DSS_CHANNEL_LCD2 ? 12 : 19); REG_FLD_MOD(DSS_CONTROL, b, pos, pos); /* LCDx_CLK_SWITCH */ - ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1; + ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : + (channel == OMAP_DSS_CHANNEL_LCD2 ? 1 : 2); dss.lcd_clk_source[ix] = clk_src; } @@ -418,7 +421,8 @@ enum omap_dss_clk_source dss_get_dsi_clk_source(int dsi_module) enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel) { if (dss_has_feature(FEAT_LCD_CLK_SRC)) { - int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1; + int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : + (channel == OMAP_DSS_CHANNEL_LCD2 ? 1 : 2); return dss.lcd_clk_source[ix]; } else { /* LCD_CLK source is the same as DISPC_FCLK source for diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h index bdf469f080e7..996ffcbfed58 100644 --- a/drivers/video/omap2/dss/dss_features.h +++ b/drivers/video/omap2/dss/dss_features.h @@ -24,9 +24,9 @@ #include "ti_hdmi.h" #endif -#define MAX_DSS_MANAGERS 3 +#define MAX_DSS_MANAGERS 4 #define MAX_DSS_OVERLAYS 4 -#define MAX_DSS_LCD_MANAGERS 2 +#define MAX_DSS_LCD_MANAGERS 3 #define MAX_NUM_DSI 2 /* DSS has feature id */ @@ -36,6 +36,7 @@ enum dss_feat_id { FEAT_PCKFREEENABLE, FEAT_FUNCGATED, FEAT_MGR_LCD2, + FEAT_MGR_LCD3, FEAT_LINEBUFFERSPLIT, FEAT_ROWREPEATENABLE, FEAT_RESIZECONF, diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index bb602a2d57c1..a51eb060c142 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -541,6 +541,10 @@ int dss_init_overlay_managers(struct platform_device *pdev) mgr->name = "lcd2"; mgr->id = OMAP_DSS_CHANNEL_LCD2; break; + case 3: + mgr->name = "lcd3"; + mgr->id = OMAP_DSS_CHANNEL_LCD3; + break; } mgr->set_device = &dss_mgr_set_device; diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index c492bb074d0f..ec69325a118a 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -528,14 +528,24 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) struct omap_overlay_manager *lcd_mgr; struct omap_overlay_manager *tv_mgr; struct omap_overlay_manager *lcd2_mgr = NULL; + struct omap_overlay_manager *lcd3_mgr = NULL; struct omap_overlay_manager *mgr = NULL; lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD); tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_DIGIT); + if (dss_has_feature(FEAT_MGR_LCD3)) + lcd3_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD3); if (dss_has_feature(FEAT_MGR_LCD2)) lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD2); - if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) { + if (dssdev->channel == OMAP_DSS_CHANNEL_LCD3) { + if (!lcd3_mgr->device || force) { + if (lcd3_mgr->device) + lcd3_mgr->unset_device(lcd3_mgr); + lcd3_mgr->set_device(lcd3_mgr, dssdev); + mgr = lcd3_mgr; + } + } else if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) { if (!lcd2_mgr->device || force) { if (lcd2_mgr->device) lcd2_mgr->unset_device(lcd2_mgr); diff --git a/include/video/omapdss.h b/include/video/omapdss.h index 23887b358a49..117de0e695f4 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -48,6 +48,10 @@ #define DISPC_IRQ_FRAMEDONEWB (1 << 23) #define DISPC_IRQ_FRAMEDONETV (1 << 24) #define DISPC_IRQ_WBBUFFEROVERFLOW (1 << 25) +#define DISPC_IRQ_FRAMEDONE3 (1 << 26) +#define DISPC_IRQ_VSYNC3 (1 << 27) +#define DISPC_IRQ_ACBIAS_COUNT_STAT3 (1 << 28) +#define DISPC_IRQ_SYNC_LOST3 (1 << 29) struct omap_dss_device; struct omap_overlay_manager; -- cgit v1.2.3 From 5ae9eaa6dbeccab781cd9312371fad801a5ba1a2 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 21 Jun 2012 09:41:10 +0530 Subject: OMAPDSS: Remove passive matrix LCD support (part 2) Remove OMAP_DSS_LCD_TFT as a omap_panel_config flag. We don't support passive matrix displays any more. Remove this flag from all the panel drivers. Force the display_type to OMAP_DSS_LCD_DISPLAY_TFT in the interface drivers. Signed-off-by: Archit Taneja --- drivers/video/omap2/displays/panel-acx565akm.c | 4 +- drivers/video/omap2/displays/panel-generic-dpi.c | 61 +++++++++------------- .../omap2/displays/panel-lgphilips-lb035q02.c | 3 +- drivers/video/omap2/displays/panel-n8x0.c | 1 - .../omap2/displays/panel-nec-nl8048hl11-01b.c | 5 +- drivers/video/omap2/displays/panel-picodlp.c | 4 +- .../video/omap2/displays/panel-sharp-ls037v7dw01.c | 3 +- drivers/video/omap2/displays/panel-taal.c | 1 - drivers/video/omap2/displays/panel-tfp410.c | 1 - .../video/omap2/displays/panel-tpo-td043mtea1.c | 4 +- drivers/video/omap2/dss/display.c | 4 -- drivers/video/omap2/dss/dpi.c | 8 +-- drivers/video/omap2/dss/hdmi_panel.c | 3 +- include/video/omapdss.h | 2 - 14 files changed, 37 insertions(+), 67 deletions(-) (limited to 'include') diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index ad741c3d1ae1..9dc21ddc93da 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -498,8 +498,8 @@ static int acx_panel_probe(struct omap_dss_device *dssdev) struct backlight_properties props; dev_dbg(&dssdev->dev, "%s\n", __func__); - dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS; + dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; + /* FIXME AC bias ? */ dssdev->panel.timings = acx_panel_timings; diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index e42f9dc22123..c17ba743792a 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -76,8 +76,8 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x0, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IEO, .power_on_delay = 50, .power_off_delay = 100, .name = "sharp_lq", @@ -101,8 +101,7 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x28, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 50, .power_off_delay = 100, .name = "sharp_ls", @@ -126,9 +125,9 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x0, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC | - OMAP_DSS_LCD_ONOFF, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IPC | + OMAP_DSS_LCD_ONOFF, .power_on_delay = 0, .power_off_delay = 0, .name = "toppoly_tdo35s", @@ -152,8 +151,7 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x0, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, .power_off_delay = 0, .name = "samsung_lte430wq_f0c", @@ -177,8 +175,7 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x0, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, .power_off_delay = 0, .name = "seiko_70wvw1tz3", @@ -202,8 +199,8 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x0, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IEO, .power_on_delay = 0, .power_off_delay = 0, .name = "powertip_ph480272t", @@ -227,8 +224,7 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x28, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, .power_off_delay = 0, .name = "innolux_at070tn83", @@ -250,8 +246,7 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 2, .vbp = 7, }, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .name = "nec_nl2432dr22-11b", }, @@ -271,8 +266,6 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 1, .vbp = 1, }, - .config = OMAP_DSS_LCD_TFT, - .name = "h4", }, @@ -292,8 +285,7 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 2, .vbp = 2, }, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .name = "apollon", }, @@ -313,8 +305,7 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 13, .vbp = 29, }, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .name = "focaltech_etm070003dh6", }, @@ -336,8 +327,8 @@ static struct panel_config generic_dpi_panels[] = { }, .acbi = 0x0, .acb = 0x0, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IPC, .power_on_delay = 0, .power_off_delay = 0, .name = "microtips_umsh_8173md", @@ -359,8 +350,6 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 4, .vbp = 2, }, - .config = OMAP_DSS_LCD_TFT, - .name = "ortustech_com43h4m10xtc", }, @@ -381,8 +370,8 @@ static struct panel_config generic_dpi_panels[] = { .vbp = 23, }, .acb = 0x0, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IEO, .name = "innolux_at080tn52", }, @@ -402,7 +391,6 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 26, .vbp = 1, }, - .config = OMAP_DSS_LCD_TFT, .name = "mitsubishi_aa084sb01", }, /* EDT ET0500G0DH6 */ @@ -420,7 +408,6 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 35, .vbp = 10, }, - .config = OMAP_DSS_LCD_TFT, .name = "edt_et0500g0dh6", }, @@ -440,8 +427,8 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 10, .vbp = 33, }, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IPC, .name = "primeview_pd050vl1", }, @@ -461,8 +448,8 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 10, .vbp = 33, }, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IPC, .name = "primeview_pm070wl4", }, @@ -482,8 +469,8 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 1, .vbp = 23, }, - .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IPC, .name = "primeview_pd104slf", }, }; diff --git a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c index 0841cc2b3f77..3b1877ce7983 100644 --- a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c +++ b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c @@ -82,8 +82,7 @@ static int lb035q02_panel_probe(struct omap_dss_device *dssdev) struct lb035q02_data *ld; int r; - dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS; + dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; dssdev->panel.timings = lb035q02_timings; ld = kzalloc(sizeof(*ld), GFP_KERNEL); diff --git a/drivers/video/omap2/displays/panel-n8x0.c b/drivers/video/omap2/displays/panel-n8x0.c index 4a34cdc1371b..e6c115373c00 100644 --- a/drivers/video/omap2/displays/panel-n8x0.c +++ b/drivers/video/omap2/displays/panel-n8x0.c @@ -473,7 +473,6 @@ static int n8x0_panel_probe(struct omap_dss_device *dssdev) mutex_init(&ddata->lock); - dssdev->panel.config = OMAP_DSS_LCD_TFT; dssdev->panel.timings.x_res = 800; dssdev->panel.timings.y_res = 480; dssdev->ctrl.pixel_size = 16; diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c index 8b38b39213f4..531440875054 100644 --- a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c +++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c @@ -116,9 +116,8 @@ static int nec_8048_panel_probe(struct omap_dss_device *dssdev) struct backlight_properties props; int r; - dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_RF | - OMAP_DSS_LCD_ONOFF; + dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF; dssdev->panel.timings = nec_8048_panel_timings; necd = kzalloc(sizeof(*necd), GFP_KERNEL); diff --git a/drivers/video/omap2/displays/panel-picodlp.c b/drivers/video/omap2/displays/panel-picodlp.c index 98ebdaddab5a..6563d85e00e4 100644 --- a/drivers/video/omap2/displays/panel-picodlp.c +++ b/drivers/video/omap2/displays/panel-picodlp.c @@ -414,8 +414,8 @@ static int picodlp_panel_probe(struct omap_dss_device *dssdev) struct i2c_client *picodlp_i2c_client; int r = 0, picodlp_adapter_id; - dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_ONOFF | - OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IVS; + dssdev->panel.config = OMAP_DSS_LCD_ONOFF | OMAP_DSS_LCD_IHS | + OMAP_DSS_LCD_IVS; dssdev->panel.acb = 0x0; dssdev->panel.timings = pico_ls_timings; diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c index ba38b3ad17d6..d71386a864f5 100644 --- a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c +++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c @@ -86,8 +86,7 @@ static int sharp_ls_panel_probe(struct omap_dss_device *dssdev) struct sharp_data *sd; int r; - dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IHS; + dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; dssdev->panel.acb = 0x28; dssdev->panel.timings = sharp_ls_timings; diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index 901576eb5a84..3f5acc7771da 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -882,7 +882,6 @@ static int taal_probe(struct omap_dss_device *dssdev) goto err; } - dssdev->panel.config = OMAP_DSS_LCD_TFT; dssdev->panel.timings = panel_config->timings; dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888; diff --git a/drivers/video/omap2/displays/panel-tfp410.c b/drivers/video/omap2/displays/panel-tfp410.c index bff306e041ca..ab5b1d43ca10 100644 --- a/drivers/video/omap2/displays/panel-tfp410.c +++ b/drivers/video/omap2/displays/panel-tfp410.c @@ -95,7 +95,6 @@ static int tfp410_probe(struct omap_dss_device *dssdev) return -ENOMEM; dssdev->panel.timings = tfp410_default_timings; - dssdev->panel.config = OMAP_DSS_LCD_TFT; ddata->dssdev = dssdev; mutex_init(&ddata->lock); diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index 4b6448b3c31f..d9585c895052 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -423,8 +423,8 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev) return -ENODEV; } - dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IPC; + dssdev->panel.config = OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IPC; dssdev->panel.timings = tpo_td043_timings; dssdev->ctrl.pixel_size = 24; diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index 249010630370..25be51e22827 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -327,10 +327,6 @@ bool dss_use_replication(struct omap_dss_device *dssdev, if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16) return false; - if (dssdev->type == OMAP_DISPLAY_TYPE_DPI && - (dssdev->panel.config & OMAP_DSS_LCD_TFT) == 0) - return false; - switch (dssdev->type) { case OMAP_DISPLAY_TYPE_DPI: bpp = dssdev->phy.dpi.data_lines; diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index 09dd2435df11..e6904b2b9fec 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -160,15 +160,11 @@ static int dpi_set_mode(struct omap_dss_device *dssdev) static void dpi_basic_init(struct omap_dss_device *dssdev) { - bool is_tft; - - is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; - dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS); dispc_mgr_enable_stallmode(dssdev->manager->id, false); - dispc_mgr_set_lcd_display_type(dssdev->manager->id, is_tft ? - OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN); + dispc_mgr_set_lcd_display_type(dssdev->manager->id, + OMAP_DSS_LCD_DISPLAY_TFT); dispc_mgr_set_tft_data_lines(dssdev->manager->id, dssdev->phy.dpi.data_lines); } diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 1179e3c4b1c7..788ebf001cb0 100644 --- a/drivers/video/omap2/dss/hdmi_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c @@ -43,8 +43,7 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev) { DSSDBG("ENTER hdmi_panel_probe\n"); - dssdev->panel.config = OMAP_DSS_LCD_TFT | - OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; + dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; dssdev->panel.timings = (struct omap_video_timings){640, 480, 25175, 96, 16, 48, 2 , 11, 31}; diff --git a/include/video/omapdss.h b/include/video/omapdss.h index 117de0e695f4..12bc2dfdcdc4 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -133,8 +133,6 @@ enum omap_panel_config { OMAP_DSS_LCD_IEO = 1<<3, OMAP_DSS_LCD_RF = 1<<4, OMAP_DSS_LCD_ONOFF = 1<<5, - - OMAP_DSS_LCD_TFT = 1<<20, }; enum omap_dss_venc_type { -- cgit v1.2.3 From d21f43bc392911acf01b7f2090615df4ca09ac7d Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 21 Jun 2012 09:45:11 +0530 Subject: OMAPDSS: Remove passive matrix LCD support (part 3) Remove omap_lcd_display_type enum The enum omap_lcd_display_type is used to configure the lcd display type in DISPC. Remove this enum and always set display type to TFT by creating function dss_mgr_set_lcd_type_tft(). Signed-off-by: Archit Taneja --- drivers/video/omap2/dss/dispc.c | 21 ++------------------- drivers/video/omap2/dss/dpi.c | 4 ++-- drivers/video/omap2/dss/dsi.c | 9 +++++---- drivers/video/omap2/dss/dss.h | 3 +-- drivers/video/omap2/dss/rfbi.c | 3 +-- drivers/video/omap2/dss/sdi.c | 3 +-- include/video/omapdss.h | 5 ----- 7 files changed, 12 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 6e25624b3faf..c017d22cad48 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -2490,26 +2490,9 @@ void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable) } -void dispc_mgr_set_lcd_display_type(enum omap_channel channel, - enum omap_lcd_display_type type) +void dispc_mgr_set_lcd_type_tft(enum omap_channel channel) { - int mode; - - switch (type) { - case OMAP_DSS_LCD_DISPLAY_STN: - mode = 0; - break; - - case OMAP_DSS_LCD_DISPLAY_TFT: - mode = 1; - break; - - default: - BUG(); - return; - } - - mgr_fld_write(channel, DISPC_MGR_FLD_STNTFT, mode); + mgr_fld_write(channel, DISPC_MGR_FLD_STNTFT, 1); } void dispc_set_loadmode(enum omap_dss_load_mode mode) diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index e6904b2b9fec..45fb1a673a2a 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -163,8 +163,8 @@ static void dpi_basic_init(struct omap_dss_device *dssdev) dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS); dispc_mgr_enable_stallmode(dssdev->manager->id, false); - dispc_mgr_set_lcd_display_type(dssdev->manager->id, - OMAP_DSS_LCD_DISPLAY_TFT); + dispc_mgr_set_lcd_type_tft(dssdev->manager->id); + dispc_mgr_set_tft_data_lines(dssdev->manager->id, dssdev->phy.dpi.data_lines); } diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index aa3b81088a23..06b578036497 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -4380,10 +4380,11 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev) dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); } - dispc_mgr_set_lcd_display_type(dssdev->manager->id, - OMAP_DSS_LCD_DISPLAY_TFT); - dispc_mgr_set_tft_data_lines(dssdev->manager->id, - dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt)); + dispc_mgr_set_lcd_type_tft(dssdev->manager->id); + + dispc_mgr_set_tft_data_lines(dssdev->manager->id, + dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt)); + return 0; } diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index 88e1c12195e5..692d6501b4e8 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -420,8 +420,7 @@ bool dispc_mgr_is_channel_enabled(enum omap_channel channel); void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode); void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable); void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines); -void dispc_mgr_set_lcd_display_type(enum omap_channel channel, - enum omap_lcd_display_type type); +void dispc_mgr_set_lcd_type_tft(enum omap_channel channel); void dispc_mgr_set_timings(enum omap_channel channel, struct omap_video_timings *timings); void dispc_mgr_set_pol_freq(enum omap_channel channel, diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index 7985fa12b9b4..539d709c6c0e 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -885,8 +885,7 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev) goto err1; } - dispc_mgr_set_lcd_display_type(dssdev->manager->id, - OMAP_DSS_LCD_DISPLAY_TFT); + dispc_mgr_set_lcd_type_tft(dssdev->manager->id); dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_RFBI); dispc_mgr_enable_stallmode(dssdev->manager->id, true); diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index d07ecc44603e..1a369decad38 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -40,8 +40,7 @@ static void sdi_basic_init(struct omap_dss_device *dssdev) dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS); dispc_mgr_enable_stallmode(dssdev->manager->id, false); - dispc_mgr_set_lcd_display_type(dssdev->manager->id, - OMAP_DSS_LCD_DISPLAY_TFT); + dispc_mgr_set_lcd_type_tft(dssdev->manager->id); dispc_mgr_set_tft_data_lines(dssdev->manager->id, 24); dispc_lcd_enable_signal_polarity(1); diff --git a/include/video/omapdss.h b/include/video/omapdss.h index 12bc2dfdcdc4..115bbd849806 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -104,11 +104,6 @@ enum omap_color_mode { OMAP_DSS_COLOR_XRGB16_1555 = 1 << 18, /* xRGB16 - 1555 */ }; -enum omap_lcd_display_type { - OMAP_DSS_LCD_DISPLAY_STN, - OMAP_DSS_LCD_DISPLAY_TFT, -}; - enum omap_dss_load_mode { OMAP_DSS_LOAD_CLUT_AND_FRAME = 0, OMAP_DSS_LOAD_CLUT_ONLY = 1, -- cgit v1.2.3 From a8d5e41cef43bd740ca7c56ff316bdee30040a91 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Mon, 25 Jun 2012 12:26:38 +0530 Subject: OMAPDSS: Add some new fields to omap_video_timings Some panel timing related fields are contained in omap_panel_config in the form of flags. The fields are: - Hsync logic level - Vsync logic level - Data driven on rising/falling edge of pixel clock - Output enable/Data enable logic level - HSYNC/VSYNC driven on rising/falling edge of pixel clock Out of these parameters, Hsync and Vsync logic levels are a part of the timings in the Xorg modeline configuration. So it makes sense to move the to omap_video_timings. The rest aren't a part of modeline, but it still makes sense to move these since they are related to panel timings. These fields stored in omap_panel_config in dssdev are configured for LCD panels, and the corresponding LCD managers in the DISPC_POL_FREQo registers. Add the above fields in omap_video_timings. Represent their state via new enums. Add these parameters to the omap_video_timings instances in the panel drivers. Keep the corresponding IVS, IHS, IPC, IEO, RF and ONOFF flags in omap_panel_config for now. The struct will be removed later. Signed-off-by: Archit Taneja --- drivers/video/omap2/displays/panel-acx565akm.c | 7 ++ drivers/video/omap2/displays/panel-generic-dpi.c | 114 +++++++++++++++++++++ .../omap2/displays/panel-lgphilips-lb035q02.c | 6 ++ .../omap2/displays/panel-nec-nl8048hl11-01b.c | 6 ++ drivers/video/omap2/displays/panel-picodlp.c | 6 ++ .../video/omap2/displays/panel-sharp-ls037v7dw01.c | 6 ++ drivers/video/omap2/displays/panel-tfp410.c | 6 ++ .../video/omap2/displays/panel-tpo-td043mtea1.c | 6 ++ drivers/video/omap2/dss/hdmi_panel.c | 5 +- drivers/video/omap2/dss/sdi.c | 3 + include/video/omapdss.h | 22 ++++ 11 files changed, 186 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index 9dc21ddc93da..acfaa40b932e 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -487,6 +487,13 @@ static struct omap_video_timings acx_panel_timings = { .vfp = 3, .vsw = 3, .vbp = 4, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }; static int acx_panel_probe(struct omap_dss_device *dssdev) diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 0534926dfc74..6cdd97c64330 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -69,6 +69,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 11, .vfp = 3, .vbp = 2, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_LOW, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, @@ -92,6 +98,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 1, .vfp = 1, .vbp = 1, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 50, @@ -114,6 +126,12 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 4, .vsw = 2, .vbp = 2, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC | @@ -138,6 +156,12 @@ static struct panel_config generic_dpi_panels[] = { .vfp = 4, .vsw = 10, .vbp = 12 - 10, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, @@ -160,6 +184,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 2, .vfp = 4, .vbp = 11, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, @@ -182,6 +212,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 10, .vfp = 2, .vbp = 2, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_LOW, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, @@ -205,6 +241,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 3, .vfp = 12, .vbp = 25, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, @@ -227,6 +269,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 1, .vfp = 2, .vbp = 7, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .name = "nec_nl2432dr22-11b", @@ -247,6 +295,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 1, .vfp = 1, .vbp = 1, + + .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .name = "h4", }, @@ -266,6 +320,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 10, .vfp = 2, .vbp = 2, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, @@ -286,6 +346,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 3, .vfp = 13, .vbp = 29, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .name = "focaltech_etm070003dh6", @@ -306,6 +372,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 23, .vfp = 1, .vbp = 1, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, @@ -329,6 +401,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 10, .vfp = 4, .vbp = 2, + + .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .name = "ortustech_com43h4m10xtc", }, @@ -348,6 +426,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 10, .vfp = 12, .vbp = 23, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_LOW, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO, @@ -369,6 +453,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 1, .vfp = 26, .vbp = 1, + + .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .name = "mitsubishi_aa084sb01", }, @@ -386,6 +476,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 2, .vfp = 35, .vbp = 10, + + .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .name = "edt_et0500g0dh6", }, @@ -405,6 +501,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 2, .vfp = 10, .vbp = 33, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, @@ -426,6 +528,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 2, .vfp = 10, .vbp = 33, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, @@ -447,6 +555,12 @@ static struct panel_config generic_dpi_panels[] = { .vsw = 4, .vfp = 1, .vbp = 23, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, diff --git a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c index 3b1877ce7983..fc57ed63b08b 100644 --- a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c +++ b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c @@ -40,6 +40,12 @@ static struct omap_video_timings lb035q02_timings = { .vsw = 2, .vfp = 4, .vbp = 18, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }; static int lb035q02_panel_power_on(struct omap_dss_device *dssdev) diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c index 531440875054..9eab0dda2f33 100644 --- a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c +++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c @@ -76,6 +76,12 @@ static struct omap_video_timings nec_8048_panel_timings = { .vfp = 3, .vsw = 1, .vbp = 4, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, }; static int nec_8048_bl_update_status(struct backlight_device *bl) diff --git a/drivers/video/omap2/displays/panel-picodlp.c b/drivers/video/omap2/displays/panel-picodlp.c index 118e76b7b440..9363cf4ab740 100644 --- a/drivers/video/omap2/displays/panel-picodlp.c +++ b/drivers/video/omap2/displays/panel-picodlp.c @@ -69,6 +69,12 @@ static struct omap_video_timings pico_ls_timings = { .vsw = 2, .vfp = 3, .vbp = 14, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, }; static inline struct picodlp_panel_data diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c index 4285849d27b3..6b7b43c808e2 100644 --- a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c +++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c @@ -44,6 +44,12 @@ static struct omap_video_timings sharp_ls_timings = { .vsw = 1, .vfp = 1, .vbp = 1, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }; static int sharp_ls_bl_update_status(struct backlight_device *bl) diff --git a/drivers/video/omap2/displays/panel-tfp410.c b/drivers/video/omap2/displays/panel-tfp410.c index ab5b1d43ca10..40cc0cfa5d17 100644 --- a/drivers/video/omap2/displays/panel-tfp410.c +++ b/drivers/video/omap2/displays/panel-tfp410.c @@ -39,6 +39,12 @@ static const struct omap_video_timings tfp410_default_timings = { .vfp = 3, .vsw = 4, .vbp = 7, + + .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }; struct panel_drv_data { diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index d9585c895052..e627c6c5c551 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -267,6 +267,12 @@ static const struct omap_video_timings tpo_td043_timings = { .vsw = 1, .vfp = 39, .vbp = 34, + + .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, + .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, + .de_level = OMAPDSS_SIG_ACTIVE_HIGH, + .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }; static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043) diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 788ebf001cb0..5bf3a84d89cd 100644 --- a/drivers/video/omap2/dss/hdmi_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c @@ -45,7 +45,10 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev) dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; - dssdev->panel.timings = (struct omap_video_timings){640, 480, 25175, 96, 16, 48, 2 , 11, 31}; + dssdev->panel.timings = (struct omap_video_timings) + { 640, 480, 25175, 96, 16, 48, 2, 11, 31, + OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW, + }; DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n", dssdev->panel.timings.x_res, diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index 5268fdba955d..4f43537d0214 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -80,6 +80,9 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) /* 15.5.9.1.2 */ dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF; + dssdev->panel.timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; + dssdev->panel.timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; + dispc_mgr_set_pol_freq(dssdev->manager->id, dssdev->panel.config); r = dss_calc_clock_div(t->pixel_clock * 1000, &dss_cinfo, &dispc_cinfo); diff --git a/include/video/omapdss.h b/include/video/omapdss.h index 115bbd849806..be6590dc66a5 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -130,6 +130,17 @@ enum omap_panel_config { OMAP_DSS_LCD_ONOFF = 1<<5, }; +enum omap_dss_signal_level { + OMAPDSS_SIG_ACTIVE_HIGH = 0, + OMAPDSS_SIG_ACTIVE_LOW = 1, +}; + +enum omap_dss_signal_edge { + OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, + OMAPDSS_DRIVE_SIG_RISING_EDGE, + OMAPDSS_DRIVE_SIG_FALLING_EDGE, +}; + enum omap_dss_venc_type { OMAP_DSS_VENC_TYPE_COMPOSITE, OMAP_DSS_VENC_TYPE_SVIDEO, @@ -337,6 +348,17 @@ struct omap_video_timings { u16 vfp; /* Vertical front porch */ /* Unit: line clocks */ u16 vbp; /* Vertical back porch */ + + /* Vsync logic level */ + enum omap_dss_signal_level vsync_level; + /* Hsync logic level */ + enum omap_dss_signal_level hsync_level; + /* Pixel clock edge to drive LCD data */ + enum omap_dss_signal_edge data_pclk_edge; + /* Data enable logic level */ + enum omap_dss_signal_level de_level; + /* Pixel clock edges to drive HSYNC and VSYNC signals */ + enum omap_dss_signal_edge sync_pclk_edge; }; #ifdef CONFIG_OMAP2_DSS_VENC -- cgit v1.2.3 From 07fb51c6bda74210b57a06e6dc901a6b0f04c09a Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Fri, 29 Jun 2012 12:13:32 +0530 Subject: OMAPDSS: Remove omap_panel_config enum from omap_dss_device omap_panel_config contains fields which are finally written to DISPC_POL_FREQo registers. These are now held by omap_video_timings and are set when the manager timings are applied. Remove the omap_panel_config enum, and remove all it's references from panel or interface drivers. Signed-off-by: Archit Taneja --- drivers/video/omap2/displays/panel-acx565akm.c | 1 - drivers/video/omap2/displays/panel-generic-dpi.c | 29 ---------------------- .../omap2/displays/panel-lgphilips-lb035q02.c | 1 - .../omap2/displays/panel-nec-nl8048hl11-01b.c | 2 -- drivers/video/omap2/displays/panel-picodlp.c | 2 -- .../video/omap2/displays/panel-sharp-ls037v7dw01.c | 1 - .../video/omap2/displays/panel-tpo-td043mtea1.c | 2 -- drivers/video/omap2/dss/hdmi_panel.c | 2 -- drivers/video/omap2/dss/sdi.c | 2 -- include/video/omapdss.h | 11 -------- 10 files changed, 53 deletions(-) (limited to 'include') diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index acfaa40b932e..eaeed4340e04 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -505,7 +505,6 @@ static int acx_panel_probe(struct omap_dss_device *dssdev) struct backlight_properties props; dev_dbg(&dssdev->dev, "%s\n", __func__); - dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; /* FIXME AC bias ? */ dssdev->panel.timings = acx_panel_timings; diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 6cdd97c64330..bc5af2500eb9 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -40,8 +40,6 @@ struct panel_config { struct omap_video_timings timings; - enum omap_panel_config config; - int power_on_delay; int power_off_delay; @@ -76,8 +74,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_LOW, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IEO, .power_on_delay = 50, .power_off_delay = 100, .name = "sharp_lq", @@ -105,7 +101,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 50, .power_off_delay = 100, .name = "sharp_ls", @@ -133,9 +128,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IPC | - OMAP_DSS_LCD_ONOFF, .power_on_delay = 0, .power_off_delay = 0, .name = "toppoly_tdo35s", @@ -163,7 +155,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, .power_off_delay = 0, .name = "samsung_lte430wq_f0c", @@ -191,7 +182,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, .power_off_delay = 0, .name = "seiko_70wvw1tz3", @@ -219,8 +209,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_LOW, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IEO, .power_on_delay = 0, .power_off_delay = 0, .name = "powertip_ph480272t", @@ -248,7 +236,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .power_on_delay = 0, .power_off_delay = 0, .name = "innolux_at070tn83", @@ -276,7 +263,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .name = "nec_nl2432dr22-11b", }, @@ -327,8 +313,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, - .name = "apollon", }, /* FocalTech ETM070003DH6 */ @@ -353,7 +337,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS, .name = "focaltech_etm070003dh6", }, @@ -379,8 +362,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IPC, .power_on_delay = 0, .power_off_delay = 0, .name = "microtips_umsh_8173md", @@ -433,9 +414,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_LOW, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IEO, - .name = "innolux_at080tn52", }, @@ -508,8 +486,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IPC, .name = "primeview_pd050vl1", }, @@ -535,8 +511,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IPC, .name = "primeview_pm070wl4", }, @@ -562,8 +536,6 @@ static struct panel_config generic_dpi_panels[] = { .de_level = OMAPDSS_SIG_ACTIVE_HIGH, .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, }, - .config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IPC, .name = "primeview_pd104slf", }, }; @@ -653,7 +625,6 @@ static int generic_dpi_panel_probe(struct omap_dss_device *dssdev) if (!panel_config) return -EINVAL; - dssdev->panel.config = panel_config->config; dssdev->panel.timings = panel_config->timings; drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL); diff --git a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c index fc57ed63b08b..802807798846 100644 --- a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c +++ b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c @@ -88,7 +88,6 @@ static int lb035q02_panel_probe(struct omap_dss_device *dssdev) struct lb035q02_data *ld; int r; - dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; dssdev->panel.timings = lb035q02_timings; ld = kzalloc(sizeof(*ld), GFP_KERNEL); diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c index 9eab0dda2f33..b122b0f31c43 100644 --- a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c +++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c @@ -122,8 +122,6 @@ static int nec_8048_panel_probe(struct omap_dss_device *dssdev) struct backlight_properties props; int r; - dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF; dssdev->panel.timings = nec_8048_panel_timings; necd = kzalloc(sizeof(*necd), GFP_KERNEL); diff --git a/drivers/video/omap2/displays/panel-picodlp.c b/drivers/video/omap2/displays/panel-picodlp.c index 9363cf4ab740..2d35bd388860 100644 --- a/drivers/video/omap2/displays/panel-picodlp.c +++ b/drivers/video/omap2/displays/panel-picodlp.c @@ -420,8 +420,6 @@ static int picodlp_panel_probe(struct omap_dss_device *dssdev) struct i2c_client *picodlp_i2c_client; int r = 0, picodlp_adapter_id; - dssdev->panel.config = OMAP_DSS_LCD_ONOFF | OMAP_DSS_LCD_IHS | - OMAP_DSS_LCD_IVS; dssdev->panel.timings = pico_ls_timings; picod = kzalloc(sizeof(struct picodlp_data), GFP_KERNEL); diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c index 6b7b43c808e2..bd86ba9ccf76 100644 --- a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c +++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c @@ -92,7 +92,6 @@ static int sharp_ls_panel_probe(struct omap_dss_device *dssdev) struct sharp_data *sd; int r; - dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; dssdev->panel.timings = sharp_ls_timings; sd = kzalloc(sizeof(*sd), GFP_KERNEL); diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index e627c6c5c551..fa7baa650ae0 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -429,8 +429,6 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev) return -ENODEV; } - dssdev->panel.config = OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IVS | - OMAP_DSS_LCD_IPC; dssdev->panel.timings = tpo_td043_timings; dssdev->ctrl.pixel_size = 24; diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 5bf3a84d89cd..723a13788476 100644 --- a/drivers/video/omap2/dss/hdmi_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c @@ -43,8 +43,6 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev) { DSSDBG("ENTER hdmi_panel_probe\n"); - dssdev->panel.config = OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; - dssdev->panel.timings = (struct omap_video_timings) { 640, 480, 25175, 96, 16, 48, 2, 11, 31, OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW, diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index 59992a3ed0ee..0fcd4d7e202e 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -78,8 +78,6 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) sdi_basic_init(dssdev); /* 15.5.9.1.2 */ - dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF; - dssdev->panel.timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; dssdev->panel.timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; diff --git a/include/video/omapdss.h b/include/video/omapdss.h index be6590dc66a5..14f261b584fa 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -121,15 +121,6 @@ enum omap_rfbi_te_mode { OMAP_DSS_RFBI_TE_MODE_2 = 2, }; -enum omap_panel_config { - OMAP_DSS_LCD_IVS = 1<<0, - OMAP_DSS_LCD_IHS = 1<<1, - OMAP_DSS_LCD_IPC = 1<<2, - OMAP_DSS_LCD_IEO = 1<<3, - OMAP_DSS_LCD_RF = 1<<4, - OMAP_DSS_LCD_ONOFF = 1<<5, -}; - enum omap_dss_signal_level { OMAPDSS_SIG_ACTIVE_HIGH = 0, OMAPDSS_SIG_ACTIVE_LOW = 1, @@ -572,8 +563,6 @@ struct omap_dss_device { /* Unit: line clocks */ int acb; /* ac-bias pin frequency */ - enum omap_panel_config config; - enum omap_dss_dsi_pixel_format dsi_pix_fmt; enum omap_dss_dsi_mode dsi_mode; struct omap_dss_dsi_videomode_data dsi_vm_data; -- cgit v1.2.3 From 23c8f88e8a140c8435658c369b26c7b60d8fe3c0 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Thu, 28 Jun 2012 11:15:51 +0530 Subject: OMAPDSS: Add interlace parameter to omap_video_timings Add a parameter called interlace which tells whether the timings are in interlaced or progressive mode. This aligns the omap_video_timings struct with the Xorg modeline configuration. It also removes the hack needed to write to divide the manager height by 2 if the connected interface is VENC. Signed-off-by: Archit Taneja --- drivers/video/omap2/dss/dispc.c | 6 +----- drivers/video/omap2/dss/hdmi_panel.c | 1 + drivers/video/omap2/dss/venc.c | 4 ++++ include/video/omapdss.h | 2 ++ 4 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 957c04962793..d200f446840f 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -2754,11 +2754,7 @@ void dispc_mgr_set_timings(enum omap_channel channel, DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); } else { - enum dss_hdmi_venc_clk_source_select source; - - source = dss_get_hdmi_venc_clk_source(); - - if (source == DSS_VENC_TV_CLK) + if (t.interlace == true) t.y_res /= 2; } diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 723a13788476..e10844faadf9 100644 --- a/drivers/video/omap2/dss/hdmi_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c @@ -46,6 +46,7 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev) dssdev->panel.timings = (struct omap_video_timings) { 640, 480, 25175, 96, 16, 48, 2, 11, 31, OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW, + false, }; DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n", diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index 416d478803e5..3a220877461a 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -272,6 +272,8 @@ const struct omap_video_timings omap_dss_pal_timings = { .vsw = 5, .vfp = 5, .vbp = 41, + + .interlace = true, }; EXPORT_SYMBOL(omap_dss_pal_timings); @@ -285,6 +287,8 @@ const struct omap_video_timings omap_dss_ntsc_timings = { .vsw = 6, .vfp = 6, .vbp = 31, + + .interlace = true, }; EXPORT_SYMBOL(omap_dss_ntsc_timings); diff --git a/include/video/omapdss.h b/include/video/omapdss.h index 14f261b584fa..d8ab94485c97 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -344,6 +344,8 @@ struct omap_video_timings { enum omap_dss_signal_level vsync_level; /* Hsync logic level */ enum omap_dss_signal_level hsync_level; + /* Interlaced or Progressive timings */ + bool interlace; /* Pixel clock edge to drive LCD data */ enum omap_dss_signal_edge data_pclk_edge; /* Data enable logic level */ -- cgit v1.2.3 From bd5a7b11a0bfd172b4cd6ef3e01e6beb1753c3f1 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Tue, 26 Jun 2012 12:38:31 +0530 Subject: OMAPDSS: DSI: Fix HSYNC, VSYNC and DE polarities between DISPC and DSI For DSI operation in videomode, DISPC logic levels for the signals HSYNC, VSYNC and DE need to be specified to DSI via the fields VP_HSYNC_POL, VP_VSYNC_POL and VP_DE_POL in DSI_CTRL registers. This information is completely internal to DSS as logic levels for the above signals hold no meaning on the DSI bus. Hence a DSI panel driver should be totally oblivious of these fields. Fix the logic levels/polarities in the DISPC and DSI registers to a default value. This is done by overriding these fields in omap_video_timings struct filled by the panel driver for DISPC, and use the equivalent default values when programming DSI_CTRL registers. Also, remove the redundant polarity related fields in omap_dss_dsi_videomode_data. Signed-off-by: Archit Taneja --- drivers/video/omap2/dss/dsi.c | 44 +++++++++++++++++++++++++------------------ include/video/omapdss.h | 3 --- 2 files changed, 26 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 06b578036497..e0d43b275e3e 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -3631,17 +3631,14 @@ static void dsi_config_vp_num_line_buffers(struct omap_dss_device *dssdev) static void dsi_config_vp_sync_events(struct omap_dss_device *dssdev) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - int de_pol = dssdev->panel.dsi_vm_data.vp_de_pol; - int hsync_pol = dssdev->panel.dsi_vm_data.vp_hsync_pol; - int vsync_pol = dssdev->panel.dsi_vm_data.vp_vsync_pol; bool vsync_end = dssdev->panel.dsi_vm_data.vp_vsync_end; bool hsync_end = dssdev->panel.dsi_vm_data.vp_hsync_end; u32 r; r = dsi_read_reg(dsidev, DSI_CTRL); - r = FLD_MOD(r, de_pol, 9, 9); /* VP_DE_POL */ - r = FLD_MOD(r, hsync_pol, 10, 10); /* VP_HSYNC_POL */ - r = FLD_MOD(r, vsync_pol, 11, 11); /* VP_VSYNC_POL */ + r = FLD_MOD(r, 1, 9, 9); /* VP_DE_POL */ + r = FLD_MOD(r, 1, 10, 10); /* VP_HSYNC_POL */ + r = FLD_MOD(r, 1, 11, 11); /* VP_VSYNC_POL */ r = FLD_MOD(r, 1, 15, 15); /* VP_VSYNC_START */ r = FLD_MOD(r, vsync_end, 16, 16); /* VP_VSYNC_END */ r = FLD_MOD(r, 1, 17, 17); /* VP_HSYNC_START */ @@ -4343,22 +4340,22 @@ EXPORT_SYMBOL(omap_dsi_update); static int dsi_display_init_dispc(struct omap_dss_device *dssdev) { int r; + struct omap_video_timings timings; if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) { u16 dw, dh; u32 irq; - struct omap_video_timings timings = { - .hsw = 1, - .hfp = 1, - .hbp = 1, - .vsw = 1, - .vfp = 0, - .vbp = 0, - }; dssdev->driver->get_resolution(dssdev, &dw, &dh); + timings.x_res = dw; timings.y_res = dh; + timings.hsw = 1; + timings.hfp = 1; + timings.hbp = 1; + timings.vsw = 1; + timings.vfp = 0; + timings.vbp = 0; irq = dispc_mgr_get_framedone_irq(dssdev->manager->id); @@ -4371,15 +4368,26 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev) dispc_mgr_enable_stallmode(dssdev->manager->id, true); dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 1); - - dss_mgr_set_timings(dssdev->manager, &timings); } else { + timings = dssdev->panel.timings; + dispc_mgr_enable_stallmode(dssdev->manager->id, false); dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 0); - - dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); } + /* + * override interlace, logic level and edge related parameters in + * omap_video_timings with default values + */ + timings.interlace = false; + timings.hsync_level = OMAPDSS_SIG_ACTIVE_HIGH; + timings.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH; + timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; + timings.de_level = OMAPDSS_SIG_ACTIVE_HIGH; + timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; + + dss_mgr_set_timings(dssdev->manager, &timings); + dispc_mgr_set_lcd_type_tft(dssdev->manager->id); dispc_mgr_set_tft_data_lines(dssdev->manager->id, diff --git a/include/video/omapdss.h b/include/video/omapdss.h index d8ab94485c97..a6267a2d292b 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -261,9 +261,6 @@ struct omap_dss_dsi_videomode_data { int hfp_blanking_mode; /* Video port sync events */ - int vp_de_pol; - int vp_hsync_pol; - int vp_vsync_pol; bool vp_vsync_end; bool vp_hsync_end; -- cgit v1.2.3 From d0087b29f77176480c27c203988b5704847d617c Mon Sep 17 00:00:00 2001 From: Ville Nuorvala Date: Thu, 28 Jun 2012 18:15:52 +0000 Subject: ipv6_tunnel: Allow receiving packets on the fallback tunnel if they pass sanity checks At Facebook, we do Layer-3 DSR via IP-in-IP tunneling. Our load balancers wrap an extra IP header on incoming packets so they can be routed to the backend. In the v4 tunnel driver, when these packets fall on the default tunl0 device, the behavior is to decapsulate them and drop them back on the stack. So our setup is that tunl0 has the VIP and eth0 has (obviously) the backend's real address. In IPv6 we do the same thing, but the v6 tunnel driver didn't have this same behavior - if you didn't have an explicit tunnel setup, it would drop the packet. This patch brings that v4 feature to the v6 driver. The same IPv6 address checks are performed as with any normal tunnel, but as the fallback tunnel endpoint addresses are unspecified, the checks must be performed on a per-packet basis, rather than at tunnel configuration time. [Patch description modified by phil@ipom.com] Signed-off-by: Ville Nuorvala Tested-by: Phil Dibowitz Signed-off-by: David S. Miller --- include/net/ip6_tunnel.h | 2 ++ net/ipv6/ip6_tunnel.c | 65 ++++++++++++++++++++++++++++-------------------- 2 files changed, 40 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index fc73e667b50e..358fb86f57eb 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -9,6 +9,8 @@ #define IP6_TNL_F_CAP_XMIT 0x10000 /* capable of receiving packets */ #define IP6_TNL_F_CAP_RCV 0x20000 +/* determine capability on a per-packet basis */ +#define IP6_TNL_F_CAP_PER_PACKET 0x40000 /* IPv6 tunnel */ diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index c9015fad8d65..04a3cba2c123 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -684,24 +684,50 @@ static void ip6ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t, IP6_ECN_set_ce(ipv6_hdr(skb)); } +static __u32 ip6_tnl_get_cap(struct ip6_tnl *t, + const struct in6_addr *laddr, + const struct in6_addr *raddr) +{ + struct ip6_tnl_parm *p = &t->parms; + int ltype = ipv6_addr_type(laddr); + int rtype = ipv6_addr_type(raddr); + __u32 flags = 0; + + if (ltype == IPV6_ADDR_ANY || rtype == IPV6_ADDR_ANY) { + flags = IP6_TNL_F_CAP_PER_PACKET; + } else if (ltype & (IPV6_ADDR_UNICAST|IPV6_ADDR_MULTICAST) && + rtype & (IPV6_ADDR_UNICAST|IPV6_ADDR_MULTICAST) && + !((ltype|rtype) & IPV6_ADDR_LOOPBACK) && + (!((ltype|rtype) & IPV6_ADDR_LINKLOCAL) || p->link)) { + if (ltype&IPV6_ADDR_UNICAST) + flags |= IP6_TNL_F_CAP_XMIT; + if (rtype&IPV6_ADDR_UNICAST) + flags |= IP6_TNL_F_CAP_RCV; + } + return flags; +} + /* called with rcu_read_lock() */ -static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t) +static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t, + const struct in6_addr *laddr, + const struct in6_addr *raddr) { struct ip6_tnl_parm *p = &t->parms; int ret = 0; struct net *net = dev_net(t->dev); - if (p->flags & IP6_TNL_F_CAP_RCV) { + if ((p->flags & IP6_TNL_F_CAP_RCV) || + ((p->flags & IP6_TNL_F_CAP_PER_PACKET) && + (ip6_tnl_get_cap(t, laddr, raddr) & IP6_TNL_F_CAP_RCV))) { struct net_device *ldev = NULL; if (p->link) ldev = dev_get_by_index_rcu(net, p->link); - if ((ipv6_addr_is_multicast(&p->laddr) || - likely(ipv6_chk_addr(net, &p->laddr, ldev, 0))) && - likely(!ipv6_chk_addr(net, &p->raddr, NULL, 0))) + if ((ipv6_addr_is_multicast(laddr) || + likely(ipv6_chk_addr(net, laddr, ldev, 0))) && + likely(!ipv6_chk_addr(net, raddr, NULL, 0))) ret = 1; - } return ret; } @@ -740,7 +766,7 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol, goto discard; } - if (!ip6_tnl_rcv_ctl(t)) { + if (!ip6_tnl_rcv_ctl(t, &ipv6h->daddr, &ipv6h->saddr)) { t->dev->stats.rx_dropped++; rcu_read_unlock(); goto discard; @@ -1114,25 +1140,6 @@ tx_err: return NETDEV_TX_OK; } -static void ip6_tnl_set_cap(struct ip6_tnl *t) -{ - struct ip6_tnl_parm *p = &t->parms; - int ltype = ipv6_addr_type(&p->laddr); - int rtype = ipv6_addr_type(&p->raddr); - - p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV); - - if (ltype & (IPV6_ADDR_UNICAST|IPV6_ADDR_MULTICAST) && - rtype & (IPV6_ADDR_UNICAST|IPV6_ADDR_MULTICAST) && - !((ltype|rtype) & IPV6_ADDR_LOOPBACK) && - (!((ltype|rtype) & IPV6_ADDR_LINKLOCAL) || p->link)) { - if (ltype&IPV6_ADDR_UNICAST) - p->flags |= IP6_TNL_F_CAP_XMIT; - if (rtype&IPV6_ADDR_UNICAST) - p->flags |= IP6_TNL_F_CAP_RCV; - } -} - static void ip6_tnl_link_config(struct ip6_tnl *t) { struct net_device *dev = t->dev; @@ -1153,7 +1160,8 @@ static void ip6_tnl_link_config(struct ip6_tnl *t) if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL)) fl6->flowlabel |= IPV6_FLOWLABEL_MASK & p->flowinfo; - ip6_tnl_set_cap(t); + p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV|IP6_TNL_F_CAP_PER_PACKET); + p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr); if (p->flags&IP6_TNL_F_CAP_XMIT && p->flags&IP6_TNL_F_CAP_RCV) dev->flags |= IFF_POINTOPOINT; @@ -1438,6 +1446,9 @@ static int __net_init ip6_fb_tnl_dev_init(struct net_device *dev) t->parms.proto = IPPROTO_IPV6; dev_hold(dev); + + ip6_tnl_link_config(t); + rcu_assign_pointer(ip6n->tnls_wc[0], t); return 0; } -- cgit v1.2.3 From 7a9bc9b81a5bc6e44ebc80ef781332e4385083f2 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 29 Jun 2012 01:32:45 -0700 Subject: ipv4: Elide fib_validate_source() completely when possible. If rpfilter is off (or the SKB has an IPSEC path) and there are not tclassid users, we don't have to do anything at all when fib_validate_source() is invoked besides setting the itag to zero. We monitor tclassid uses with a counter (modified only under RTNL and marked __read_mostly) and we protect the fib_validate_source() real work with a test against this counter and whether rpfilter is to be done. Having a way to know whether we need no tclassid processing or not also opens the door for future optimized rpfilter algorithms that do not perform full FIB lookups. Signed-off-by: David S. Miller --- include/net/fib_rules.h | 1 + include/net/ip_fib.h | 5 +++++ net/core/fib_rules.c | 4 ++++ net/ipv4/fib_frontend.c | 32 ++++++++++++++++++++++++-------- net/ipv4/fib_rules.c | 16 +++++++++++++++- net/ipv4/fib_semantics.c | 10 ++++++++++ 6 files changed, 59 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 075f1e3a0fed..e361f4882426 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -52,6 +52,7 @@ struct fib_rules_ops { struct sk_buff *, struct fib_rule_hdr *, struct nlattr **); + void (*delete)(struct fib_rule *); int (*compare)(struct fib_rule *, struct fib_rule_hdr *, struct nlattr **); diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 619f68a7185c..3dc7c96bbeab 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -235,6 +235,11 @@ extern int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, u8 tos, int oif, struct net_device *dev, struct in_device *idev, u32 *itag); extern void fib_select_default(struct fib_result *res); +#ifdef CONFIG_IP_ROUTE_CLASSID +extern int fib_num_tclassid_users; +#else +#define fib_num_tclassid_users 0 +#endif /* Exported by fib_semantics.c */ extern int ip_fib_check_default(__be32 gw, struct net_device *dev); diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 72cceb79d0d4..ab7db83236c9 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -151,6 +151,8 @@ static void fib_rules_cleanup_ops(struct fib_rules_ops *ops) list_for_each_entry_safe(rule, tmp, &ops->rules_list, list) { list_del_rcu(&rule->list); + if (ops->delete) + ops->delete(rule); fib_rule_put(rule); } } @@ -499,6 +501,8 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) notify_rule_change(RTM_DELRULE, rule, ops, nlh, NETLINK_CB(skb).pid); + if (ops->delete) + ops->delete(rule); fib_rule_put(rule); flush_route_cache(ops); rules_ops_put(ops); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index c84cff52021e..ae528d1b293a 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -217,6 +218,10 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb) return inet_select_addr(dev, ip_hdr(skb)->saddr, scope); } +#ifdef CONFIG_IP_ROUTE_CLASSID +int fib_num_tclassid_users __read_mostly; +#endif + /* Given (packet source, input interface) and optional (dst, oif, tos): * - (main) check, that source is valid i.e. not broadcast or our local * address. @@ -225,11 +230,11 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb) * - check, that packet arrived from expected physical interface. * called with rcu_read_lock() */ -int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, u8 tos, - int oif, struct net_device *dev, struct in_device *idev, - u32 *itag) +static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, + u8 tos, int oif, struct net_device *dev, + int rpf, struct in_device *idev, u32 *itag) { - int ret, no_addr, rpf, accept_local; + int ret, no_addr, accept_local; struct fib_result res; struct flowi4 fl4; struct net *net; @@ -242,12 +247,9 @@ int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, u8 tos, fl4.flowi4_tos = tos; fl4.flowi4_scope = RT_SCOPE_UNIVERSE; - no_addr = rpf = accept_local = 0; + no_addr = accept_local = 0; no_addr = idev->ifa_list == NULL; - /* Ignore rp_filter for packets protected by IPsec. */ - rpf = secpath_exists(skb) ? 0 : IN_DEV_RPFILTER(idev); - accept_local = IN_DEV_ACCEPT_LOCAL(idev); fl4.flowi4_mark = IN_DEV_SRC_VMARK(idev) ? skb->mark : 0; @@ -303,6 +305,20 @@ e_rpf: return -EXDEV; } +/* Ignore rp_filter for packets protected by IPsec. */ +int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, + u8 tos, int oif, struct net_device *dev, + struct in_device *idev, u32 *itag) +{ + int r = secpath_exists(skb) ? 0 : IN_DEV_RPFILTER(idev); + + if (!r && !fib_num_tclassid_users) { + *itag = 0; + return 0; + } + return __fib_validate_source(skb, src, dst, tos, oif, dev, r, idev, itag); +} + static inline __be32 sk_extract_addr(struct sockaddr *addr) { return ((struct sockaddr_in *) addr)->sin_addr.s_addr; diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 2d043f71ef70..b23fd952c84f 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -169,8 +169,11 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, rule4->dst = nla_get_be32(tb[FRA_DST]); #ifdef CONFIG_IP_ROUTE_CLASSID - if (tb[FRA_FLOW]) + if (tb[FRA_FLOW]) { rule4->tclassid = nla_get_u32(tb[FRA_FLOW]); + if (rule4->tclassid) + fib_num_tclassid_users++; + } #endif rule4->src_len = frh->src_len; @@ -184,6 +187,16 @@ errout: return err; } +static void fib4_rule_delete(struct fib_rule *rule) +{ +#ifdef CONFIG_IP_ROUTE_CLASSID + struct fib4_rule *rule4 = (struct fib4_rule *) rule; + + if (rule4->tclassid) + fib_num_tclassid_users--; +#endif +} + static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, struct nlattr **tb) { @@ -256,6 +269,7 @@ static const struct fib_rules_ops __net_initdata fib4_rules_ops_template = { .action = fib4_rule_action, .match = fib4_rule_match, .configure = fib4_rule_configure, + .delete = fib4_rule_delete, .compare = fib4_rule_compare, .fill = fib4_rule_fill, .default_pref = fib_default_rule_pref, diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 415f8230fc88..c46c20b6b0b6 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -163,6 +163,12 @@ void free_fib_info(struct fib_info *fi) return; } fib_info_cnt--; +#ifdef CONFIG_IP_ROUTE_CLASSID + change_nexthops(fi) { + if (nexthop_nh->nh_tclassid) + fib_num_tclassid_users--; + } endfor_nexthops(fi); +#endif call_rcu(&fi->rcu, free_fib_info_rcu); } @@ -421,6 +427,8 @@ static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh, #ifdef CONFIG_IP_ROUTE_CLASSID nla = nla_find(attrs, attrlen, RTA_FLOW); nexthop_nh->nh_tclassid = nla ? nla_get_u32(nla) : 0; + if (nexthop_nh->nh_tclassid) + fib_num_tclassid_users++; #endif } @@ -815,6 +823,8 @@ struct fib_info *fib_create_info(struct fib_config *cfg) nh->nh_flags = cfg->fc_flags; #ifdef CONFIG_IP_ROUTE_CLASSID nh->nh_tclassid = cfg->fc_flow; + if (nh->nh_tclassid) + fib_num_tclassid_users++; #endif #ifdef CONFIG_IP_ROUTE_MULTIPATH nh->nh_weight = 1; -- cgit v1.2.3 From f4489ebeffa436c8427a20e2f05004e783708cde Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:46:58 +0200 Subject: cfg80211: add channel tracking for AP and mesh We need to know which channel is used by a running AP and mesh for channel context accounting and finding matching/active interface combination. STA/IBSS have current_bss already which allows us to check which channel a vif is tuned to. Non-fixed channel IBSS can be handled with additional changes. Monitor mode is going to be handled differently. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 3 +++ net/wireless/ap.c | 4 +++- net/wireless/mesh.c | 18 ++++++++++++++---- net/wireless/mlme.c | 1 + net/wireless/nl80211.c | 1 + 5 files changed, 22 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1fc89c4f930c..c62bc7864adf 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2408,6 +2408,9 @@ struct wireless_dev { struct ieee80211_channel *preset_chan; enum nl80211_channel_type preset_chantype; + /* for AP and mesh channel tracking */ + struct ieee80211_channel *channel; + bool ps; int ps_timeout; diff --git a/net/wireless/ap.c b/net/wireless/ap.c index 45199cca63d5..fcc60d8dbefa 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -24,8 +24,10 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, return -ENOENT; err = rdev->ops->stop_ap(&rdev->wiphy, dev); - if (!err) + if (!err) { wdev->beacon_interval = 0; + wdev->channel = NULL; + } return err; } diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 3b73b07486cf..bab381344723 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -159,6 +159,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, if (!err) { memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); wdev->mesh_id_len = setup->mesh_id_len; + wdev->channel = setup->channel; } return err; @@ -184,6 +185,7 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, enum nl80211_channel_type channel_type) { struct ieee80211_channel *channel; + int err; channel = rdev_freq_to_chan(rdev, freq, channel_type); if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, @@ -205,9 +207,14 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, if (!netif_running(wdev->netdev)) return -ENETDOWN; - return rdev->ops->libertas_set_mesh_channel(&rdev->wiphy, - wdev->netdev, - channel); + + err = rdev->ops->libertas_set_mesh_channel(&rdev->wiphy, + wdev->netdev, + channel); + if (!err) + wdev->channel = channel; + + return err; } if (wdev->mesh_id_len) @@ -249,8 +256,11 @@ static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, return -ENOTCONN; err = rdev->ops->leave_mesh(&rdev->wiphy, dev); - if (!err) + if (!err) { wdev->mesh_id_len = 0; + wdev->channel = NULL; + } + return err; } diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index da4406f11929..a7882eb8c46e 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -947,6 +947,7 @@ void cfg80211_ch_switch_notify(struct net_device *dev, int freq, if (WARN_ON(!chan)) goto out; + wdev->channel = chan; nl80211_ch_switch_notify(rdev, dev, freq, type, GFP_KERNEL); out: wdev_unlock(wdev); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 20d0fd6d1286..12096b4ebf62 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2488,6 +2488,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) wdev->preset_chan = params.channel; wdev->preset_chantype = params.channel_type; wdev->beacon_interval = params.beacon_interval; + wdev->channel = params.channel; } return err; } -- cgit v1.2.3 From c30a3d38689bc601e03d5f2ad3c37d8ea13e46ca Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:46:59 +0200 Subject: cfg80211: track ibss fixed channel IBSS may hop between channels. It is necessary to account this special case when considering interface combinations. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ net/wireless/ibss.c | 1 + 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c62bc7864adf..e030c6af86dd 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2411,6 +2411,8 @@ struct wireless_dev { /* for AP and mesh channel tracking */ struct ieee80211_channel *channel; + bool ibss_fixed; + bool ps; int ps_timeout; diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 89baa3328411..b90fd86b2d18 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -113,6 +113,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, kfree(wdev->connect_keys); wdev->connect_keys = connkeys; + wdev->ibss_fixed = params->channel_fixed; #ifdef CONFIG_CFG80211_WEXT wdev->wext.ibss.channel = params->channel; #endif -- cgit v1.2.3 From dbbae26afa81320b3315fb4ad755b20f1ff256b4 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:47:01 +0200 Subject: cfg80211: track monitor interfaces count Implements .set_monitor_enabled(wiphy, enabled). Notifies driver upon change of interface layout. If only monitor interfaces become present it is called with 2nd argument being true. If non-monitor interface appears then 2nd argument is false. Driver is notified only upon change. This makes it more obvious about the fact that cfg80211 supports single monitor channel. Once we implement multi-channel we don't want to allow setting monitor channel while other interface types are running. Otherwise it would be ambiguous once we start considering num_different_channels. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++++ net/wireless/core.c | 24 ++++++++++++++++++++++++ net/wireless/core.h | 14 ++++++++++++++ net/wireless/util.c | 5 +++++ 4 files changed, 47 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e030c6af86dd..f0d213dd8fe7 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1499,6 +1499,8 @@ struct cfg80211_gtk_rekey_data { * interfaces are active this callback should reject the configuration. * If no interfaces are active or the device is down, the channel should * be stored for when a monitor interface becomes active. + * @set_monitor_enabled: Notify driver that there are only monitor + * interfaces running. * @get_channel: Get the current operating channel, should return %NULL if * there's no single defined operating channel if for example the * device implements channel hopping for multi-channel virtual interfaces. @@ -1817,6 +1819,8 @@ struct cfg80211_ops { struct ethtool_stats *stats, u64 *data); void (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev, u32 sset, u8 *data); + + void (*set_monitor_enabled)(struct wiphy *wiphy, bool enabled); }; /* diff --git a/net/wireless/core.c b/net/wireless/core.c index c65f59c952c9..8412da7d0f25 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -717,6 +717,24 @@ static struct device_type wiphy_type = { .name = "wlan", }; +void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, + enum nl80211_iftype iftype, int num) +{ + bool has_monitors_only_old = cfg80211_has_monitors_only(rdev); + bool has_monitors_only_new; + + ASSERT_RDEV_LOCK(rdev); + + rdev->num_running_ifaces += num; + if (iftype == NL80211_IFTYPE_MONITOR) + rdev->num_running_monitor_ifaces += num; + + has_monitors_only_new = cfg80211_has_monitors_only(rdev); + if (has_monitors_only_new != has_monitors_only_old) + rdev->ops->set_monitor_enabled(&rdev->wiphy, + has_monitors_only_new); +} + static int cfg80211_netdev_notifier_call(struct notifier_block *nb, unsigned long state, void *ndev) @@ -820,6 +838,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, break; case NETDEV_DOWN: dev_hold(dev); + cfg80211_lock_rdev(rdev); + cfg80211_update_iface_num(rdev, wdev->iftype, -1); + cfg80211_unlock_rdev(rdev); queue_work(cfg80211_wq, &wdev->cleanup_work); break; case NETDEV_UP: @@ -927,6 +948,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, ret = cfg80211_can_add_interface(rdev, wdev->iftype); if (ret) return notifier_from_errno(ret); + cfg80211_lock_rdev(rdev); + cfg80211_update_iface_num(rdev, wdev->iftype, 1); + cfg80211_unlock_rdev(rdev); break; } diff --git a/net/wireless/core.h b/net/wireless/core.h index 56f18c2eb919..99acd51343b1 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -56,6 +56,9 @@ struct cfg80211_registered_device { u32 ap_beacons_nlpid; + int num_running_ifaces; + int num_running_monitor_ifaces; + /* BSSes/scanning */ spinlock_t bss_lock; struct list_head bss_list; @@ -197,6 +200,14 @@ static inline void wdev_unlock(struct wireless_dev *wdev) #define ASSERT_RDEV_LOCK(rdev) lockdep_assert_held(&(rdev)->mtx) #define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx) +static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev) +{ + ASSERT_RDEV_LOCK(rdev); + + return rdev->num_running_ifaces == rdev->num_running_monitor_ifaces && + rdev->num_running_ifaces > 0; +} + enum cfg80211_event_type { EVENT_CONNECT_RESULT, EVENT_ROAMED, @@ -444,6 +455,9 @@ int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, u32 beacon_int); +void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, + enum nl80211_iftype iftype, int num); + #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) #else diff --git a/net/wireless/util.c b/net/wireless/util.c index fc948d0a53f3..9b92ec57d07b 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -871,6 +871,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, } } + if (!err && ntype != otype && netif_running(dev)) { + cfg80211_update_iface_num(rdev, ntype, 1); + cfg80211_update_iface_num(rdev, otype, -1); + } + return err; } -- cgit v1.2.3 From 2e165b818456ecc1024dd0387eeac64745526377 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 29 Jun 2012 12:47:06 +0200 Subject: cfg80211/mac80211: remove .get_channel We do not need it anymore since cfg80211 tracks monitor channel and monitor channel type. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 6 ------ net/mac80211/cfg.c | 11 ----------- net/wireless/nl80211.c | 15 +++++---------- net/wireless/wext-compat.c | 9 ++------- 4 files changed, 7 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f0d213dd8fe7..fa269347355b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1501,9 +1501,6 @@ struct cfg80211_gtk_rekey_data { * be stored for when a monitor interface becomes active. * @set_monitor_enabled: Notify driver that there are only monitor * interfaces running. - * @get_channel: Get the current operating channel, should return %NULL if - * there's no single defined operating channel if for example the - * device implements channel hopping for multi-channel virtual interfaces. * * @scan: Request to do a scan. If returning zero, the scan request is given * the driver, and will be valid until passed to cfg80211_scan_done(). @@ -1810,9 +1807,6 @@ struct cfg80211_ops { struct net_device *dev, u16 noack_map); - struct ieee80211_channel *(*get_channel)(struct wiphy *wiphy, - enum nl80211_channel_type *type); - int (*get_et_sset_count)(struct wiphy *wiphy, struct net_device *dev, int sset); void (*get_et_stats)(struct wiphy *wiphy, struct net_device *dev, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index ea4b1ea9105a..ccbe2413142a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2980,16 +2980,6 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, return 0; } -static struct ieee80211_channel * -ieee80211_wiphy_get_channel(struct wiphy *wiphy, - enum nl80211_channel_type *type) -{ - struct ieee80211_local *local = wiphy_priv(wiphy); - - *type = local->_oper_channel_type; - return local->oper_channel; -} - static void ieee80211_set_monitor_enabled(struct wiphy *wiphy, bool enabled) { struct ieee80211_local *local = wiphy_priv(wiphy); @@ -3073,7 +3063,6 @@ struct cfg80211_ops mac80211_config_ops = { .tdls_oper = ieee80211_tdls_oper, .tdls_mgmt = ieee80211_tdls_mgmt, .probe_client = ieee80211_probe_client, - .get_channel = ieee80211_wiphy_get_channel, .set_noack_map = ieee80211_set_noack_map, .set_monitor_enabled = ieee80211_set_monitor_enabled, #ifdef CONFIG_PM diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 12096b4ebf62..5d29ed1f7c62 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1689,16 +1689,11 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, (cfg80211_rdev_list_generation << 2))) goto nla_put_failure; - if (rdev->ops->get_channel) { - struct ieee80211_channel *chan; - enum nl80211_channel_type channel_type; - - chan = rdev->ops->get_channel(&rdev->wiphy, &channel_type); - if (chan && - (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, - chan->center_freq) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, - channel_type))) + if (rdev->monitor_channel) { + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, + rdev->monitor_channel->center_freq) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + rdev->monitor_channel_type)) goto nla_put_failure; } diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index bc879833b21f..7df42f541873 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -827,8 +827,6 @@ static int cfg80211_wext_giwfreq(struct net_device *dev, { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct ieee80211_channel *chan; - enum nl80211_channel_type channel_type; switch (wdev->iftype) { case NL80211_IFTYPE_STATION: @@ -836,13 +834,10 @@ static int cfg80211_wext_giwfreq(struct net_device *dev, case NL80211_IFTYPE_ADHOC: return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); case NL80211_IFTYPE_MONITOR: - if (!rdev->ops->get_channel) + if (!rdev->monitor_channel) return -EINVAL; - chan = rdev->ops->get_channel(wdev->wiphy, &channel_type); - if (!chan) - return -EINVAL; - freq->m = chan->center_freq; + freq->m = rdev->monitor_channel->center_freq; freq->e = 6; return 0; default: -- cgit v1.2.3 From de50ada55b6b83b54b817911ec42dc590e1c1738 Mon Sep 17 00:00:00 2001 From: Holger Macht Date: Mon, 25 Jun 2012 16:13:02 +0800 Subject: [SCSI] add wrapper to access and set scsi_bus_type in struct acpi_bus_type For being able to bind ata devices against acpi devices, scsi_bus_type needs to be set as bus in struct acpi_bus_type. So add wrapper to scsi_lib to accomplish that. Signed-off-by: Holger Macht Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/scsi/scsi_lib.c | 17 +++++++++++++++++ include/scsi/scsi.h | 10 ++++++++++ 2 files changed, 27 insertions(+) (limited to 'include') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 6dfb9785d345..08f1e297c735 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -68,6 +68,23 @@ static struct scsi_host_sg_pool scsi_sg_pools[] = { struct kmem_cache *scsi_sdb_cache; +#ifdef CONFIG_ACPI +#include + +int scsi_register_acpi_bus_type(struct acpi_bus_type *bus) +{ + bus->bus = &scsi_bus_type; + return register_acpi_bus_type(bus); +} +EXPORT_SYMBOL_GPL(scsi_register_acpi_bus_type); + +void scsi_unregister_acpi_bus_type(struct acpi_bus_type *bus) +{ + unregister_acpi_bus_type(bus); +} +EXPORT_SYMBOL_GPL(scsi_unregister_acpi_bus_type); +#endif + /* * When to reinvoke queueing after a resource shortage. It's 3 msecs to * not change behaviour from the previous unplug mechanism, experimentation diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index f34a5a87af38..4527b3a13321 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -214,6 +214,16 @@ scsi_command_size(const unsigned char *cmnd) scsi_varlen_cdb_length(cmnd) : COMMAND_SIZE(cmnd[0]); } +#ifdef CONFIG_ACPI +struct acpi_bus_type; + +extern int +scsi_register_acpi_bus_type(struct acpi_bus_type *bus); + +extern void +scsi_unregister_acpi_bus_type(struct acpi_bus_type *bus); +#endif + /* * SCSI Architecture Model (SAM) Status codes. Taken from SAM-3 draft * T10/1561-D Revision 4 Draft dated 7th November 2002. -- cgit v1.2.3 From 30dcf76acc695cbd2fa919e294670fe9552e16e7 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 25 Jun 2012 16:13:04 +0800 Subject: libata: migrate ACPI code over to new bindings Now that we have the ability to directly glue the ACPI namespace to the driver model in libata, we don't need the custom code to handle the same thing. Remove it and migrate the functions over to the new code. Signed-off-by: Matthew Garrett Signed-off-by: Holger Macht Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/ata/libata-acpi.c | 161 +++++++++++----------------------------------- drivers/ata/libata-core.c | 3 - drivers/ata/libata-pmp.c | 4 -- drivers/ata/libata.h | 5 -- drivers/ata/pata_acpi.c | 4 +- include/linux/libata.h | 7 +- 6 files changed, 40 insertions(+), 144 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index b3025a7d71e3..c6f0101fd253 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -47,7 +47,14 @@ static void ata_acpi_clear_gtf(struct ata_device *dev) dev->gtf_cache = NULL; } -static acpi_handle ap_acpi_handle(struct ata_port *ap) +/** + * ata_ap_acpi_handle - provide the acpi_handle for an ata_port + * @ap: the acpi_handle returned will correspond to this port + * + * Returns the acpi_handle for the ACPI namespace object corresponding to + * the ata_port passed into the function, or NULL if no such object exists + */ +acpi_handle ata_ap_acpi_handle(struct ata_port *ap) { if (ap->flags & ATA_FLAG_ACPI_SATA) return NULL; @@ -64,8 +71,16 @@ static acpi_handle ap_acpi_handle(struct ata_port *ap) return acpi_get_child(DEVICE_ACPI_HANDLE(ap->host->dev), ap->port_no); } +EXPORT_SYMBOL(ata_ap_acpi_handle); -static acpi_handle dev_acpi_handle(struct ata_device *dev) +/** + * ata_dev_acpi_handle - provide the acpi_handle for an ata_device + * @dev: the acpi_device returned will correspond to this port + * + * Returns the acpi_handle for the ACPI namespace object corresponding to + * the ata_device passed into the function, or NULL if no such object exists + */ +acpi_handle ata_dev_acpi_handle(struct ata_device *dev) { acpi_integer adr; struct ata_port *ap = dev->link->ap; @@ -77,66 +92,9 @@ static acpi_handle dev_acpi_handle(struct ata_device *dev) adr = SATA_ADR(ap->port_no, dev->link->pmp); return acpi_get_child(DEVICE_ACPI_HANDLE(ap->host->dev), adr); } else - return acpi_get_child(ap_acpi_handle(ap), dev->devno); -} - -/** - * ata_acpi_associate_sata_port - associate SATA port with ACPI objects - * @ap: target SATA port - * - * Look up ACPI objects associated with @ap and initialize acpi_handle - * fields of @ap, the port and devices accordingly. - * - * LOCKING: - * EH context. - * - * RETURNS: - * 0 on success, -errno on failure. - */ -void ata_acpi_associate_sata_port(struct ata_port *ap) -{ - WARN_ON(!(ap->flags & ATA_FLAG_ACPI_SATA)); - - if (!sata_pmp_attached(ap)) { - u64 adr = SATA_ADR(ap->port_no, NO_PORT_MULT); - - ap->link.device->acpi_handle = - acpi_get_child(ap->host->acpi_handle, adr); - } else { - struct ata_link *link; - - ap->link.device->acpi_handle = NULL; - - ata_for_each_link(link, ap, EDGE) { - u64 adr = SATA_ADR(ap->port_no, link->pmp); - - link->device->acpi_handle = - acpi_get_child(ap->host->acpi_handle, adr); - } - } -} - -static void ata_acpi_associate_ide_port(struct ata_port *ap) -{ - int max_devices, i; - - ap->acpi_handle = acpi_get_child(ap->host->acpi_handle, ap->port_no); - if (!ap->acpi_handle) - return; - - max_devices = 1; - if (ap->flags & ATA_FLAG_SLAVE_POSS) - max_devices++; - - for (i = 0; i < max_devices; i++) { - struct ata_device *dev = &ap->link.device[i]; - - dev->acpi_handle = acpi_get_child(ap->acpi_handle, i); - } - - if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0) - ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; + return acpi_get_child(ata_ap_acpi_handle(ap), dev->devno); } +EXPORT_SYMBOL(ata_dev_acpi_handle); /* @ap and @dev are the same as ata_acpi_handle_hotplug() */ static void ata_acpi_detach_device(struct ata_port *ap, struct ata_device *dev) @@ -261,56 +219,6 @@ static const struct acpi_dock_ops ata_acpi_ap_dock_ops = { .uevent = ata_acpi_ap_uevent, }; -/** - * ata_acpi_associate - associate ATA host with ACPI objects - * @host: target ATA host - * - * Look up ACPI objects associated with @host and initialize - * acpi_handle fields of @host, its ports and devices accordingly. - * - * LOCKING: - * EH context. - * - * RETURNS: - * 0 on success, -errno on failure. - */ -void ata_acpi_associate(struct ata_host *host) -{ - int i, j; - - if (!is_pci_dev(host->dev) || libata_noacpi) - return; - - host->acpi_handle = DEVICE_ACPI_HANDLE(host->dev); - if (!host->acpi_handle) - return; - - for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap = host->ports[i]; - - if (host->ports[0]->flags & ATA_FLAG_ACPI_SATA) - ata_acpi_associate_sata_port(ap); - else - ata_acpi_associate_ide_port(ap); - - if (ap->acpi_handle) { - /* we might be on a docking station */ - register_hotplug_dock_device(ap->acpi_handle, - &ata_acpi_ap_dock_ops, ap); - } - - for (j = 0; j < ata_link_max_devices(&ap->link); j++) { - struct ata_device *dev = &ap->link.device[j]; - - if (dev->acpi_handle) { - /* we might be on a docking station */ - register_hotplug_dock_device(dev->acpi_handle, - &ata_acpi_dev_dock_ops, dev); - } - } - } -} - /** * ata_acpi_dissociate - dissociate ATA host from ACPI objects * @host: target ATA host @@ -332,7 +240,7 @@ void ata_acpi_dissociate(struct ata_host *host) struct ata_port *ap = host->ports[i]; const struct ata_acpi_gtm *gtm = ata_acpi_init_gtm(ap); - if (ap->acpi_handle && gtm) + if (ata_ap_acpi_handle(ap) && gtm) ata_acpi_stm(ap, gtm); } } @@ -357,7 +265,8 @@ int ata_acpi_gtm(struct ata_port *ap, struct ata_acpi_gtm *gtm) acpi_status status; int rc = 0; - status = acpi_evaluate_object(ap->acpi_handle, "_GTM", NULL, &output); + status = acpi_evaluate_object(ata_ap_acpi_handle(ap), "_GTM", NULL, + &output); rc = -ENOENT; if (status == AE_NOT_FOUND) @@ -427,7 +336,8 @@ int ata_acpi_stm(struct ata_port *ap, const struct ata_acpi_gtm *stm) input.count = 3; input.pointer = in_params; - status = acpi_evaluate_object(ap->acpi_handle, "_STM", &input, NULL); + status = acpi_evaluate_object(ata_ap_acpi_handle(ap), "_STM", &input, + NULL); if (status == AE_NOT_FOUND) return -ENOENT; @@ -484,7 +394,8 @@ static int ata_dev_get_GTF(struct ata_device *dev, struct ata_acpi_gtf **gtf) __func__, ap->port_no); /* _GTF has no input parameters */ - status = acpi_evaluate_object(dev->acpi_handle, "_GTF", NULL, &output); + status = acpi_evaluate_object(ata_dev_acpi_handle(dev), "_GTF", NULL, + &output); out_obj = dev->gtf_cache = output.pointer; if (ACPI_FAILURE(status)) { @@ -850,7 +761,8 @@ static int ata_acpi_push_id(struct ata_device *dev) /* It's OK for _SDD to be missing too. */ swap_buf_le16(dev->id, ATA_ID_WORDS); - status = acpi_evaluate_object(dev->acpi_handle, "_SDD", &input, NULL); + status = acpi_evaluate_object(ata_dev_acpi_handle(dev), "_SDD", &input, + NULL); swap_buf_le16(dev->id, ATA_ID_WORDS); if (status == AE_NOT_FOUND) @@ -900,7 +812,7 @@ void ata_acpi_on_resume(struct ata_port *ap) const struct ata_acpi_gtm *gtm = ata_acpi_init_gtm(ap); struct ata_device *dev; - if (ap->acpi_handle && gtm) { + if (ata_ap_acpi_handle(ap) && gtm) { /* _GTM valid */ /* restore timing parameters */ @@ -941,22 +853,22 @@ void ata_acpi_set_state(struct ata_port *ap, pm_message_t state) { struct ata_device *dev; - if (!ap->acpi_handle || (ap->flags & ATA_FLAG_ACPI_SATA)) + if (!ata_ap_acpi_handle(ap) || (ap->flags & ATA_FLAG_ACPI_SATA)) return; /* channel first and then drives for power on and vica versa for power off */ if (state.event == PM_EVENT_ON) - acpi_bus_set_power(ap->acpi_handle, ACPI_STATE_D0); + acpi_bus_set_power(ata_ap_acpi_handle(ap), ACPI_STATE_D0); ata_for_each_dev(dev, &ap->link, ENABLED) { - if (dev->acpi_handle) - acpi_bus_set_power(dev->acpi_handle, + if (ata_dev_acpi_handle(dev)) + acpi_bus_set_power(ata_dev_acpi_handle(dev), state.event == PM_EVENT_ON ? ACPI_STATE_D0 : ACPI_STATE_D3); } if (state.event != PM_EVENT_ON) - acpi_bus_set_power(ap->acpi_handle, ACPI_STATE_D3); + acpi_bus_set_power(ata_ap_acpi_handle(ap), ACPI_STATE_D3); } /** @@ -981,7 +893,7 @@ int ata_acpi_on_devcfg(struct ata_device *dev) int nr_executed = 0; int rc; - if (!dev->acpi_handle) + if (!ata_dev_acpi_handle(dev)) return 0; /* do we need to do _GTF? */ @@ -1027,7 +939,6 @@ int ata_acpi_on_devcfg(struct ata_device *dev) } ata_dev_warn(dev, "ACPI: failed the second time, disabled\n"); - dev->acpi_handle = NULL; /* We can safely continue if no _GTF command has been executed * and port is not frozen. @@ -1093,7 +1004,7 @@ static int ata_acpi_bind_device(struct ata_port *ap, struct scsi_device *sdev, else ata_dev = &ap->link.device[sdev->id]; - *handle = dev_acpi_handle(ata_dev); + *handle = ata_dev_acpi_handle(ata_dev); if (!*handle) return -ENODEV; diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index bd33b4a7ac13..7705191b5a81 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6049,9 +6049,6 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) if (rc) goto err_tadd; - /* associate with ACPI nodes */ - ata_acpi_associate(host); - /* set cable, sata_spd_limit and report */ for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 21b80c555c60..61c59ee45ce9 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -529,8 +529,6 @@ int sata_pmp_attach(struct ata_device *dev) ata_for_each_link(tlink, ap, EDGE) sata_link_init_spd(tlink); - ata_acpi_associate_sata_port(ap); - return 0; fail: @@ -570,8 +568,6 @@ static void sata_pmp_detach(struct ata_device *dev) ap->nr_pmp_links = 0; link->pmp = 0; spin_unlock_irqrestore(ap->lock, flags); - - ata_acpi_associate_sata_port(ap); } /** diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 7f48b602b01b..b0d5294982eb 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -112,9 +112,6 @@ extern void __ata_port_probe(struct ata_port *ap); /* libata-acpi.c */ #ifdef CONFIG_ATA_ACPI extern unsigned int ata_acpi_gtf_filter; - -extern void ata_acpi_associate_sata_port(struct ata_port *ap); -extern void ata_acpi_associate(struct ata_host *host); extern void ata_acpi_dissociate(struct ata_host *host); extern int ata_acpi_on_suspend(struct ata_port *ap); extern void ata_acpi_on_resume(struct ata_port *ap); @@ -124,8 +121,6 @@ extern void ata_acpi_set_state(struct ata_port *ap, pm_message_t state); extern int ata_acpi_register(void); extern void ata_acpi_unregister(void); #else -static inline void ata_acpi_associate_sata_port(struct ata_port *ap) { } -static inline void ata_acpi_associate(struct ata_host *host) { } static inline void ata_acpi_dissociate(struct ata_host *host) { } static inline int ata_acpi_on_suspend(struct ata_port *ap) { return 0; } static inline void ata_acpi_on_resume(struct ata_port *ap) { } diff --git a/drivers/ata/pata_acpi.c b/drivers/ata/pata_acpi.c index 54145edf50e8..b63ca3b54fb9 100644 --- a/drivers/ata/pata_acpi.c +++ b/drivers/ata/pata_acpi.c @@ -39,7 +39,7 @@ static int pacpi_pre_reset(struct ata_link *link, unsigned long deadline) { struct ata_port *ap = link->ap; struct pata_acpi *acpi = ap->private_data; - if (ap->acpi_handle == NULL || ata_acpi_gtm(ap, &acpi->gtm) < 0) + if (ata_ap_acpi_handle(ap) == NULL || ata_acpi_gtm(ap, &acpi->gtm) < 0) return -ENODEV; return ata_sff_prereset(link, deadline); @@ -195,7 +195,7 @@ static int pacpi_port_start(struct ata_port *ap) struct pci_dev *pdev = to_pci_dev(ap->host->dev); struct pata_acpi *acpi; - if (ap->acpi_handle == NULL) + if (ata_ap_acpi_handle(ap) == NULL) return -ENODEV; acpi = ap->private_data = devm_kzalloc(&pdev->dev, sizeof(struct pata_acpi), GFP_KERNEL); diff --git a/include/linux/libata.h b/include/linux/libata.h index 6e887c742a27..888feef3cda4 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -545,9 +545,6 @@ struct ata_host { struct mutex eh_mutex; struct task_struct *eh_owner; -#ifdef CONFIG_ATA_ACPI - acpi_handle acpi_handle; -#endif struct ata_port *simplex_claimed; /* channel owning the DMA */ struct ata_port *ports[0]; }; @@ -615,7 +612,6 @@ struct ata_device { struct scsi_device *sdev; /* attached SCSI device */ void *private_data; #ifdef CONFIG_ATA_ACPI - acpi_handle acpi_handle; union acpi_object *gtf_cache; unsigned int gtf_filter; #endif @@ -797,7 +793,6 @@ struct ata_port { void *private_data; #ifdef CONFIG_ATA_ACPI - acpi_handle acpi_handle; struct ata_acpi_gtm __acpi_init_gtm; /* use ata_acpi_init_gtm() */ #endif /* owned by EH */ @@ -1114,6 +1109,8 @@ int ata_acpi_stm(struct ata_port *ap, const struct ata_acpi_gtm *stm); int ata_acpi_gtm(struct ata_port *ap, struct ata_acpi_gtm *stm); unsigned long ata_acpi_gtm_xfermask(struct ata_device *dev, const struct ata_acpi_gtm *gtm); +acpi_handle ata_ap_acpi_handle(struct ata_port *ap); +acpi_handle ata_dev_acpi_handle(struct ata_device *dev); int ata_acpi_cbl_80wire(struct ata_port *ap, const struct ata_acpi_gtm *gtm); #else static inline const struct ata_acpi_gtm *ata_acpi_init_gtm(struct ata_port *ap) -- cgit v1.2.3 From b1354cbb5bfce28f2e1ed28d77b362dfdfca638d Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Mon, 25 Jun 2012 16:13:08 +0800 Subject: libata: detect Device Attention support Add a new flag ATA_DFLAG_DA to indicate that device supports "Device Attention". Acked-by: Aaron Lu Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 3 +++ include/linux/ata.h | 1 + include/linux/libata.h | 2 ++ 3 files changed, 6 insertions(+) (limited to 'include') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 7705191b5a81..c14f88c1f1da 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2374,6 +2374,9 @@ int ata_dev_configure(struct ata_device *dev) dma_dir_string = ", DMADIR"; } + if (ata_id_has_da(dev->id)) + dev->flags |= ATA_DFLAG_DA; + /* print device info to dmesg */ if (ata_msg_drv(ap) && print_info) ata_dev_info(dev, diff --git a/include/linux/ata.h b/include/linux/ata.h index 32df2b6ef0e0..5713d3ac381a 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -578,6 +578,7 @@ static inline int ata_is_data(u8 prot) ((u64) (id)[(n) + 0]) ) #define ata_id_cdb_intr(id) (((id)[ATA_ID_CONFIG] & 0x60) == 0x20) +#define ata_id_has_da(id) ((id)[77] & (1 << 4)) static inline bool ata_id_has_hipm(const u16 *id) { diff --git a/include/linux/libata.h b/include/linux/libata.h index 888feef3cda4..cc22b943db83 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -161,6 +161,8 @@ enum { ATA_DFLAG_DETACH = (1 << 24), ATA_DFLAG_DETACHED = (1 << 25), + ATA_DFLAG_DA = (1 << 26), /* device supports Device Attention */ + ATA_DEV_UNKNOWN = 0, /* unknown device */ ATA_DEV_ATA = 1, /* ATA device */ ATA_DEV_ATA_UNSUP = 2, /* ATA device (unsupported) */ -- cgit v1.2.3 From 166a2967b45ede2e2e56f3ede3cd32053dc17812 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Mon, 25 Jun 2012 16:13:09 +0800 Subject: libata: tell scsi layer device supports runtime power off If ATA device supports "Device Attention", then tell scsi layer that the device supports runtime power off. Signed-off-by: Aaron Lu Signed-off-by: Lin Ming Signed-off-by: Jeff Garzik --- drivers/ata/libata-acpi.c | 28 ++++++++++++++++++++++++++-- include/scsi/scsi_device.h | 1 + 2 files changed, 27 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index f36284e3290d..f1d6901de37d 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -1000,7 +1000,10 @@ static void ata_acpi_add_pm_notifier(struct ata_device *dev) return; status = acpi_bus_get_device(handle, &acpi_dev); - if (ACPI_SUCCESS(status)) { + if (ACPI_FAILURE(status)) + return; + + if (dev->sdev->can_power_off) { acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, ata_acpi_wake_dev, dev); device_set_run_wake(&dev->sdev->sdev_gendev, true); @@ -1018,7 +1021,10 @@ static void ata_acpi_remove_pm_notifier(struct ata_device *dev) return; status = acpi_bus_get_device(handle, &acpi_dev); - if (ACPI_SUCCESS(status)) { + if (ACPI_FAILURE(status)) + return; + + if (dev->sdev->can_power_off) { device_set_run_wake(&dev->sdev->sdev_gendev, false); acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, ata_acpi_wake_dev); @@ -1102,6 +1108,9 @@ static int ata_acpi_bind_device(struct ata_port *ap, struct scsi_device *sdev, acpi_handle *handle) { struct ata_device *ata_dev; + acpi_status status; + struct acpi_device *acpi_dev; + struct acpi_device_power_state *states; if (ap->flags & ATA_FLAG_ACPI_SATA) ata_dev = &ap->link.device[sdev->channel]; @@ -1113,6 +1122,21 @@ static int ata_acpi_bind_device(struct ata_port *ap, struct scsi_device *sdev, if (!*handle) return -ENODEV; + status = acpi_bus_get_device(*handle, &acpi_dev); + if (ACPI_FAILURE(status)) + return 0; + + /* + * If firmware has _PS3 or _PR3 for this device, + * and this ata ODD device support device attention, + * it means this device can be powered off + */ + states = acpi_dev->power.states; + if ((states[ACPI_STATE_D3_HOT].flags.valid || + states[ACPI_STATE_D3_COLD].flags.explicit_set) && + ata_dev->flags & ATA_DFLAG_DA) + sdev->can_power_off = 1; + return 0; } diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index ba9698852321..aff7525de194 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -153,6 +153,7 @@ struct scsi_device { unsigned no_read_capacity_16:1; /* Avoid READ_CAPACITY_16 cmds */ unsigned try_rc_10_first:1; /* Try READ_CAPACACITY_10 first */ unsigned is_visible:1; /* is the device visible in sysfs */ + unsigned can_power_off:1; /* Device supports runtime power off */ DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */ struct list_head event_list; /* asserted events */ -- cgit v1.2.3 From 011e2a7fd5e9e0c2fdba6b9466d53fc437f8bfaf Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 20 Jun 2012 15:53:43 -0400 Subject: NFS: Create a have_delegation rpc_op Delegations are a v4 feature, so push them out of the generic code. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/delegation.c | 2 +- fs/nfs/delegation.h | 9 ++------- fs/nfs/dir.c | 2 +- fs/nfs/file.c | 6 +++--- fs/nfs/inode.c | 2 +- fs/nfs/nfs3proc.c | 6 ++++++ fs/nfs/nfs4proc.c | 5 +++-- fs/nfs/proc.c | 6 ++++++ fs/nfs/write.c | 2 +- include/linux/nfs_xdr.h | 1 + 10 files changed, 25 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 9a7a1b488af9..36c7c647a1d0 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -47,7 +47,7 @@ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) * * Returns one if inode has the indicated delegation, otherwise zero. */ -int nfs_have_delegation(struct inode *inode, fmode_t flags) +int nfs4_have_delegation(struct inode *inode, fmode_t flags) { struct nfs_delegation *delegation; int ret = 0; diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index 206db5679996..d134fc5fda70 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -56,14 +56,9 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl); bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode, fmode_t flags); void nfs_mark_delegation_referenced(struct nfs_delegation *delegation); -int nfs_have_delegation(struct inode *inode, fmode_t flags); +int nfs4_have_delegation(struct inode *inode, fmode_t flags); #else -static inline int nfs_have_delegation(struct inode *inode, fmode_t flags) -{ - return 0; -} - static inline int nfs_inode_return_delegation(struct inode *inode) { nfs_wb_all(inode); @@ -73,7 +68,7 @@ static inline int nfs_inode_return_delegation(struct inode *inode) static inline int nfs_have_delegated_attributes(struct inode *inode) { - return nfs_have_delegation(inode, FMODE_READ) && + return NFS_PROTO(inode)->have_delegation(inode, FMODE_READ) && !(NFS_I(inode)->cache_validity & NFS_INO_REVAL_FORCED); } diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index f430057ff3b3..4a3e23aea143 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1144,7 +1144,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd) goto out_bad; } - if (nfs_have_delegation(inode, FMODE_READ)) + if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ)) goto out_set_verifier; /* Force a full look up iff the parent directory has changed */ diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 8941ac41c59b..57a22a1533e2 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -178,7 +178,7 @@ nfs_file_flush(struct file *file, fl_owner_t id) * If we're holding a write delegation, then just start the i/o * but don't wait for completion (or send a commit). */ - if (nfs_have_delegation(inode, FMODE_WRITE)) + if (NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE)) return filemap_fdatawrite(file->f_mapping); /* Flush writes to the server and return any errors */ @@ -677,7 +677,7 @@ do_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) } fl->fl_type = saved_type; - if (nfs_have_delegation(inode, FMODE_READ)) + if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ)) goto out_noconflict; if (is_local) @@ -772,7 +772,7 @@ do_setlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) * This makes locking act as a cache coherency point. */ nfs_sync_mapping(filp->f_mapping); - if (!nfs_have_delegation(inode, FMODE_READ)) { + if (!NFS_PROTO(inode)->have_delegation(inode, FMODE_READ)) { if (is_time_granular(&NFS_SERVER(inode)->time_delta)) __nfs_revalidate_inode(NFS_SERVER(inode), inode); else diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index f7296983eba6..0f0b928ef252 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1457,7 +1457,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) invalid &= ~NFS_INO_INVALID_DATA; - if (!nfs_have_delegation(inode, FMODE_READ) || + if (!NFS_PROTO(inode)->have_delegation(inode, FMODE_READ) || (save_cache_validity & NFS_INO_REVAL_FORCED)) nfsi->cache_validity |= invalid; diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 2292a0fd2bff..08f832634ef9 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -877,6 +877,11 @@ nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl) return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl); } +static int nfs3_have_delegation(struct inode *inode, fmode_t flags) +{ + return 0; +} + const struct nfs_rpc_ops nfs_v3_clientops = { .version = 3, /* protocol version */ .dentry_ops = &nfs_dentry_operations, @@ -921,5 +926,6 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .lock = nfs3_proc_lock, .clear_acl_cache = nfs3_forget_cached_acls, .close_context = nfs_close_context, + .have_delegation = nfs3_have_delegation, .init_client = nfs_init_client, }; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e9a8ad2df7af..86f428bb5e07 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -294,7 +294,7 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc case 0: return 0; case -NFS4ERR_OPENMODE: - if (inode && nfs_have_delegation(inode, FMODE_READ)) { + if (inode && nfs4_have_delegation(inode, FMODE_READ)) { nfs_inode_return_delegation(inode); exception->retry = 1; return 0; @@ -3466,7 +3466,7 @@ bool nfs4_write_need_cache_consistency_data(const struct nfs_write_data *data) /* Otherwise, request attributes if and only if we don't hold * a delegation */ - return nfs_have_delegation(hdr->inode, FMODE_READ) == 0; + return nfs4_have_delegation(hdr->inode, FMODE_READ) == 0; } static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_message *msg) @@ -6804,6 +6804,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .clear_acl_cache = nfs4_zap_acl_attr, .close_context = nfs4_close_context, .open_context = nfs4_atomic_open, + .have_delegation = nfs4_have_delegation, .init_client = nfs4_init_client, }; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 617c7419a08e..4aed3ddf9bba 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -734,6 +734,11 @@ out_einval: return -EINVAL; } +static int nfs_have_delegation(struct inode *inode, fmode_t flags) +{ + return 0; +} + const struct nfs_rpc_ops nfs_v2_clientops = { .version = 2, /* protocol version */ .dentry_ops = &nfs_dentry_operations, @@ -777,5 +782,6 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .lock = nfs_proc_lock, .lock_check_bounds = nfs_lock_check_bounds, .close_context = nfs_close_context, + .have_delegation = nfs_have_delegation, .init_client = nfs_init_client, }; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index ee929e5e1f7b..f163355b9618 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -410,7 +410,7 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req) nfs_lock_request(req); spin_lock(&inode->i_lock); - if (!nfsi->npages && nfs_have_delegation(inode, FMODE_WRITE)) + if (!nfsi->npages && NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE)) inode->i_version++; set_bit(PG_MAPPED, &req->wb_flags); SetPagePrivate(req->wb_page); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 5c0014d1c969..8787f77c64b3 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1422,6 +1422,7 @@ struct nfs_rpc_ops { struct nfs_open_context *ctx, int open_flags, struct iattr *iattr); + int (*have_delegation)(struct inode *, fmode_t); struct nfs_client * (*init_client) (struct nfs_client *, const struct rpc_timeout *, const char *, rpc_authflavor_t); -- cgit v1.2.3 From 57ec14c55dee2733330327499d16e40f8c23219e Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 20 Jun 2012 15:53:44 -0400 Subject: NFS: Create a return_delegation rpc op Delegations are a v4 feature, so push return_delegation out of the generic client by creating a new rpc_op and renaming the old function to be in the nfs v4 "namespace" Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/delegation.c | 2 +- fs/nfs/delegation.h | 8 +------- fs/nfs/dir.c | 8 ++++---- fs/nfs/inode.c | 2 +- fs/nfs/nfs3proc.c | 7 +++++++ fs/nfs/nfs4proc.c | 7 ++++--- fs/nfs/proc.c | 7 +++++++ fs/nfs/unlink.c | 2 +- include/linux/nfs_xdr.h | 1 + 9 files changed, 27 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 36c7c647a1d0..81c5eec3cf38 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -388,7 +388,7 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode) * * Returns zero on success, or a negative errno value. */ -int nfs_inode_return_delegation(struct inode *inode) +int nfs4_inode_return_delegation(struct inode *inode) { struct nfs_server *server = NFS_SERVER(inode); struct nfs_inode *nfsi = NFS_I(inode); diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index d134fc5fda70..1f3ccd934635 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -33,7 +33,7 @@ enum { int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); -int nfs_inode_return_delegation(struct inode *inode); +int nfs4_inode_return_delegation(struct inode *inode); int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid); void nfs_inode_return_delegation_noreclaim(struct inode *inode); @@ -58,12 +58,6 @@ bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode, fmode_ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation); int nfs4_have_delegation(struct inode *inode, fmode_t flags); -#else -static inline int nfs_inode_return_delegation(struct inode *inode) -{ - nfs_wb_all(inode); - return 0; -} #endif static inline int nfs_have_delegated_attributes(struct inode *inode) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 4a3e23aea143..68e451f59305 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1778,7 +1778,7 @@ static int nfs_safe_remove(struct dentry *dentry) } if (inode != NULL) { - nfs_inode_return_delegation(inode); + NFS_PROTO(inode)->return_delegation(inode); error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); /* The VFS may want to delete this inode */ if (error == 0) @@ -1906,7 +1906,7 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) old_dentry->d_parent->d_name.name, old_dentry->d_name.name, dentry->d_parent->d_name.name, dentry->d_name.name); - nfs_inode_return_delegation(inode); + NFS_PROTO(inode)->return_delegation(inode); d_drop(dentry); error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name); @@ -1990,9 +1990,9 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, } } - nfs_inode_return_delegation(old_inode); + NFS_PROTO(old_inode)->return_delegation(old_inode); if (new_inode != NULL) - nfs_inode_return_delegation(new_inode); + NFS_PROTO(new_inode)->return_delegation(new_inode); error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 0f0b928ef252..28c9ebbe78a6 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -430,7 +430,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr) * Return any delegations if we're going to change ACLs */ if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) - nfs_inode_return_delegation(inode); + NFS_PROTO(inode)->return_delegation(inode); error = NFS_PROTO(inode)->setattr(dentry, fattr, attr); if (error == 0) nfs_refresh_inode(inode, fattr); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 08f832634ef9..4749a32e54be 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -882,6 +882,12 @@ static int nfs3_have_delegation(struct inode *inode, fmode_t flags) return 0; } +static int nfs3_return_delegation(struct inode *inode) +{ + nfs_wb_all(inode); + return 0; +} + const struct nfs_rpc_ops nfs_v3_clientops = { .version = 3, /* protocol version */ .dentry_ops = &nfs_dentry_operations, @@ -927,5 +933,6 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .clear_acl_cache = nfs3_forget_cached_acls, .close_context = nfs_close_context, .have_delegation = nfs3_have_delegation, + .return_delegation = nfs3_return_delegation, .init_client = nfs_init_client, }; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 86f428bb5e07..035f7a0829ec 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -295,7 +295,7 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc return 0; case -NFS4ERR_OPENMODE: if (inode && nfs4_have_delegation(inode, FMODE_READ)) { - nfs_inode_return_delegation(inode); + nfs4_inode_return_delegation(inode); exception->retry = 1; return 0; } @@ -1065,7 +1065,7 @@ static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmo return; } rcu_read_unlock(); - nfs_inode_return_delegation(inode); + nfs4_inode_return_delegation(inode); } static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata) @@ -3870,7 +3870,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase); if (i < 0) return i; - nfs_inode_return_delegation(inode); + nfs4_inode_return_delegation(inode); ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); /* @@ -6805,6 +6805,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .close_context = nfs4_close_context, .open_context = nfs4_atomic_open, .have_delegation = nfs4_have_delegation, + .return_delegation = nfs4_inode_return_delegation, .init_client = nfs4_init_client, }; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 4aed3ddf9bba..16632930abd2 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -739,6 +739,12 @@ static int nfs_have_delegation(struct inode *inode, fmode_t flags) return 0; } +static int nfs_return_delegation(struct inode *inode) +{ + nfs_wb_all(inode); + return 0; +} + const struct nfs_rpc_ops nfs_v2_clientops = { .version = 2, /* protocol version */ .dentry_ops = &nfs_dentry_operations, @@ -783,5 +789,6 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .lock_check_bounds = nfs_lock_check_bounds, .close_context = nfs_close_context, .have_delegation = nfs_have_delegation, + .return_delegation = nfs_return_delegation, .init_client = nfs_init_client, }; diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 3210a03342f9..13cea637eff8 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -501,7 +501,7 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry) (unsigned long long)NFS_FILEID(dentry->d_inode)); /* Return delegation in anticipation of the rename */ - nfs_inode_return_delegation(dentry->d_inode); + NFS_PROTO(dentry->d_inode)->return_delegation(dentry->d_inode); sdentry = NULL; do { diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 8787f77c64b3..62235be07fb8 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1423,6 +1423,7 @@ struct nfs_rpc_ops { int open_flags, struct iattr *iattr); int (*have_delegation)(struct inode *, fmode_t); + int (*return_delegation)(struct inode *); struct nfs_client * (*init_client) (struct nfs_client *, const struct rpc_timeout *, const char *, rpc_authflavor_t); -- cgit v1.2.3 From cdb7ecedec766861e7c4cc35a203518f92023bff Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 20 Jun 2012 15:53:45 -0400 Subject: NFS: Create a free_client rpc_op NFS v4 needs a way to shut down callbacks and sessions, but v2 and v3 don't. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 18 +++++++++--------- fs/nfs/internal.h | 1 + fs/nfs/nfs3proc.c | 1 + fs/nfs/nfs4_fs.h | 2 ++ fs/nfs/nfs4proc.c | 1 + fs/nfs/proc.c | 1 + include/linux/nfs_xdr.h | 1 + 7 files changed, 16 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index bf0f896284a8..82cb8a386a8f 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -242,6 +242,12 @@ static void nfs4_shutdown_client(struct nfs_client *clp) kfree(clp->cl_implid); } +void nfs4_free_client(struct nfs_client *clp) +{ + nfs4_shutdown_client(clp); + nfs_free_client(clp); +} + /* idr_remove_all is not needed as all id's are removed by nfs_put_client */ void nfs_cleanup_cb_ident_idr(struct net *net) { @@ -272,10 +278,6 @@ static void nfs4_destroy_server(struct nfs_server *server) } #else -static void nfs4_shutdown_client(struct nfs_client *clp) -{ -} - void nfs_cleanup_cb_ident_idr(struct net *net) { } @@ -293,12 +295,10 @@ static void pnfs_init_server(struct nfs_server *server) /* * Destroy a shared client record */ -static void nfs_free_client(struct nfs_client *clp) +void nfs_free_client(struct nfs_client *clp) { dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); - nfs4_shutdown_client(clp); - nfs_fscache_release_client_cookie(clp); /* -EIO all pending I/O */ @@ -335,7 +335,7 @@ void nfs_put_client(struct nfs_client *clp) BUG_ON(!list_empty(&clp->cl_superblocks)); - nfs_free_client(clp); + clp->rpc_ops->free_client(clp); } } EXPORT_SYMBOL_GPL(nfs_put_client); @@ -574,7 +574,7 @@ nfs_get_client(const struct nfs_client_initdata *cl_init, if (clp) { spin_unlock(&nn->nfs_client_lock); if (new) - nfs_free_client(new); + new->rpc_ops->free_client(new); return nfs_found_client(cl_init, clp); } if (new) { diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 18f99ef71343..93b732523342 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -151,6 +151,7 @@ extern void nfs_clients_init(struct net *net); extern void nfs_cleanup_cb_ident_idr(struct net *); extern void nfs_put_client(struct nfs_client *); +extern void nfs_free_client(struct nfs_client *); extern struct nfs_client *nfs4_find_client_ident(struct net *, int); extern struct nfs_client * nfs4_find_client_sessionid(struct net *, const struct sockaddr *, diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 4749a32e54be..4ccb34bf1732 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -935,4 +935,5 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .have_delegation = nfs3_have_delegation, .return_delegation = nfs3_return_delegation, .init_client = nfs_init_client, + .free_client = nfs_free_client, }; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index cc5900ac61b5..9889ee476e37 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -301,6 +301,8 @@ extern const u32 nfs4_pathconf_bitmap[2]; extern const u32 nfs4_fsinfo_bitmap[3]; extern const u32 nfs4_fs_locations_bitmap[2]; +void nfs4_free_client(struct nfs_client *); + /* nfs4renewd.c */ extern void nfs4_schedule_state_renewal(struct nfs_client *); extern void nfs4_renewd_prepare_shutdown(struct nfs_server *); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 035f7a0829ec..f301c53926b2 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6807,6 +6807,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .have_delegation = nfs4_have_delegation, .return_delegation = nfs4_inode_return_delegation, .init_client = nfs4_init_client, + .free_client = nfs4_free_client, }; static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = { diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 16632930abd2..53620bf10969 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -791,4 +791,5 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .have_delegation = nfs_have_delegation, .return_delegation = nfs_return_delegation, .init_client = nfs_init_client, + .free_client = nfs_free_client, }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 62235be07fb8..e61dc7235d5d 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1427,6 +1427,7 @@ struct nfs_rpc_ops { struct nfs_client * (*init_client) (struct nfs_client *, const struct rpc_timeout *, const char *, rpc_authflavor_t); + void (*free_client) (struct nfs_client *); }; /* -- cgit v1.2.3 From 6663ee7f8187708143255c057bc132bbc84c1894 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 20 Jun 2012 15:53:46 -0400 Subject: NFS: Create an alloc_client rpc_op This gives NFS v4 a way to set up callbacks and sessions without v2 or v3 having to do them as well. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 40 ++++++++++++++++++++++++++-------------- fs/nfs/internal.h | 1 + fs/nfs/nfs3proc.c | 1 + fs/nfs/nfs4_fs.h | 2 ++ fs/nfs/nfs4proc.c | 1 + fs/nfs/proc.c | 1 + include/linux/nfs_xdr.h | 2 ++ 7 files changed, 34 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 82cb8a386a8f..254719c4a575 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -147,7 +147,7 @@ struct nfs_client_initdata { * Since these are allocated/deallocated very rarely, we don't * bother putting them in a slab cache... */ -static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) +struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) { struct nfs_client *clp; struct rpc_cred *cred; @@ -177,18 +177,6 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ clp->cl_proto = cl_init->proto; clp->cl_net = get_net(cl_init->net); -#ifdef CONFIG_NFS_V4 - err = nfs_get_cb_ident_idr(clp, cl_init->minorversion); - if (err) - goto error_cleanup; - - spin_lock_init(&clp->cl_lock); - INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); - rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); - clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; - clp->cl_minorversion = cl_init->minorversion; - clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion]; -#endif cred = rpc_lookup_machine_cred("*"); if (!IS_ERR(cred)) clp->cl_machine_cred = cred; @@ -218,6 +206,30 @@ static void nfs4_shutdown_session(struct nfs_client *clp) } #endif /* CONFIG_NFS_V4_1 */ +struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init) +{ + int err; + struct nfs_client *clp = nfs_alloc_client(cl_init); + if (IS_ERR(clp)) + return clp; + + err = nfs_get_cb_ident_idr(clp, cl_init->minorversion); + if (err) + goto error; + + spin_lock_init(&clp->cl_lock); + INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); + rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); + clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; + clp->cl_minorversion = cl_init->minorversion; + clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion]; + return clp; + +error: + kfree(clp); + return ERR_PTR(err); +} + /* * Destroy the NFS4 callback service */ @@ -588,7 +600,7 @@ nfs_get_client(const struct nfs_client_initdata *cl_init, spin_unlock(&nn->nfs_client_lock); - new = nfs_alloc_client(cl_init); + new = cl_init->rpc_ops->alloc_client(cl_init); } while (!IS_ERR(new)); dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n", diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 93b732523342..633af813984d 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -148,6 +148,7 @@ extern void nfs_umount(const struct nfs_mount_request *info); /* client.c */ extern const struct rpc_program nfs_program; extern void nfs_clients_init(struct net *net); +extern struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *); extern void nfs_cleanup_cb_ident_idr(struct net *); extern void nfs_put_client(struct nfs_client *); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 4ccb34bf1732..77c7aac228bb 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -934,6 +934,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .close_context = nfs_close_context, .have_delegation = nfs3_have_delegation, .return_delegation = nfs3_return_delegation, + .alloc_client = nfs_alloc_client, .init_client = nfs_init_client, .free_client = nfs_free_client, }; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 9889ee476e37..a0be2d1af04b 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -303,6 +303,8 @@ extern const u32 nfs4_fs_locations_bitmap[2]; void nfs4_free_client(struct nfs_client *); +struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *); + /* nfs4renewd.c */ extern void nfs4_schedule_state_renewal(struct nfs_client *); extern void nfs4_renewd_prepare_shutdown(struct nfs_server *); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f301c53926b2..7f39e7ecde6c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6806,6 +6806,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .open_context = nfs4_atomic_open, .have_delegation = nfs4_have_delegation, .return_delegation = nfs4_inode_return_delegation, + .alloc_client = nfs4_alloc_client, .init_client = nfs4_init_client, .free_client = nfs4_free_client, }; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 53620bf10969..99a002515dfe 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -790,6 +790,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .close_context = nfs_close_context, .have_delegation = nfs_have_delegation, .return_delegation = nfs_return_delegation, + .alloc_client = nfs_alloc_client, .init_client = nfs_init_client, .free_client = nfs_free_client, }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index e61dc7235d5d..4d62b774ddaf 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1353,6 +1353,7 @@ struct nfs_renamedata { struct nfs_access_entry; struct nfs_client; struct rpc_timeout; +struct nfs_client_initdata; /* * RPC procedure vector for NFSv2/NFSv3 demuxing @@ -1424,6 +1425,7 @@ struct nfs_rpc_ops { struct iattr *iattr); int (*have_delegation)(struct inode *, fmode_t); int (*return_delegation)(struct inode *); + struct nfs_client *(*alloc_client) (const struct nfs_client_initdata *); struct nfs_client * (*init_client) (struct nfs_client *, const struct rpc_timeout *, const char *, rpc_authflavor_t); -- cgit v1.2.3 From 1abb50886afe8a126705c93dab2b50c1252a9c19 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 20 Jun 2012 15:53:47 -0400 Subject: NFS: Create an read_pageio_init() function pNFS needs to select a read function based on the layout driver currently in use, so I let each NFS version decide how to best handle initializing reads. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/internal.h | 2 +- fs/nfs/nfs3proc.c | 1 + fs/nfs/nfs4proc.c | 1 + fs/nfs/pnfs.c | 11 +++++------ fs/nfs/pnfs.h | 6 +++--- fs/nfs/proc.c | 1 + fs/nfs/read.c | 16 +++------------- include/linux/nfs_xdr.h | 3 +++ 8 files changed, 18 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 633af813984d..b3121123b40d 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -306,7 +306,7 @@ extern int nfs_initiate_read(struct rpc_clnt *clnt, extern void nfs_read_prepare(struct rpc_task *task, void *calldata); extern int nfs_generic_pagein(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr); -extern void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio, +extern void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, const struct nfs_pgio_completion_ops *compl_ops); extern void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 77c7aac228bb..9864d05432da 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -921,6 +921,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .pathconf = nfs3_proc_pathconf, .decode_dirent = nfs3_decode_dirent, .read_setup = nfs3_proc_read_setup, + .read_pageio_init = nfs_pageio_init_read, .read_rpc_prepare = nfs3_proc_read_rpc_prepare, .read_done = nfs3_read_done, .write_setup = nfs3_proc_write_setup, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 7f39e7ecde6c..f99cf71f4e36 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6792,6 +6792,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .set_capabilities = nfs4_server_capabilities, .decode_dirent = nfs4_decode_dirent, .read_setup = nfs4_proc_read_setup, + .read_pageio_init = pnfs_pageio_init_read, .read_rpc_prepare = nfs4_proc_read_rpc_prepare, .read_done = nfs4_read_done, .write_setup = nfs4_proc_write_setup, diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index bbc49caa7a82..9c830603a16c 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1209,7 +1209,7 @@ pnfs_generic_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page * } EXPORT_SYMBOL_GPL(pnfs_generic_pg_init_write); -bool +void pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, const struct nfs_pgio_completion_ops *compl_ops) { @@ -1217,10 +1217,9 @@ pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld; if (ld == NULL) - return false; - nfs_pageio_init(pgio, inode, ld->pg_read_ops, compl_ops, - server->rsize, 0); - return true; + nfs_pageio_init_read(pgio, inode, compl_ops); + else + nfs_pageio_init(pgio, inode, ld->pg_read_ops, compl_ops, server->rsize, 0); } bool @@ -1427,7 +1426,7 @@ int pnfs_read_done_resend_to_mds(struct inode *inode, LIST_HEAD(failed); /* Resend all requests through the MDS */ - nfs_pageio_init_read_mds(&pgio, inode, compl_ops); + nfs_pageio_init_read(&pgio, inode, compl_ops); while (!list_empty(head)) { struct nfs_page *req = nfs_list_entry(head->next); diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 64f90d845f6a..80ee8919dd5e 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -178,7 +178,7 @@ extern int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp); void get_layout_hdr(struct pnfs_layout_hdr *lo); void put_lseg(struct pnfs_layout_segment *lseg); -bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *, struct inode *, +void pnfs_pageio_init_read(struct nfs_pageio_descriptor *, struct inode *, const struct nfs_pgio_completion_ops *); bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *, struct inode *, int, const struct nfs_pgio_completion_ops *); @@ -438,10 +438,10 @@ static inline void unset_pnfs_layoutdriver(struct nfs_server *s) { } -static inline bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, +static inline void pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, const struct nfs_pgio_completion_ops *compl_ops) { - return false; + nfs_pageio_init_read(pgio, inode, compl_ops); } static inline bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags, diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 99a002515dfe..6fea6e107bc3 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -778,6 +778,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .pathconf = nfs_proc_pathconf, .decode_dirent = nfs2_decode_dirent, .read_setup = nfs_proc_read_setup, + .read_pageio_init = nfs_pageio_init_read, .read_rpc_prepare = nfs_proc_read_rpc_prepare, .read_done = nfs_read_done, .write_setup = nfs_proc_write_setup, diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 86ced7836214..6267b873bbcb 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -20,8 +20,6 @@ #include #include -#include "pnfs.h" - #include "nfs4_fs.h" #include "internal.h" #include "iostat.h" @@ -108,7 +106,7 @@ int nfs_return_empty_page(struct page *page) return 0; } -void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio, +void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, const struct nfs_pgio_completion_ops *compl_ops) { @@ -123,14 +121,6 @@ void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio) } EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds); -void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, - struct inode *inode, - const struct nfs_pgio_completion_ops *compl_ops) -{ - if (!pnfs_pageio_init_read(pgio, inode, compl_ops)) - nfs_pageio_init_read_mds(pgio, inode, compl_ops); -} - int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, struct page *page) { @@ -149,7 +139,7 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, if (len < PAGE_CACHE_SIZE) zero_user_segment(page, len, PAGE_CACHE_SIZE); - nfs_pageio_init_read(&pgio, inode, &nfs_async_read_completion_ops); + NFS_PROTO(inode)->read_pageio_init(&pgio, inode, &nfs_async_read_completion_ops); nfs_pageio_add_request(&pgio, new); nfs_pageio_complete(&pgio); NFS_I(inode)->read_io += pgio.pg_bytes_written; @@ -652,7 +642,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping, if (ret == 0) goto read_complete; /* all pages were read */ - nfs_pageio_init_read(&pgio, inode, &nfs_async_read_completion_ops); + NFS_PROTO(inode)->read_pageio_init(&pgio, inode, &nfs_async_read_completion_ops); ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 4d62b774ddaf..e00b8b3c334e 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1354,6 +1354,7 @@ struct nfs_access_entry; struct nfs_client; struct rpc_timeout; struct nfs_client_initdata; +struct nfs_pageio_descriptor; /* * RPC procedure vector for NFSv2/NFSv3 demuxing @@ -1407,6 +1408,8 @@ struct nfs_rpc_ops { int (*set_capabilities)(struct nfs_server *, struct nfs_fh *); int (*decode_dirent)(struct xdr_stream *, struct nfs_entry *, int); void (*read_setup) (struct nfs_read_data *, struct rpc_message *); + void (*read_pageio_init)(struct nfs_pageio_descriptor *, struct inode *, + const struct nfs_pgio_completion_ops *); void (*read_rpc_prepare)(struct rpc_task *, struct nfs_read_data *); int (*read_done) (struct rpc_task *, struct nfs_read_data *); void (*write_setup) (struct nfs_write_data *, struct rpc_message *); -- cgit v1.2.3 From 57208fa7e51ca16cd68de8e8bf482f16b06d3ea1 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 20 Jun 2012 15:53:48 -0400 Subject: NFS: Create an write_pageio_init() function pNFS needs to select a write function based on the layout driver currently in use, so I let each NFS version decide how to best handle initializing writes. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/internal.h | 2 +- fs/nfs/nfs3proc.c | 1 + fs/nfs/nfs4proc.c | 1 + fs/nfs/pnfs.c | 11 +++++------ fs/nfs/pnfs.h | 6 +++--- fs/nfs/proc.c | 1 + fs/nfs/write.c | 18 ++++++------------ include/linux/nfs_xdr.h | 2 ++ 8 files changed, 20 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index b3121123b40d..7edc172c371e 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -320,7 +320,7 @@ extern struct nfs_write_header *nfs_writehdr_alloc(void); extern void nfs_writehdr_free(struct nfs_pgio_header *hdr); extern int nfs_generic_flush(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr); -extern void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio, +extern void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags, const struct nfs_pgio_completion_ops *compl_ops); extern void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 9864d05432da..f3344f7f46a9 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -925,6 +925,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .read_rpc_prepare = nfs3_proc_read_rpc_prepare, .read_done = nfs3_read_done, .write_setup = nfs3_proc_write_setup, + .write_pageio_init = nfs_pageio_init_write, .write_rpc_prepare = nfs3_proc_write_rpc_prepare, .write_done = nfs3_write_done, .commit_setup = nfs3_proc_commit_setup, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f99cf71f4e36..7d387cb8ceb5 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6796,6 +6796,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .read_rpc_prepare = nfs4_proc_read_rpc_prepare, .read_done = nfs4_read_done, .write_setup = nfs4_proc_write_setup, + .write_pageio_init = pnfs_pageio_init_write, .write_rpc_prepare = nfs4_proc_write_rpc_prepare, .write_done = nfs4_write_done, .commit_setup = nfs4_proc_commit_setup, diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 9c830603a16c..2617831afd39 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1222,7 +1222,7 @@ pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, nfs_pageio_init(pgio, inode, ld->pg_read_ops, compl_ops, server->rsize, 0); } -bool +void pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags, const struct nfs_pgio_completion_ops *compl_ops) @@ -1231,10 +1231,9 @@ pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld; if (ld == NULL) - return false; - nfs_pageio_init(pgio, inode, ld->pg_write_ops, compl_ops, - server->wsize, ioflags); - return true; + nfs_pageio_init_write(pgio, inode, ioflags, compl_ops); + else + nfs_pageio_init(pgio, inode, ld->pg_write_ops, compl_ops, server->wsize, ioflags); } bool @@ -1271,7 +1270,7 @@ int pnfs_write_done_resend_to_mds(struct inode *inode, LIST_HEAD(failed); /* Resend all requests through the MDS */ - nfs_pageio_init_write_mds(&pgio, inode, FLUSH_STABLE, compl_ops); + nfs_pageio_init_write(&pgio, inode, FLUSH_STABLE, compl_ops); while (!list_empty(head)) { struct nfs_page *req = nfs_list_entry(head->next); diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 80ee8919dd5e..592beb02c955 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -180,7 +180,7 @@ void put_lseg(struct pnfs_layout_segment *lseg); void pnfs_pageio_init_read(struct nfs_pageio_descriptor *, struct inode *, const struct nfs_pgio_completion_ops *); -bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *, struct inode *, +void pnfs_pageio_init_write(struct nfs_pageio_descriptor *, struct inode *, int, const struct nfs_pgio_completion_ops *); void set_pnfs_layoutdriver(struct nfs_server *, const struct nfs_fh *, u32); @@ -444,10 +444,10 @@ static inline void pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, str nfs_pageio_init_read(pgio, inode, compl_ops); } -static inline bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags, +static inline void pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags, const struct nfs_pgio_completion_ops *compl_ops) { - return false; + nfs_pageio_init_write(pgio, inode, ioflags, compl_ops); } static inline int diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 6fea6e107bc3..cf6499742b10 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -782,6 +782,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .read_rpc_prepare = nfs_proc_read_rpc_prepare, .read_done = nfs_read_done, .write_setup = nfs_proc_write_setup, + .write_pageio_init = nfs_pageio_init_write, .write_rpc_prepare = nfs_proc_write_rpc_prepare, .write_done = nfs_write_done, .commit_setup = nfs_proc_commit_setup, diff --git a/fs/nfs/write.c b/fs/nfs/write.c index f163355b9618..c11fb0025f0b 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -336,8 +336,10 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc struct nfs_pageio_descriptor pgio; int err; - nfs_pageio_init_write(&pgio, page->mapping->host, wb_priority(wbc), - &nfs_async_write_completion_ops); + NFS_PROTO(page->mapping->host)->write_pageio_init(&pgio, + page->mapping->host, + wb_priority(wbc), + &nfs_async_write_completion_ops); err = nfs_do_writepage(page, wbc, &pgio); nfs_pageio_complete(&pgio); if (err < 0) @@ -380,8 +382,7 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES); - nfs_pageio_init_write(&pgio, inode, wb_priority(wbc), - &nfs_async_write_completion_ops); + NFS_PROTO(inode)->write_pageio_init(&pgio, inode, wb_priority(wbc), &nfs_async_write_completion_ops); err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio); nfs_pageio_complete(&pgio); @@ -1202,7 +1203,7 @@ static const struct nfs_pageio_ops nfs_pageio_write_ops = { .pg_doio = nfs_generic_pg_writepages, }; -void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio, +void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags, const struct nfs_pgio_completion_ops *compl_ops) { @@ -1217,13 +1218,6 @@ void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio) } EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds); -void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, - struct inode *inode, int ioflags, - const struct nfs_pgio_completion_ops *compl_ops) -{ - if (!pnfs_pageio_init_write(pgio, inode, ioflags, compl_ops)) - nfs_pageio_init_write_mds(pgio, inode, ioflags, compl_ops); -} void nfs_write_prepare(struct rpc_task *task, void *calldata) { diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index e00b8b3c334e..8ed8ec628290 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1413,6 +1413,8 @@ struct nfs_rpc_ops { void (*read_rpc_prepare)(struct rpc_task *, struct nfs_read_data *); int (*read_done) (struct rpc_task *, struct nfs_read_data *); void (*write_setup) (struct nfs_write_data *, struct rpc_message *); + void (*write_pageio_init)(struct nfs_pageio_descriptor *, struct inode *, int, + const struct nfs_pgio_completion_ops *); void (*write_rpc_prepare)(struct rpc_task *, struct nfs_write_data *); int (*write_done) (struct rpc_task *, struct nfs_write_data *); void (*commit_setup) (struct nfs_commit_data *, struct rpc_message *); -- cgit v1.2.3 From b37d2a3a75cb0e72e18c29336cb2095b63dabfc8 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 29 Jun 2012 07:47:19 -0300 Subject: [media] i2c: Export an unlocked flavor of i2c_transfer Some drivers (in particular for TV cards) need exclusive access to their I2C buses for specific operations. Export an unlocked flavor of i2c_transfer to give them full control. The unlocked flavor has the following limitations: * Obviously, caller must hold the i2c adapter lock. * No debug messages are logged. We don't want to log messages while holding a rt_mutex. * No check is done on the existence of adap->algo->master_xfer. It is thus the caller's responsibility to ensure that the function is OK to call. Signed-off-by: Jean Delvare Signed-off-by: Mauro Carvalho Chehab --- drivers/i2c/i2c-core.c | 44 +++++++++++++++++++++++++++++++++----------- include/linux/i2c.h | 3 +++ 2 files changed, 36 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index feb7dc359186..ccc6445979c4 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1294,6 +1294,37 @@ module_exit(i2c_exit); * ---------------------------------------------------- */ +/** + * __i2c_transfer - unlocked flavor of i2c_transfer + * @adap: Handle to I2C bus + * @msgs: One or more messages to execute before STOP is issued to + * terminate the operation; each message begins with a START. + * @num: Number of messages to be executed. + * + * Returns negative errno, else the number of messages executed. + * + * Adapter lock must be held when calling this function. No debug logging + * takes place. adap->algo->master_xfer existence isn't checked. + */ +int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + unsigned long orig_jiffies; + int ret, try; + + /* Retry automatically on arbitration loss */ + orig_jiffies = jiffies; + for (ret = 0, try = 0; try <= adap->retries; try++) { + ret = adap->algo->master_xfer(adap, msgs, num); + if (ret != -EAGAIN) + break; + if (time_after(jiffies, orig_jiffies + adap->timeout)) + break; + } + + return ret; +} +EXPORT_SYMBOL(__i2c_transfer); + /** * i2c_transfer - execute a single or combined I2C message * @adap: Handle to I2C bus @@ -1308,8 +1339,7 @@ module_exit(i2c_exit); */ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { - unsigned long orig_jiffies; - int ret, try; + int ret; /* REVISIT the fault reporting model here is weak: * @@ -1347,15 +1377,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) i2c_lock_adapter(adap); } - /* Retry automatically on arbitration loss */ - orig_jiffies = jiffies; - for (ret = 0, try = 0; try <= adap->retries; try++) { - ret = adap->algo->master_xfer(adap, msgs, num); - if (ret != -EAGAIN) - break; - if (time_after(jiffies, orig_jiffies + adap->timeout)) - break; - } + ret = __i2c_transfer(adap, msgs, num); i2c_unlock_adapter(adap); return ret; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 195d8b3d9cfb..a121c012309a 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -68,6 +68,9 @@ extern int i2c_master_recv(const struct i2c_client *client, char *buf, */ extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); +/* Unlocked flavor */ +extern int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num); /* This is the very generalized SMBus access routine. You probably do not want to use this, though; one of the functions below may be much easier, -- cgit v1.2.3 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') 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 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') 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 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') 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 From baedee177e6c553af455865718971d9a9c75e537 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 17 Jun 2012 16:40:36 +0200 Subject: firewire: core: add is_local sysfs device attribute Making this information available in sysfs allows to differentiate between controllers in the local and remote Linux PCs, and thus is useful for servers that are started with udev rules. Signed-off-by: Clemens Ladisch Signed-off-by: Stefan Richter --- drivers/firewire/core-device.c | 9 +++++++++ include/linux/firewire.h | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 4d460ef87161..7a05fd24d68b 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -398,6 +398,14 @@ static ssize_t guid_show(struct device *dev, return ret; } +static ssize_t is_local_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fw_device *device = fw_device(dev); + + return sprintf(buf, "%u\n", device->is_local); +} + static int units_sprintf(char *buf, const u32 *directory) { struct fw_csr_iterator ci; @@ -447,6 +455,7 @@ static ssize_t units_show(struct device *dev, static struct device_attribute fw_device_attributes[] = { __ATTR_RO(config_rom), __ATTR_RO(guid), + __ATTR_RO(is_local), __ATTR_RO(units), __ATTR_NULL, }; diff --git a/include/linux/firewire.h b/include/linux/firewire.h index d77f60c6d1ed..cb2445e2e10e 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -138,7 +138,7 @@ struct fw_card { struct fw_attribute_group { struct attribute_group *groups[2]; struct attribute_group group; - struct attribute *attrs[12]; + struct attribute *attrs[13]; }; enum fw_device_state { -- cgit v1.2.3 From 8ec4cf5303e03941fa5fd91bbb9c85bd4ae88c47 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 25 Jun 2012 14:52:49 +0200 Subject: iio:adc: Add AD7265/AD7266 support This patch adds support for the Analog Devices AD7265 and AD7266 Analog-to-Digital converters. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 10 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad7266.c | 536 +++++++++++++++++++++++++++++++++++ include/linux/platform_data/ad7266.h | 54 ++++ 4 files changed, 601 insertions(+) create mode 100644 drivers/iio/adc/ad7266.c create mode 100644 include/linux/platform_data/ad7266.h (limited to 'include') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 4f7f584cfd61..8a78b4f3ef58 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -3,6 +3,16 @@ # menu "Analog to digital converters" +config AD7266 + tristate "Analog Devices AD7265/AD7266 ADC driver" + depends on SPI_MASTER + select IIO_BUFFER + select IIO_TRIGGER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices AD7265 and AD7266 + ADCs. + config AT91_ADC tristate "Atmel AT91 ADC" depends on ARCH_AT91 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 175c8d41ea99..52eec254c38c 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -2,4 +2,5 @@ # Makefile for IIO ADC drivers # +obj-$(CONFIG_AD7266) += ad7266.o obj-$(CONFIG_AT91_ADC) += at91_adc.o diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c new file mode 100644 index 000000000000..5c3f1ba5a06d --- /dev/null +++ b/drivers/iio/adc/ad7266.c @@ -0,0 +1,536 @@ +/* + * AD7266/65 SPI ADC driver + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +struct ad7266_state { + struct spi_device *spi; + struct regulator *reg; + unsigned long vref_uv; + + struct spi_transfer single_xfer[3]; + struct spi_message single_msg; + + enum ad7266_range range; + enum ad7266_mode mode; + bool fixed_addr; + struct gpio gpios[3]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + * The buffer needs to be large enough to hold two samples (4 bytes) and + * the naturally aligned timestamp (8 bytes). + */ + uint8_t data[ALIGN(4, sizeof(s64)) + sizeof(s64)] ____cacheline_aligned; +}; + +static int ad7266_wakeup(struct ad7266_state *st) +{ + /* Any read with >= 2 bytes will wake the device */ + return spi_read(st->spi, st->data, 2); +} + +static int ad7266_powerdown(struct ad7266_state *st) +{ + /* Any read with < 2 bytes will powerdown the device */ + return spi_read(st->spi, st->data, 1); +} + +static int ad7266_preenable(struct iio_dev *indio_dev) +{ + struct ad7266_state *st = iio_priv(indio_dev); + int ret; + + ret = ad7266_wakeup(st); + if (ret) + return ret; + + ret = iio_sw_buffer_preenable(indio_dev); + if (ret) + ad7266_powerdown(st); + + return ret; +} + +static int ad7266_postdisable(struct iio_dev *indio_dev) +{ + struct ad7266_state *st = iio_priv(indio_dev); + return ad7266_powerdown(st); +} + +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { + .preenable = &ad7266_preenable, + .postenable = &iio_triggered_buffer_postenable, + .predisable = &iio_triggered_buffer_predisable, + .postdisable = &ad7266_postdisable, +}; + +static irqreturn_t ad7266_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct iio_buffer *buffer = indio_dev->buffer; + struct ad7266_state *st = iio_priv(indio_dev); + int ret; + + ret = spi_read(st->spi, st->data, 4); + if (ret == 0) { + if (indio_dev->scan_timestamp) + ((s64 *)st->data)[1] = pf->timestamp; + iio_push_to_buffer(buffer, (u8 *)st->data, pf->timestamp); + } + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static void ad7266_select_input(struct ad7266_state *st, unsigned int nr) +{ + unsigned int i; + + if (st->fixed_addr) + return; + + switch (st->mode) { + case AD7266_MODE_SINGLE_ENDED: + nr >>= 1; + break; + case AD7266_MODE_PSEUDO_DIFF: + nr |= 1; + break; + case AD7266_MODE_DIFF: + nr &= ~1; + break; + } + + for (i = 0; i < 3; ++i) + gpio_set_value(st->gpios[i].gpio, (bool)(nr & BIT(i))); +} + +static int ad7266_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad7266_state *st = iio_priv(indio_dev); + unsigned int nr = find_first_bit(scan_mask, indio_dev->masklength); + + ad7266_select_input(st, nr); + + return 0; +} + +static int ad7266_read_single(struct ad7266_state *st, int *val, + unsigned int address) +{ + int ret; + + ad7266_select_input(st, address); + + ret = spi_sync(st->spi, &st->single_msg); + *val = be16_to_cpu(st->data[address % 2]); + + return ret; +} + +static int ad7266_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long m) +{ + struct ad7266_state *st = iio_priv(indio_dev); + unsigned long scale_uv; + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + + ret = ad7266_read_single(st, val, chan->address); + if (ret) + return ret; + + *val = (*val >> 2) & 0xfff; + if (chan->scan_type.sign == 's') + *val = sign_extend32(*val, 11); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_uv * 100); + if (st->mode == AD7266_MODE_DIFF) + scale_uv *= 2; + if (st->range == AD7266_RANGE_2VREF) + scale_uv *= 2; + + scale_uv >>= chan->scan_type.realbits; + *val = scale_uv / 100000; + *val2 = (scale_uv % 100000) * 10; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + if (st->range == AD7266_RANGE_2VREF && + st->mode != AD7266_MODE_DIFF) + *val = 2048; + else + *val = 0; + return IIO_VAL_INT; + } + return -EINVAL; +} + +#define AD7266_CHAN(_chan, _sign) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan), \ + .address = (_chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT \ + | IIO_CHAN_INFO_SCALE_SHARED_BIT \ + | IIO_CHAN_INFO_OFFSET_SHARED_BIT, \ + .scan_index = (_chan), \ + .scan_type = { \ + .sign = (_sign), \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 2, \ + .endianness = IIO_BE, \ + }, \ +} + +#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_##_name[] = { \ + AD7266_CHAN(0, (_sign)), \ + AD7266_CHAN(1, (_sign)), \ + AD7266_CHAN(2, (_sign)), \ + AD7266_CHAN(3, (_sign)), \ + AD7266_CHAN(4, (_sign)), \ + AD7266_CHAN(5, (_sign)), \ + AD7266_CHAN(6, (_sign)), \ + AD7266_CHAN(7, (_sign)), \ + AD7266_CHAN(8, (_sign)), \ + AD7266_CHAN(9, (_sign)), \ + AD7266_CHAN(10, (_sign)), \ + AD7266_CHAN(11, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(13), \ +} + +#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_##_name##_fixed[] = { \ + AD7266_CHAN(0, (_sign)), \ + AD7266_CHAN(1, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(2), \ +} + +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(u, 'u'); +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(s, 's'); +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(u, 'u'); +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(s, 's'); + +#define AD7266_CHAN_DIFF(_chan, _sign) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan) * 2, \ + .channel2 = (_chan) * 2 + 1, \ + .address = (_chan), \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT \ + | IIO_CHAN_INFO_SCALE_SHARED_BIT \ + | IIO_CHAN_INFO_OFFSET_SHARED_BIT, \ + .scan_index = (_chan), \ + .scan_type = { \ + .sign = _sign, \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 2, \ + .endianness = IIO_BE, \ + }, \ + .differential = 1, \ +} + +#define AD7266_DECLARE_DIFF_CHANNELS(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_diff_##_name[] = { \ + AD7266_CHAN_DIFF(0, (_sign)), \ + AD7266_CHAN_DIFF(1, (_sign)), \ + AD7266_CHAN_DIFF(2, (_sign)), \ + AD7266_CHAN_DIFF(3, (_sign)), \ + AD7266_CHAN_DIFF(4, (_sign)), \ + AD7266_CHAN_DIFF(5, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(6), \ +} + +static AD7266_DECLARE_DIFF_CHANNELS(s, 's'); +static AD7266_DECLARE_DIFF_CHANNELS(u, 'u'); + +#define AD7266_DECLARE_DIFF_CHANNELS_FIXED(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_diff_fixed_##_name[] = { \ + AD7266_CHAN_DIFF(0, (_sign)), \ + AD7266_CHAN_DIFF(1, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(2), \ +} + +static AD7266_DECLARE_DIFF_CHANNELS_FIXED(s, 's'); +static AD7266_DECLARE_DIFF_CHANNELS_FIXED(u, 'u'); + +static const struct iio_info ad7266_info = { + .read_raw = &ad7266_read_raw, + .update_scan_mode = &ad7266_update_scan_mode, + .driver_module = THIS_MODULE, +}; + +static unsigned long ad7266_available_scan_masks[] = { + 0x003, + 0x00c, + 0x030, + 0x0c0, + 0x300, + 0xc00, + 0x000, +}; + +static unsigned long ad7266_available_scan_masks_diff[] = { + 0x003, + 0x00c, + 0x030, + 0x000, +}; + +static unsigned long ad7266_available_scan_masks_fixed[] = { + 0x003, + 0x000, +}; + +struct ad7266_chan_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + unsigned long *scan_masks; +}; + +#define AD7266_CHAN_INFO_INDEX(_differential, _signed, _fixed) \ + (((_differential) << 2) | ((_signed) << 1) | ((_fixed) << 0)) + +static const struct ad7266_chan_info ad7266_chan_infos[] = { + [AD7266_CHAN_INFO_INDEX(0, 0, 0)] = { + .channels = ad7266_channels_u, + .num_channels = ARRAY_SIZE(ad7266_channels_u), + .scan_masks = ad7266_available_scan_masks, + }, + [AD7266_CHAN_INFO_INDEX(0, 0, 1)] = { + .channels = ad7266_channels_u_fixed, + .num_channels = ARRAY_SIZE(ad7266_channels_u_fixed), + .scan_masks = ad7266_available_scan_masks_fixed, + }, + [AD7266_CHAN_INFO_INDEX(0, 1, 0)] = { + .channels = ad7266_channels_s, + .num_channels = ARRAY_SIZE(ad7266_channels_s), + .scan_masks = ad7266_available_scan_masks, + }, + [AD7266_CHAN_INFO_INDEX(0, 1, 1)] = { + .channels = ad7266_channels_s_fixed, + .num_channels = ARRAY_SIZE(ad7266_channels_s_fixed), + .scan_masks = ad7266_available_scan_masks_fixed, + }, + [AD7266_CHAN_INFO_INDEX(1, 0, 0)] = { + .channels = ad7266_channels_diff_u, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_u), + .scan_masks = ad7266_available_scan_masks_diff, + }, + [AD7266_CHAN_INFO_INDEX(1, 0, 1)] = { + .channels = ad7266_channels_diff_fixed_u, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_u), + .scan_masks = ad7266_available_scan_masks_fixed, + }, + [AD7266_CHAN_INFO_INDEX(1, 1, 0)] = { + .channels = ad7266_channels_diff_s, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_s), + .scan_masks = ad7266_available_scan_masks_diff, + }, + [AD7266_CHAN_INFO_INDEX(1, 1, 1)] = { + .channels = ad7266_channels_diff_fixed_s, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_s), + .scan_masks = ad7266_available_scan_masks_fixed, + }, +}; + +static void __devinit ad7266_init_channels(struct iio_dev *indio_dev) +{ + struct ad7266_state *st = iio_priv(indio_dev); + bool is_differential, is_signed; + const struct ad7266_chan_info *chan_info; + int i; + + is_differential = st->mode != AD7266_MODE_SINGLE_ENDED; + is_signed = (st->range == AD7266_RANGE_2VREF) | + (st->mode == AD7266_MODE_DIFF); + + i = AD7266_CHAN_INFO_INDEX(is_differential, is_signed, st->fixed_addr); + chan_info = &ad7266_chan_infos[i]; + + indio_dev->channels = chan_info->channels; + indio_dev->num_channels = chan_info->num_channels; + indio_dev->available_scan_masks = chan_info->scan_masks; + indio_dev->masklength = chan_info->num_channels - 1; +} + +static const char * const ad7266_gpio_labels[] = { + "AD0", "AD1", "AD2", +}; + +static int __devinit ad7266_probe(struct spi_device *spi) +{ + struct ad7266_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev; + struct ad7266_state *st; + unsigned int i; + int ret; + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = regulator_get(&spi->dev, "vref"); + if (!IS_ERR_OR_NULL(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + + st->vref_uv = regulator_get_voltage(st->reg); + } else { + /* Use internal reference */ + st->vref_uv = 2500000; + } + + if (pdata) { + st->fixed_addr = pdata->fixed_addr; + st->mode = pdata->mode; + st->range = pdata->range; + + if (!st->fixed_addr) { + for (i = 0; i < ARRAY_SIZE(st->gpios); ++i) { + st->gpios[i].gpio = pdata->addr_gpios[i]; + st->gpios[i].flags = GPIOF_OUT_INIT_LOW; + st->gpios[i].label = ad7266_gpio_labels[i]; + } + ret = gpio_request_array(st->gpios, + ARRAY_SIZE(st->gpios)); + if (ret) + goto error_disable_reg; + } + } else { + st->fixed_addr = true; + st->range = AD7266_RANGE_VREF; + st->mode = AD7266_MODE_DIFF; + } + + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad7266_info; + + ad7266_init_channels(indio_dev); + + /* wakeup */ + st->single_xfer[0].rx_buf = &st->data; + st->single_xfer[0].len = 2; + st->single_xfer[0].cs_change = 1; + /* conversion */ + st->single_xfer[1].rx_buf = &st->data; + st->single_xfer[1].len = 4; + st->single_xfer[1].cs_change = 1; + /* powerdown */ + st->single_xfer[2].tx_buf = &st->data; + st->single_xfer[2].len = 1; + + spi_message_init(&st->single_msg); + spi_message_add_tail(&st->single_xfer[0], &st->single_msg); + spi_message_add_tail(&st->single_xfer[1], &st->single_msg); + spi_message_add_tail(&st->single_xfer[2], &st->single_msg); + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + &ad7266_trigger_handler, &iio_triggered_buffer_setup_ops); + if (ret) + goto error_free_gpios; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_buffer_cleanup; + + return 0; + +error_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); +error_free_gpios: + if (!st->fixed_addr) + gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios)); +error_disable_reg: + if (!IS_ERR_OR_NULL(st->reg)) + regulator_disable(st->reg); +error_put_reg: + if (!IS_ERR_OR_NULL(st->reg)) + regulator_put(st->reg); + + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad7266_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7266_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (!st->fixed_addr) + gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios)); + if (!IS_ERR_OR_NULL(st->reg)) { + regulator_disable(st->reg); + regulator_put(st->reg); + } + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad7266_id[] = { + {"ad7265", 0}, + {"ad7266", 0}, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7266_id); + +static struct spi_driver ad7266_driver = { + .driver = { + .name = "ad7266", + .owner = THIS_MODULE, + }, + .probe = ad7266_probe, + .remove = __devexit_p(ad7266_remove), + .id_table = ad7266_id, +}; +module_spi_driver(ad7266_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Devices AD7266/65 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/ad7266.h b/include/linux/platform_data/ad7266.h new file mode 100644 index 000000000000..eabfdcb26992 --- /dev/null +++ b/include/linux/platform_data/ad7266.h @@ -0,0 +1,54 @@ +/* + * AD7266/65 SPI ADC driver + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef __IIO_ADC_AD7266_H__ +#define __IIO_ADC_AD7266_H__ + +/** + * enum ad7266_range - AD7266 reference voltage range + * @AD7266_RANGE_VREF: Device is configured for input range 0V - VREF + * (RANGE pin set to low) + * @AD7266_RANGE_2VREF: Device is configured for input range 0V - 2VREF + * (RANGE pin set to high) + */ +enum ad7266_range { + AD7266_RANGE_VREF, + AD7266_RANGE_2VREF, +}; + +/** + * enum ad7266_mode - AD7266 sample mode + * @AD7266_MODE_DIFF: Device is configured for full differential mode + * (SGL/DIFF pin set to low, AD0 pin set to low) + * @AD7266_MODE_PSEUDO_DIFF: Device is configured for pseudo differential mode + * (SGL/DIFF pin set to low, AD0 pin set to high) + * @AD7266_MODE_SINGLE_ENDED: Device is configured for single-ended mode + * (SGL/DIFF pin set to high) + */ +enum ad7266_mode { + AD7266_MODE_DIFF, + AD7266_MODE_PSEUDO_DIFF, + AD7266_MODE_SINGLE_ENDED, +}; + +/** + * struct ad7266_platform_data - Platform data for the AD7266 driver + * @range: Reference voltage range the device is configured for + * @mode: Sample mode the device is configured for + * @fixed_addr: Whether the address pins are hard-wired + * @addr_gpios: GPIOs used for controlling the address pins, only used if + * fixed_addr is set to false. + */ +struct ad7266_platform_data { + enum ad7266_range range; + enum ad7266_mode mode; + bool fixed_addr; + unsigned int addr_gpios[3]; +}; + +#endif -- cgit v1.2.3 From 314be14bb89369b2164125b0ec3b24d85b407b62 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 1 May 2012 21:04:24 +0100 Subject: iio: Rename _st_ functions to loose the bit that meant the staging version. These were originally introduced when the plan was to have parallel IIO cores in and out of staging with a slow move between them. Now we have reached the point where the whole core has moved, they need clearing up! Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 33 +++++++++++++++------------------ drivers/staging/iio/iio_hwmon.c | 12 ++++++------ include/linux/iio/consumer.h | 34 +++++++++++++++++----------------- 3 files changed, 38 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index d4760bd1e9b1..9a46ca61ef02 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -92,8 +92,7 @@ error_ret: EXPORT_SYMBOL_GPL(iio_map_array_unregister); static const struct iio_chan_spec -*iio_chan_spec_from_name(const struct iio_dev *indio_dev, - const char *name) +*iio_chan_spec_from_name(const struct iio_dev *indio_dev, const char *name) { int i; const struct iio_chan_spec *chan = NULL; @@ -108,8 +107,7 @@ static const struct iio_chan_spec } -struct iio_channel *iio_st_channel_get(const char *name, - const char *channel_name) +struct iio_channel *iio_channel_get(const char *name, const char *channel_name) { struct iio_map_internal *c_i = NULL, *c = NULL; struct iio_channel *channel; @@ -145,16 +143,16 @@ struct iio_channel *iio_st_channel_get(const char *name, return channel; } -EXPORT_SYMBOL_GPL(iio_st_channel_get); +EXPORT_SYMBOL_GPL(iio_channel_get); -void iio_st_channel_release(struct iio_channel *channel) +void iio_channel_release(struct iio_channel *channel) { iio_device_put(channel->indio_dev); kfree(channel); } -EXPORT_SYMBOL_GPL(iio_st_channel_release); +EXPORT_SYMBOL_GPL(iio_channel_release); -struct iio_channel *iio_st_channel_get_all(const char *name) +struct iio_channel *iio_channel_get_all(const char *name) { struct iio_channel *chans; struct iio_map_internal *c = NULL; @@ -217,9 +215,9 @@ error_ret: return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(iio_st_channel_get_all); +EXPORT_SYMBOL_GPL(iio_channel_get_all); -void iio_st_channel_release_all(struct iio_channel *channels) +void iio_channel_release_all(struct iio_channel *channels) { struct iio_channel *chan = &channels[0]; @@ -229,9 +227,9 @@ void iio_st_channel_release_all(struct iio_channel *channels) } kfree(channels); } -EXPORT_SYMBOL_GPL(iio_st_channel_release_all); +EXPORT_SYMBOL_GPL(iio_channel_release_all); -int iio_st_read_channel_raw(struct iio_channel *chan, int *val) +int iio_read_channel_raw(struct iio_channel *chan, int *val) { int val2, ret; @@ -248,9 +246,9 @@ err_unlock: return ret; } -EXPORT_SYMBOL_GPL(iio_st_read_channel_raw); +EXPORT_SYMBOL_GPL(iio_read_channel_raw); -int iio_st_read_channel_scale(struct iio_channel *chan, int *val, int *val2) +int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2) { int ret; @@ -269,10 +267,9 @@ err_unlock: return ret; } -EXPORT_SYMBOL_GPL(iio_st_read_channel_scale); +EXPORT_SYMBOL_GPL(iio_read_channel_scale); -int iio_st_get_channel_type(struct iio_channel *chan, - enum iio_chan_type *type) +int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) { int ret = 0; /* Need to verify underlying driver has not gone away */ @@ -289,4 +286,4 @@ err_unlock: return ret; } -EXPORT_SYMBOL_GPL(iio_st_get_channel_type); +EXPORT_SYMBOL_GPL(iio_get_channel_type); diff --git a/drivers/staging/iio/iio_hwmon.c b/drivers/staging/iio/iio_hwmon.c index b03554fee443..27d27ec9521f 100644 --- a/drivers/staging/iio/iio_hwmon.c +++ b/drivers/staging/iio/iio_hwmon.c @@ -51,12 +51,12 @@ static ssize_t iio_hwmon_read_val(struct device *dev, * No locking between this pair, so theoretically possible * the scale has changed. */ - ret = iio_st_read_channel_raw(&state->channels[sattr->index], + ret = iio_read_channel_raw(&state->channels[sattr->index], &val); if (ret < 0) return ret; - ret = iio_st_read_channel_scale(&state->channels[sattr->index], + ret = iio_read_channel_scale(&state->channels[sattr->index], &scaleint, &scalepart); if (ret < 0) return ret; @@ -106,7 +106,7 @@ static int __devinit iio_hwmon_probe(struct platform_device *pdev) goto error_ret; } - st->channels = iio_st_channel_get_all(dev_name(&pdev->dev)); + st->channels = iio_channel_get_all(dev_name(&pdev->dev)); if (IS_ERR(st->channels)) { ret = PTR_ERR(st->channels); goto error_free_state; @@ -130,7 +130,7 @@ static int __devinit iio_hwmon_probe(struct platform_device *pdev) } sysfs_attr_init(&a->dev_attr.attr); - ret = iio_st_get_channel_type(&st->channels[i], &type); + ret = iio_get_channel_type(&st->channels[i], &type); if (ret < 0) { kfree(a); goto error_free_attrs; @@ -186,7 +186,7 @@ error_free_attrs: iio_hwmon_free_attrs(st); kfree(st->attrs); error_release_channels: - iio_st_channel_release_all(st->channels); + iio_channel_release_all(st->channels); error_free_state: kfree(st); error_ret: @@ -201,7 +201,7 @@ static int __devexit iio_hwmon_remove(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &st->attr_group); iio_hwmon_free_attrs(st); kfree(st->attrs); - iio_st_channel_release_all(st->channels); + iio_channel_release_all(st->channels); return 0; } diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 1a15e560a5a1..e2657e6d4d26 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -33,17 +33,17 @@ struct iio_channel { * side. This typically describes the channels use within * the consumer. E.g. 'battery_voltage' */ -struct iio_channel *iio_st_channel_get(const char *name, - const char *consumer_channel); +struct iio_channel *iio_channel_get(const char *name, + const char *consumer_channel); /** - * iio_st_channel_release() - release channels obtained via iio_st_channel_get + * iio_channel_release() - release channels obtained via iio_channel_get * @chan: The channel to be released. */ -void iio_st_channel_release(struct iio_channel *chan); +void iio_channel_release(struct iio_channel *chan); /** - * iio_st_channel_get_all() - get all channels associated with a client + * iio_channel_get_all() - get all channels associated with a client * @name: name of consumer device. * * Returns an array of iio_channel structures terminated with one with @@ -51,37 +51,37 @@ void iio_st_channel_release(struct iio_channel *chan); * This function is used by fairly generic consumers to get all the * channels registered as having this consumer. */ -struct iio_channel *iio_st_channel_get_all(const char *name); +struct iio_channel *iio_channel_get_all(const char *name); /** - * iio_st_channel_release_all() - reverse iio_st_get_all + * iio_channel_release_all() - reverse iio_channel_get_all * @chan: Array of channels to be released. */ -void iio_st_channel_release_all(struct iio_channel *chan); +void iio_channel_release_all(struct iio_channel *chan); /** - * iio_st_read_channel_raw() - read from a given channel + * iio_read_channel_raw() - read from a given channel * @channel: The channel being queried. * @val: Value read back. * * Note raw reads from iio channels are in adc counts and hence * scale will need to be applied if standard units required. */ -int iio_st_read_channel_raw(struct iio_channel *chan, - int *val); +int iio_read_channel_raw(struct iio_channel *chan, + int *val); /** - * iio_st_get_channel_type() - get the type of a channel + * iio_get_channel_type() - get the type of a channel * @channel: The channel being queried. * @type: The type of the channel. * * returns the enum iio_chan_type of the channel */ -int iio_st_get_channel_type(struct iio_channel *channel, - enum iio_chan_type *type); +int iio_get_channel_type(struct iio_channel *channel, + enum iio_chan_type *type); /** - * iio_st_read_channel_scale() - read the scale value for a channel + * iio_read_channel_scale() - read the scale value for a channel * @channel: The channel being queried. * @val: First part of value read back. * @val2: Second part of value read back. @@ -90,7 +90,7 @@ int iio_st_get_channel_type(struct iio_channel *channel, * as IIO_VAL_INT_PLUS_MICRO telling us we have a value of val * + val2/1e6 */ -int iio_st_read_channel_scale(struct iio_channel *chan, int *val, - int *val2); +int iio_read_channel_scale(struct iio_channel *chan, int *val, + int *val2); #endif -- cgit v1.2.3 From 8f5879b20be7f918cdc4b3d831cfd8f3dc02c74c Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 5 May 2012 10:39:22 +0100 Subject: IIO: Add a modifier for sqrt(x^2+y^2) There will probably be a number of such modifiers eventually but this one is used in the adis16204 accelerometer driver. Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 1 + include/linux/iio/types.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index a5a446beb2fa..e42749ec5c3c 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -70,6 +70,7 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_X] = "x", [IIO_MOD_Y] = "y", [IIO_MOD_Z] = "z", + [IIO_MOD_ROOT_SUM_SQUARED_X_Y] = "sqrt(x^2+y^2)", [IIO_MOD_LIGHT_BOTH] = "both", [IIO_MOD_LIGHT_IR] = "ir", }; diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index d086736a9033..210559ddf8a3 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -44,6 +44,7 @@ enum iio_modifier { IIO_MOD_X_OR_Y_OR_Z, IIO_MOD_LIGHT_BOTH, IIO_MOD_LIGHT_IR, + IIO_MOD_ROOT_SUM_SQUARED_X_Y, }; #define IIO_VAL_INT 1 -- cgit v1.2.3 From cf82cb8128496955a38fa62e1819ceb1d596e2eb Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 5 May 2012 10:56:41 +0100 Subject: IIO: Add a modifier for x^2+y^2+z^2 There will probably be a number of such modifiers eventually but this one is used in the adis16240 accelerometer driver. Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 1 + include/linux/iio/types.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index e42749ec5c3c..bb3c692e49b8 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -71,6 +71,7 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_Y] = "y", [IIO_MOD_Z] = "z", [IIO_MOD_ROOT_SUM_SQUARED_X_Y] = "sqrt(x^2+y^2)", + [IIO_MOD_SUM_SQUARED_X_Y_Z] = "x^2+y^2+z^2", [IIO_MOD_LIGHT_BOTH] = "both", [IIO_MOD_LIGHT_IR] = "ir", }; diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 210559ddf8a3..e25040173346 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -45,6 +45,7 @@ enum iio_modifier { IIO_MOD_LIGHT_BOTH, IIO_MOD_LIGHT_IR, IIO_MOD_ROOT_SUM_SQUARED_X_Y, + IIO_MOD_SUM_SQUARED_X_Y_Z, }; #define IIO_VAL_INT 1 -- cgit v1.2.3 From 38b3fef1730319e2730af3fc9f73698e3a9aeb4a Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 15 Jun 2012 11:50:28 +0300 Subject: Bluetooth: Improve debugging messages for hci_conn Improve debugging of hci_conn objects by: adding print to hci_conn refcounting, adding object spcifier when missing, change conn to hcon since conn is heavily used for l2cap_conn objects and this is misleading. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 6 ++++++ net/bluetooth/hci_conn.c | 44 ++++++++++++++++++++-------------------- 2 files changed, 28 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 75766b7f0dc7..475b8c04ba52 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -587,12 +587,18 @@ void hci_conn_put_device(struct hci_conn *conn); static inline void hci_conn_hold(struct hci_conn *conn) { + BT_DBG("hcon %p refcnt %d -> %d", conn, atomic_read(&conn->refcnt), + atomic_read(&conn->refcnt) + 1); + atomic_inc(&conn->refcnt); cancel_delayed_work(&conn->disc_work); } static inline void hci_conn_put(struct hci_conn *conn) { + BT_DBG("hcon %p refcnt %d -> %d", conn, atomic_read(&conn->refcnt), + atomic_read(&conn->refcnt) - 1); + if (atomic_dec_and_test(&conn->refcnt)) { unsigned long timeo; if (conn->type == ACL_LINK || conn->type == LE_LINK) { diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 2fcced377e50..9bbef6e95d2c 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -107,7 +107,7 @@ static void hci_acl_connect_cancel(struct hci_conn *conn) { struct hci_cp_create_conn_cancel cp; - BT_DBG("%p", conn); + BT_DBG("hcon %p", conn); if (conn->hdev->hci_ver < BLUETOOTH_VER_1_2) return; @@ -120,7 +120,7 @@ void hci_acl_disconn(struct hci_conn *conn, __u8 reason) { struct hci_cp_disconnect cp; - BT_DBG("%p", conn); + BT_DBG("hcon %p", conn); conn->state = BT_DISCONN; @@ -134,7 +134,7 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle) struct hci_dev *hdev = conn->hdev; struct hci_cp_add_sco cp; - BT_DBG("%p", conn); + BT_DBG("hcon %p", conn); conn->state = BT_CONNECT; conn->out = true; @@ -152,7 +152,7 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle) struct hci_dev *hdev = conn->hdev; struct hci_cp_setup_sync_conn cp; - BT_DBG("%p", conn); + BT_DBG("hcon %p", conn); conn->state = BT_CONNECT; conn->out = true; @@ -196,7 +196,7 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], struct hci_dev *hdev = conn->hdev; struct hci_cp_le_start_enc cp; - BT_DBG("%p", conn); + BT_DBG("hcon %p", conn); memset(&cp, 0, sizeof(cp)); @@ -213,11 +213,11 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status) { struct hci_conn *sco = conn->link; - BT_DBG("%p", conn); - if (!sco) return; + BT_DBG("hcon %p", conn); + if (!status) { if (lmp_esco_capable(conn->hdev)) hci_setup_sync(sco, conn->handle); @@ -235,7 +235,7 @@ static void hci_conn_timeout(struct work_struct *work) disc_work.work); __u8 reason; - BT_DBG("conn %p state %s", conn, state_to_string(conn->state)); + BT_DBG("hcon %p state %s", conn, state_to_string(conn->state)); if (atomic_read(&conn->refcnt)) return; @@ -266,7 +266,7 @@ static void hci_conn_enter_sniff_mode(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; - BT_DBG("conn %p mode %d", conn, conn->mode); + BT_DBG("hcon %p mode %d", conn, conn->mode); if (test_bit(HCI_RAW, &hdev->flags)) return; @@ -301,7 +301,7 @@ static void hci_conn_idle(unsigned long arg) { struct hci_conn *conn = (void *) arg; - BT_DBG("conn %p mode %d", conn, conn->mode); + BT_DBG("hcon %p mode %d", conn, conn->mode); hci_conn_enter_sniff_mode(conn); } @@ -382,7 +382,7 @@ int hci_conn_del(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; - BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle); + BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle); del_timer(&conn->idle_timer); @@ -557,7 +557,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, /* Check link security requirement */ int hci_conn_check_link_mode(struct hci_conn *conn) { - BT_DBG("conn %p", conn); + BT_DBG("hcon %p", conn); if (hci_conn_ssp_enabled(conn) && !(conn->link_mode & HCI_LM_ENCRYPT)) return 0; @@ -568,7 +568,7 @@ int hci_conn_check_link_mode(struct hci_conn *conn) /* Authenticate remote device */ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) { - BT_DBG("conn %p", conn); + BT_DBG("hcon %p", conn); if (conn->pending_sec_level > sec_level) sec_level = conn->pending_sec_level; @@ -602,7 +602,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) /* Encrypt the the link */ static void hci_conn_encrypt(struct hci_conn *conn) { - BT_DBG("conn %p", conn); + BT_DBG("hcon %p", conn); if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) { struct hci_cp_set_conn_encrypt cp; @@ -616,7 +616,7 @@ static void hci_conn_encrypt(struct hci_conn *conn) /* Enable security */ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) { - BT_DBG("conn %p", conn); + BT_DBG("hcon %p", conn); /* For sdp we don't need the link key. */ if (sec_level == BT_SECURITY_SDP) @@ -669,7 +669,7 @@ EXPORT_SYMBOL(hci_conn_security); /* Check secure link requirement */ int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level) { - BT_DBG("conn %p", conn); + BT_DBG("hcon %p", conn); if (sec_level != BT_SECURITY_HIGH) return 1; /* Accept if non-secure is required */ @@ -684,7 +684,7 @@ EXPORT_SYMBOL(hci_conn_check_secure); /* Change link key */ int hci_conn_change_link_key(struct hci_conn *conn) { - BT_DBG("conn %p", conn); + BT_DBG("hcon %p", conn); if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { struct hci_cp_change_conn_link_key cp; @@ -699,7 +699,7 @@ int hci_conn_change_link_key(struct hci_conn *conn) /* Switch role */ int hci_conn_switch_role(struct hci_conn *conn, __u8 role) { - BT_DBG("conn %p", conn); + BT_DBG("hcon %p", conn); if (!role && conn->link_mode & HCI_LM_MASTER) return 1; @@ -720,7 +720,7 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) { struct hci_dev *hdev = conn->hdev; - BT_DBG("conn %p mode %d", conn, conn->mode); + BT_DBG("hcon %p mode %d", conn, conn->mode); if (test_bit(HCI_RAW, &hdev->flags)) return; @@ -894,7 +894,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn) struct hci_dev *hdev = conn->hdev; struct hci_chan *chan; - BT_DBG("%s conn %p", hdev->name, conn); + BT_DBG("%s hcon %p", hdev->name, conn); chan = kzalloc(sizeof(struct hci_chan), GFP_KERNEL); if (!chan) @@ -913,7 +913,7 @@ int hci_chan_del(struct hci_chan *chan) struct hci_conn *conn = chan->conn; struct hci_dev *hdev = conn->hdev; - BT_DBG("%s conn %p chan %p", hdev->name, conn, chan); + BT_DBG("%s hcon %p chan %p", hdev->name, conn, chan); list_del_rcu(&chan->list); @@ -929,7 +929,7 @@ void hci_chan_list_flush(struct hci_conn *conn) { struct hci_chan *chan, *n; - BT_DBG("conn %p", conn); + BT_DBG("hcon %p", conn); list_for_each_entry_safe(chan, n, &conn->chan_list, list) hci_chan_del(chan); -- cgit v1.2.3 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') 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 From 17621e11fda095459e2f986c019f52686c7a4ffb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:25:38 +0200 Subject: ACPI / PM: Drop pm_message_t argument from device suspend callback None of the drivers implementing the ACPI device suspend callback uses the pm_message_t argument of it, so this argument may be dropped entirely from that callback. This will simplify switching the ACPI bus type to PM handling based on struct dev_pm_ops. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/fan.c | 4 ++-- drivers/acpi/processor_idle.c | 2 +- drivers/acpi/scan.c | 2 +- drivers/platform/x86/hp_accel.c | 2 +- drivers/platform/x86/sony-laptop.c | 2 +- drivers/platform/x86/toshiba_acpi.c | 3 +-- include/acpi/acpi_bus.h | 3 +-- include/acpi/processor.h | 2 +- 8 files changed, 9 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 0f0356ca1a9e..ed1e58dc19d8 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -46,7 +46,7 @@ MODULE_LICENSE("GPL"); static int acpi_fan_add(struct acpi_device *device); static int acpi_fan_remove(struct acpi_device *device, int type); -static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state); +static int acpi_fan_suspend(struct acpi_device *device); static int acpi_fan_resume(struct acpi_device *device); static const struct acpi_device_id fan_device_ids[] = { @@ -183,7 +183,7 @@ static int acpi_fan_remove(struct acpi_device *device, int type) return 0; } -static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state) +static int acpi_fan_suspend(struct acpi_device *device) { if (!device) return -EINVAL; diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 47a8caa89dbe..e28af8d38239 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -241,7 +241,7 @@ static void acpi_idle_bm_rld_restore(void) acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, saved_bm_rld); } -int acpi_processor_suspend(struct acpi_device * device, pm_message_t state) +int acpi_processor_suspend(struct acpi_device * device) { if (acpi_idle_suspend == 1) return 0; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index c8a1f3b68110..ec65ec9c529f 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -296,7 +296,7 @@ static int acpi_device_suspend(struct device *dev, pm_message_t state) struct acpi_driver *acpi_drv = acpi_dev->driver; if (acpi_drv && acpi_drv->ops.suspend) - return acpi_drv->ops.suspend(acpi_dev, state); + return acpi_drv->ops.suspend(acpi_dev); return 0; } diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 22b2dfa73148..c9e052033173 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -353,7 +353,7 @@ static int lis3lv02d_remove(struct acpi_device *device, int type) #ifdef CONFIG_PM -static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) +static int lis3lv02d_suspend(struct acpi_device *device) { /* make sure the device is off when we suspend */ lis3lv02d_poweroff(&lis3_dev); diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 210d4ae547c2..2b604f376fdb 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -4243,7 +4243,7 @@ err_free_resources: return result; } -static int sony_pic_suspend(struct acpi_device *device, pm_message_t state) +static int sony_pic_suspend(struct acpi_device *device) { if (sony_pic_disable(device)) return -ENXIO; diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index dab10f6edcd4..fd90b6da0a4b 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -1296,8 +1296,7 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) } } -static int toshiba_acpi_suspend(struct acpi_device *acpi_dev, - pm_message_t state) +static int toshiba_acpi_suspend(struct acpi_device *acpi_dev) { struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); u32 result; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 9e6e1c6eb60a..c2bbec76ba10 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -117,8 +117,7 @@ struct acpi_device; typedef int (*acpi_op_add) (struct acpi_device * device); typedef int (*acpi_op_remove) (struct acpi_device * device, int type); typedef int (*acpi_op_start) (struct acpi_device * device); -typedef int (*acpi_op_suspend) (struct acpi_device * device, - pm_message_t state); +typedef int (*acpi_op_suspend) (struct acpi_device * device); typedef int (*acpi_op_resume) (struct acpi_device * device); typedef int (*acpi_op_bind) (struct acpi_device * device); typedef int (*acpi_op_unbind) (struct acpi_device * device); diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 9d650476d5dc..8a1894a6eba2 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -334,7 +334,7 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr); int acpi_processor_hotplug(struct acpi_processor *pr); int acpi_processor_power_exit(struct acpi_processor *pr, struct acpi_device *device); -int acpi_processor_suspend(struct acpi_device * device, pm_message_t state); +int acpi_processor_suspend(struct acpi_device * device); int acpi_processor_resume(struct acpi_device * device); extern struct cpuidle_driver acpi_idle_driver; -- cgit v1.2.3 From e8110b64af8b7cce96d1878276770c76cb9c01d5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:26:26 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in processor driver Make the ACPI processor driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_driver.c | 6 ++++-- drivers/acpi/processor_idle.c | 4 ++-- include/acpi/processor.h | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 0734086537b8..13103aeb0c4f 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -93,6 +93,9 @@ static const struct acpi_device_id processor_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, processor_device_ids); +static SIMPLE_DEV_PM_OPS(acpi_processor_pm, + acpi_processor_suspend, acpi_processor_resume); + static struct acpi_driver acpi_processor_driver = { .name = "processor", .class = ACPI_PROCESSOR_CLASS, @@ -100,10 +103,9 @@ static struct acpi_driver acpi_processor_driver = { .ops = { .add = acpi_processor_add, .remove = acpi_processor_remove, - .suspend = acpi_processor_suspend, - .resume = acpi_processor_resume, .notify = acpi_processor_notify, }, + .drv.pm = &acpi_processor_pm, }; #define INSTALL_NOTIFY_HANDLER 1 diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index e28af8d38239..6d9ec3e0b52e 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -241,7 +241,7 @@ static void acpi_idle_bm_rld_restore(void) acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, saved_bm_rld); } -int acpi_processor_suspend(struct acpi_device * device) +int acpi_processor_suspend(struct device *dev) { if (acpi_idle_suspend == 1) return 0; @@ -251,7 +251,7 @@ int acpi_processor_suspend(struct acpi_device * device) return 0; } -int acpi_processor_resume(struct acpi_device * device) +int acpi_processor_resume(struct device *dev) { if (acpi_idle_suspend == 0) return 0; diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 8a1894a6eba2..ac3bff6fbbb0 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -334,8 +334,8 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr); int acpi_processor_hotplug(struct acpi_processor *pr); int acpi_processor_power_exit(struct acpi_processor *pr, struct acpi_device *device); -int acpi_processor_suspend(struct acpi_device * device); -int acpi_processor_resume(struct acpi_device * device); +int acpi_processor_suspend(struct device *dev); +int acpi_processor_resume(struct device *dev); extern struct cpuidle_driver acpi_idle_driver; /* in processor_thermal.c */ -- cgit v1.2.3 From a5cd33e1d4f3319e86f9f0fc667fa74ef9d9216c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 29 Jun 2012 23:40:22 +0200 Subject: ACPI / PM: Drop legacy driver PM callbacks that are not used any more Since the legacy ACPI driver PM callbacks included into struct acpi_device_ops are not used any more, drop them. Signed-off-by: Rafael J. Wysocki --- include/acpi/acpi_bus.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include') diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index c2bbec76ba10..18fd41033e03 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -117,8 +117,6 @@ struct acpi_device; typedef int (*acpi_op_add) (struct acpi_device * device); typedef int (*acpi_op_remove) (struct acpi_device * device, int type); typedef int (*acpi_op_start) (struct acpi_device * device); -typedef int (*acpi_op_suspend) (struct acpi_device * device); -typedef int (*acpi_op_resume) (struct acpi_device * device); typedef int (*acpi_op_bind) (struct acpi_device * device); typedef int (*acpi_op_unbind) (struct acpi_device * device); typedef void (*acpi_op_notify) (struct acpi_device * device, u32 event); @@ -132,8 +130,6 @@ struct acpi_device_ops { acpi_op_add add; acpi_op_remove remove; acpi_op_start start; - acpi_op_suspend suspend; - acpi_op_resume resume; acpi_op_bind bind; acpi_op_unbind unbind; acpi_op_notify notify; -- cgit v1.2.3 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') 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 From 48afb3112e6373a292e54d675e986a5da14c0516 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 26 May 2012 16:58:15 +0100 Subject: dmaengine: PL08x: remove circular_buffer boolean from channel data Circular buffers are not handled in this way; we have a separate API call now to setup circular buffers. So lets not mislead people with this bool. Acked-by: Linus Walleij Tested-by: Linus Walleij Signed-off-by: Russell King --- drivers/dma/amba-pl08x.c | 7 ------- include/linux/amba/pl08x.h | 4 ---- 2 files changed, 11 deletions(-) (limited to 'include') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 9c5bae6f85b5..5821125f0458 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1701,13 +1701,6 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, return -ENOMEM; } } - if (chan->cd->circular_buffer) { - dev_err(&pl08x->adev->dev, - "channel %s: circular buffers not supported\n", - chan->name); - kfree(chan); - continue; - } dev_dbg(&pl08x->adev->dev, "initialize virtual channel \"%s\"\n", chan->name); diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 02549017212a..0f5b34d668b6 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -51,9 +51,6 @@ enum { * can be the address of a FIFO register for burst requests for example. * This can be left undefined if the PrimeCell API is used for configuring * this. - * @circular_buffer: whether the buffer passed in is circular and - * shall simply be looped round round (like a record baby round - * round round round) * @single: the device connected to this channel will request single DMA * transfers, not bursts. (Bursts are default.) * @periph_buses: the device connected to this channel is accessible via @@ -66,7 +63,6 @@ struct pl08x_channel_data { u32 muxval; u32 cctl; dma_addr_t addr; - bool circular_buffer; bool single; u8 periph_buses; }; -- cgit v1.2.3 From aeea1808fe752e917b966961bde3e9603f206dec Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 17 May 2012 15:01:28 +0100 Subject: dmaengine: PL08x: clean up get_signal/put_signal Try to avoid dereferencing the DMA engine's channel struct in these platform helpers; instead, pass a pointer to the channel data into get_signal(), and the returned signal number to put_signal(). Tested-by: Linus Walleij Signed-off-by: Russell King --- drivers/dma/amba-pl08x.c | 4 ++-- include/linux/amba/pl08x.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 5821125f0458..cc08c8c01a9e 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -874,7 +874,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan, * Can the platform allow us to use this channel? */ if (plchan->slave && pl08x->pd->get_signal) { - ret = pl08x->pd->get_signal(plchan); + ret = pl08x->pd->get_signal(plchan->cd); if (ret < 0) { dev_dbg(&pl08x->adev->dev, "unable to use physical channel %d for transfer on %s due to platform restrictions\n", @@ -909,7 +909,7 @@ static void release_phy_channel(struct pl08x_dma_chan *plchan) struct pl08x_driver_data *pl08x = plchan->host; if ((plchan->phychan->signal >= 0) && pl08x->pd->put_signal) { - pl08x->pd->put_signal(plchan); + pl08x->pd->put_signal(plchan->cd, plchan->phychan->signal); plchan->phychan->signal = -1; } pl08x_put_phy_channel(pl08x, plchan->phychan); diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 0f5b34d668b6..88765a62c8f2 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -225,8 +225,8 @@ struct pl08x_platform_data { const struct pl08x_channel_data *slave_channels; unsigned int num_slave_channels; struct pl08x_channel_data memcpy_channel; - int (*get_signal)(struct pl08x_dma_chan *); - void (*put_signal)(struct pl08x_dma_chan *); + int (*get_signal)(const struct pl08x_channel_data *); + void (*put_signal)(const struct pl08x_channel_data *, int); u8 lli_buses; u8 mem_buses; }; -- cgit v1.2.3 From b23f204c8dbbed8e501442c47d7639aac21a3d84 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 16 May 2012 10:48:44 +0100 Subject: dmaengine: PL08x: move private data structures into amba-pl08x.c Move the driver private data structures into the driver itself, rather than having them exposed to everyone in a header file. Acked-by: Linus Walleij Tested-by: Linus Walleij Signed-off-by: Russell King --- drivers/dma/amba-pl08x.c | 136 +++++++++++++++++++++++++++++++++++++++++++ include/linux/amba/pl08x.h | 141 +-------------------------------------------- 2 files changed, 138 insertions(+), 139 deletions(-) (limited to 'include') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index cc08c8c01a9e..94949909e4e9 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -90,6 +90,7 @@ #define DRIVER_NAME "pl08xdmac" static struct amba_driver pl08x_amba_driver; +struct pl08x_driver_data; /** * struct vendor_data - vendor-specific config parameters for PL08x derivatives @@ -118,6 +119,141 @@ struct pl08x_lli { u32 cctl; }; +/** + * struct pl08x_bus_data - information of source or destination + * busses for a transfer + * @addr: current address + * @maxwidth: the maximum width of a transfer on this bus + * @buswidth: the width of this bus in bytes: 1, 2 or 4 + */ +struct pl08x_bus_data { + dma_addr_t addr; + u8 maxwidth; + u8 buswidth; +}; + +/** + * struct pl08x_phy_chan - holder for the physical channels + * @id: physical index to this channel + * @lock: a lock to use when altering an instance of this struct + * @signal: the physical signal (aka channel) serving this physical channel + * right now + * @serving: the virtual channel currently being served by this physical + * channel + */ +struct pl08x_phy_chan { + unsigned int id; + void __iomem *base; + spinlock_t lock; + int signal; + struct pl08x_dma_chan *serving; +}; + +/** + * struct pl08x_sg - structure containing data per sg + * @src_addr: src address of sg + * @dst_addr: dst address of sg + * @len: transfer len in bytes + * @node: node for txd's dsg_list + */ +struct pl08x_sg { + dma_addr_t src_addr; + dma_addr_t dst_addr; + size_t len; + struct list_head node; +}; + +/** + * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor + * @tx: async tx descriptor + * @node: node for txd list for channels + * @dsg_list: list of children sg's + * @direction: direction of transfer + * @llis_bus: DMA memory address (physical) start for the LLIs + * @llis_va: virtual memory address start for the LLIs + * @cctl: control reg values for current txd + * @ccfg: config reg values for current txd + */ +struct pl08x_txd { + struct dma_async_tx_descriptor tx; + struct list_head node; + struct list_head dsg_list; + enum dma_transfer_direction direction; + dma_addr_t llis_bus; + struct pl08x_lli *llis_va; + /* Default cctl value for LLIs */ + u32 cctl; + /* + * Settings to be put into the physical channel when we + * trigger this txd. Other registers are in llis_va[0]. + */ + u32 ccfg; +}; + +/** + * struct pl08x_dma_chan_state - holds the PL08x specific virtual channel + * states + * @PL08X_CHAN_IDLE: the channel is idle + * @PL08X_CHAN_RUNNING: the channel has allocated a physical transport + * channel and is running a transfer on it + * @PL08X_CHAN_PAUSED: the channel has allocated a physical transport + * channel, but the transfer is currently paused + * @PL08X_CHAN_WAITING: the channel is waiting for a physical transport + * channel to become available (only pertains to memcpy channels) + */ +enum pl08x_dma_chan_state { + PL08X_CHAN_IDLE, + PL08X_CHAN_RUNNING, + PL08X_CHAN_PAUSED, + PL08X_CHAN_WAITING, +}; + +/** + * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel + * @chan: wrappped abstract channel + * @phychan: the physical channel utilized by this channel, if there is one + * @phychan_hold: if non-zero, hold on to the physical channel even if we + * have no pending entries + * @tasklet: tasklet scheduled by the IRQ to handle actual work etc + * @name: name of channel + * @cd: channel platform data + * @runtime_addr: address for RX/TX according to the runtime config + * @runtime_direction: current direction of this channel according to + * runtime config + * @pend_list: queued transactions pending on this channel + * @at: active transaction on this channel + * @lock: a lock for this channel data + * @host: a pointer to the host (internal use) + * @state: whether the channel is idle, paused, running etc + * @slave: whether this channel is a device (slave) or for memcpy + * @device_fc: Flow Controller Settings for ccfg register. Only valid for slave + * channels. Fill with 'true' if peripheral should be flow controller. Direction + * will be selected at Runtime. + * @waiting: a TX descriptor on this channel which is waiting for a physical + * channel to become available + */ +struct pl08x_dma_chan { + struct dma_chan chan; + struct pl08x_phy_chan *phychan; + int phychan_hold; + struct tasklet_struct tasklet; + char *name; + const struct pl08x_channel_data *cd; + dma_addr_t src_addr; + dma_addr_t dst_addr; + u32 src_cctl; + u32 dst_cctl; + enum dma_transfer_direction runtime_direction; + struct list_head pend_list; + struct pl08x_txd *at; + spinlock_t lock; + struct pl08x_driver_data *host; + enum pl08x_dma_chan_state state; + bool slave; + bool device_fc; + struct pl08x_txd *waiting; +}; + /** * struct pl08x_driver_data - the local state holder for the PL08x * @slave: slave engine for this instance diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 88765a62c8f2..48d02bf66ec9 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -21,8 +21,9 @@ #include #include -struct pl08x_lli; struct pl08x_driver_data; +struct pl08x_phy_chan; +struct pl08x_txd; /* Bitmasks for selecting AHB ports for DMA transfers */ enum { @@ -67,144 +68,6 @@ struct pl08x_channel_data { u8 periph_buses; }; -/** - * Struct pl08x_bus_data - information of source or destination - * busses for a transfer - * @addr: current address - * @maxwidth: the maximum width of a transfer on this bus - * @buswidth: the width of this bus in bytes: 1, 2 or 4 - */ -struct pl08x_bus_data { - dma_addr_t addr; - u8 maxwidth; - u8 buswidth; -}; - -/** - * struct pl08x_phy_chan - holder for the physical channels - * @id: physical index to this channel - * @lock: a lock to use when altering an instance of this struct - * @signal: the physical signal (aka channel) serving this physical channel - * right now - * @serving: the virtual channel currently being served by this physical - * channel - * @locked: channel unavailable for the system, e.g. dedicated to secure - * world - */ -struct pl08x_phy_chan { - unsigned int id; - void __iomem *base; - spinlock_t lock; - int signal; - struct pl08x_dma_chan *serving; - bool locked; -}; - -/** - * struct pl08x_sg - structure containing data per sg - * @src_addr: src address of sg - * @dst_addr: dst address of sg - * @len: transfer len in bytes - * @node: node for txd's dsg_list - */ -struct pl08x_sg { - dma_addr_t src_addr; - dma_addr_t dst_addr; - size_t len; - struct list_head node; -}; - -/** - * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor - * @tx: async tx descriptor - * @node: node for txd list for channels - * @dsg_list: list of children sg's - * @direction: direction of transfer - * @llis_bus: DMA memory address (physical) start for the LLIs - * @llis_va: virtual memory address start for the LLIs - * @cctl: control reg values for current txd - * @ccfg: config reg values for current txd - */ -struct pl08x_txd { - struct dma_async_tx_descriptor tx; - struct list_head node; - struct list_head dsg_list; - enum dma_transfer_direction direction; - dma_addr_t llis_bus; - struct pl08x_lli *llis_va; - /* Default cctl value for LLIs */ - u32 cctl; - /* - * Settings to be put into the physical channel when we - * trigger this txd. Other registers are in llis_va[0]. - */ - u32 ccfg; -}; - -/** - * struct pl08x_dma_chan_state - holds the PL08x specific virtual channel - * states - * @PL08X_CHAN_IDLE: the channel is idle - * @PL08X_CHAN_RUNNING: the channel has allocated a physical transport - * channel and is running a transfer on it - * @PL08X_CHAN_PAUSED: the channel has allocated a physical transport - * channel, but the transfer is currently paused - * @PL08X_CHAN_WAITING: the channel is waiting for a physical transport - * channel to become available (only pertains to memcpy channels) - */ -enum pl08x_dma_chan_state { - PL08X_CHAN_IDLE, - PL08X_CHAN_RUNNING, - PL08X_CHAN_PAUSED, - PL08X_CHAN_WAITING, -}; - -/** - * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel - * @chan: wrappped abstract channel - * @phychan: the physical channel utilized by this channel, if there is one - * @phychan_hold: if non-zero, hold on to the physical channel even if we - * have no pending entries - * @tasklet: tasklet scheduled by the IRQ to handle actual work etc - * @name: name of channel - * @cd: channel platform data - * @runtime_addr: address for RX/TX according to the runtime config - * @runtime_direction: current direction of this channel according to - * runtime config - * @pend_list: queued transactions pending on this channel - * @at: active transaction on this channel - * @lock: a lock for this channel data - * @host: a pointer to the host (internal use) - * @state: whether the channel is idle, paused, running etc - * @slave: whether this channel is a device (slave) or for memcpy - * @device_fc: Flow Controller Settings for ccfg register. Only valid for slave - * channels. Fill with 'true' if peripheral should be flow controller. Direction - * will be selected at Runtime. - * @waiting: a TX descriptor on this channel which is waiting for a physical - * channel to become available - */ -struct pl08x_dma_chan { - struct dma_chan chan; - struct pl08x_phy_chan *phychan; - int phychan_hold; - struct tasklet_struct tasklet; - char *name; - const struct pl08x_channel_data *cd; - dma_addr_t src_addr; - dma_addr_t dst_addr; - u32 src_cctl; - u32 dst_cctl; - enum dma_transfer_direction runtime_direction; - struct list_head pend_list; - struct pl08x_txd *at; - spinlock_t lock; - struct pl08x_driver_data *host; - enum pl08x_dma_chan_state state; - bool slave; - bool device_fc; - struct pl08x_txd *waiting; -}; - /** * struct pl08x_platform_data - the platform configuration for the PL08x * PrimeCells. -- cgit v1.2.3 From 550ec36f507177470a394c4dfffcaf986ca25818 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 28 May 2012 10:18:55 +0100 Subject: dmaengine: PL08x: constify channel names and bus_id strings Acked-by: Linus Walleij Tested-by: Linus Walleij Signed-off-by: Russell King --- drivers/dma/amba-pl08x.c | 2 +- include/linux/amba/pl08x.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 94949909e4e9..775efef0349c 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -237,7 +237,7 @@ struct pl08x_dma_chan { struct pl08x_phy_chan *phychan; int phychan_hold; struct tasklet_struct tasklet; - char *name; + const char *name; const struct pl08x_channel_data *cd; dma_addr_t src_addr; dma_addr_t dst_addr; diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 48d02bf66ec9..158ce2634b01 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -58,7 +58,7 @@ enum { * these buses (use PL08X_AHB1 | PL08X_AHB2). */ struct pl08x_channel_data { - char *bus_id; + const char *bus_id; int min_signal; int max_signal; u32 muxval; -- cgit v1.2.3 From dc8d5f8de12146c8732d926a30e5f064d76061e0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 16 May 2012 12:20:55 +0100 Subject: dmaengine: PL08x: get rid of unnecessary checks in dma_slave_config Get rid of the unnecessary checks in dma_slave_config utilizing the DMA direction. This allows us to move the computation of cctl to the prepare function. Acked-by: Linus Walleij Tested-by: Linus Walleij Signed-off-by: Russell King --- arch/arm/mach-spear3xx/spear300.c | 26 -------------------- arch/arm/mach-spear3xx/spear310.c | 26 -------------------- arch/arm/mach-spear3xx/spear320.c | 26 -------------------- arch/arm/mach-spear3xx/spear3xx.c | 3 ++- arch/arm/mach-spear6xx/spear6xx.c | 51 ++------------------------------------- drivers/dma/amba-pl08x.c | 41 ++++++++++--------------------- include/linux/amba/pl08x.h | 5 ++-- 7 files changed, 20 insertions(+), 158 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-spear3xx/spear300.c b/arch/arm/mach-spear3xx/spear300.c index 0f882ecb7d81..6ec300549960 100644 --- a/arch/arm/mach-spear3xx/spear300.c +++ b/arch/arm/mach-spear3xx/spear300.c @@ -120,182 +120,156 @@ struct pl08x_channel_data spear300_dma_info[] = { .min_signal = 2, .max_signal = 2, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart0_tx", .min_signal = 3, .max_signal = 3, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_rx", .min_signal = 8, .max_signal = 8, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_tx", .min_signal = 9, .max_signal = 9, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_rx", .min_signal = 10, .max_signal = 10, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_tx", .min_signal = 11, .max_signal = 11, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "irda", .min_signal = 12, .max_signal = 12, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "adc", .min_signal = 13, .max_signal = 13, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "to_jpeg", .min_signal = 14, .max_signal = 14, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "from_jpeg", .min_signal = 15, .max_signal = 15, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras0_rx", .min_signal = 0, .max_signal = 0, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras0_tx", .min_signal = 1, .max_signal = 1, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras1_rx", .min_signal = 2, .max_signal = 2, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras1_tx", .min_signal = 3, .max_signal = 3, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras2_rx", .min_signal = 4, .max_signal = 4, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras2_tx", .min_signal = 5, .max_signal = 5, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras3_rx", .min_signal = 6, .max_signal = 6, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras3_tx", .min_signal = 7, .max_signal = 7, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras4_rx", .min_signal = 8, .max_signal = 8, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras4_tx", .min_signal = 9, .max_signal = 9, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_rx", .min_signal = 10, .max_signal = 10, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_tx", .min_signal = 11, .max_signal = 11, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_rx", .min_signal = 12, .max_signal = 12, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_tx", .min_signal = 13, .max_signal = 13, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_rx", .min_signal = 14, .max_signal = 14, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_tx", .min_signal = 15, .max_signal = 15, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, }; diff --git a/arch/arm/mach-spear3xx/spear310.c b/arch/arm/mach-spear3xx/spear310.c index bbcf4571d361..1d0e435b9045 100644 --- a/arch/arm/mach-spear3xx/spear310.c +++ b/arch/arm/mach-spear3xx/spear310.c @@ -205,182 +205,156 @@ struct pl08x_channel_data spear310_dma_info[] = { .min_signal = 2, .max_signal = 2, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart0_tx", .min_signal = 3, .max_signal = 3, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_rx", .min_signal = 8, .max_signal = 8, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_tx", .min_signal = 9, .max_signal = 9, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_rx", .min_signal = 10, .max_signal = 10, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_tx", .min_signal = 11, .max_signal = 11, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "irda", .min_signal = 12, .max_signal = 12, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "adc", .min_signal = 13, .max_signal = 13, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "to_jpeg", .min_signal = 14, .max_signal = 14, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "from_jpeg", .min_signal = 15, .max_signal = 15, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart1_rx", .min_signal = 0, .max_signal = 0, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart1_tx", .min_signal = 1, .max_signal = 1, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart2_rx", .min_signal = 2, .max_signal = 2, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart2_tx", .min_signal = 3, .max_signal = 3, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart3_rx", .min_signal = 4, .max_signal = 4, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart3_tx", .min_signal = 5, .max_signal = 5, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart4_rx", .min_signal = 6, .max_signal = 6, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart4_tx", .min_signal = 7, .max_signal = 7, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart5_rx", .min_signal = 8, .max_signal = 8, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart5_tx", .min_signal = 9, .max_signal = 9, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_rx", .min_signal = 10, .max_signal = 10, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_tx", .min_signal = 11, .max_signal = 11, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_rx", .min_signal = 12, .max_signal = 12, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_tx", .min_signal = 13, .max_signal = 13, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_rx", .min_signal = 14, .max_signal = 14, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_tx", .min_signal = 15, .max_signal = 15, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, }; diff --git a/arch/arm/mach-spear3xx/spear320.c b/arch/arm/mach-spear3xx/spear320.c index 88d483bcd66a..fd823c624575 100644 --- a/arch/arm/mach-spear3xx/spear320.c +++ b/arch/arm/mach-spear3xx/spear320.c @@ -213,182 +213,156 @@ struct pl08x_channel_data spear320_dma_info[] = { .min_signal = 2, .max_signal = 2, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart0_tx", .min_signal = 3, .max_signal = 3, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_rx", .min_signal = 8, .max_signal = 8, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_tx", .min_signal = 9, .max_signal = 9, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c0_rx", .min_signal = 10, .max_signal = 10, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c0_tx", .min_signal = 11, .max_signal = 11, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "irda", .min_signal = 12, .max_signal = 12, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "adc", .min_signal = 13, .max_signal = 13, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "to_jpeg", .min_signal = 14, .max_signal = 14, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "from_jpeg", .min_signal = 15, .max_signal = 15, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp1_rx", .min_signal = 0, .max_signal = 0, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ssp1_tx", .min_signal = 1, .max_signal = 1, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ssp2_rx", .min_signal = 2, .max_signal = 2, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ssp2_tx", .min_signal = 3, .max_signal = 3, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "uart1_rx", .min_signal = 4, .max_signal = 4, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "uart1_tx", .min_signal = 5, .max_signal = 5, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "uart2_rx", .min_signal = 6, .max_signal = 6, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "uart2_tx", .min_signal = 7, .max_signal = 7, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2c1_rx", .min_signal = 8, .max_signal = 8, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2c1_tx", .min_signal = 9, .max_signal = 9, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2c2_rx", .min_signal = 10, .max_signal = 10, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2c2_tx", .min_signal = 11, .max_signal = 11, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2s_rx", .min_signal = 12, .max_signal = 12, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "i2s_tx", .min_signal = 13, .max_signal = 13, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "rs485_rx", .min_signal = 14, .max_signal = 14, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "rs485_tx", .min_signal = 15, .max_signal = 15, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB2, }, }; diff --git a/arch/arm/mach-spear3xx/spear3xx.c b/arch/arm/mach-spear3xx/spear3xx.c index 0f41bd1c47c3..d6cd8403fe66 100644 --- a/arch/arm/mach-spear3xx/spear3xx.c +++ b/arch/arm/mach-spear3xx/spear3xx.c @@ -46,7 +46,8 @@ struct pl022_ssp_controller pl022_plat_data = { struct pl08x_platform_data pl080_plat_data = { .memcpy_channel = { .bus_id = "memcpy", - .cctl = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \ + .cctl_memcpy = + (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \ PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT | \ PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT | \ PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT | \ diff --git a/arch/arm/mach-spear6xx/spear6xx.c b/arch/arm/mach-spear6xx/spear6xx.c index 2e2e3596583e..b59ae5369e7b 100644 --- a/arch/arm/mach-spear6xx/spear6xx.c +++ b/arch/arm/mach-spear6xx/spear6xx.c @@ -36,336 +36,288 @@ static struct pl08x_channel_data spear600_dma_info[] = { .min_signal = 0, .max_signal = 0, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp1_tx", .min_signal = 1, .max_signal = 1, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart0_rx", .min_signal = 2, .max_signal = 2, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart0_tx", .min_signal = 3, .max_signal = 3, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart1_rx", .min_signal = 4, .max_signal = 4, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "uart1_tx", .min_signal = 5, .max_signal = 5, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp2_rx", .min_signal = 6, .max_signal = 6, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ssp2_tx", .min_signal = 7, .max_signal = 7, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ssp0_rx", .min_signal = 8, .max_signal = 8, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ssp0_tx", .min_signal = 9, .max_signal = 9, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_rx", .min_signal = 10, .max_signal = 10, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "i2c_tx", .min_signal = 11, .max_signal = 11, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "irda", .min_signal = 12, .max_signal = 12, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "adc", .min_signal = 13, .max_signal = 13, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "to_jpeg", .min_signal = 14, .max_signal = 14, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "from_jpeg", .min_signal = 15, .max_signal = 15, .muxval = 0, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras0_rx", .min_signal = 0, .max_signal = 0, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras0_tx", .min_signal = 1, .max_signal = 1, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras1_rx", .min_signal = 2, .max_signal = 2, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras1_tx", .min_signal = 3, .max_signal = 3, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras2_rx", .min_signal = 4, .max_signal = 4, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras2_tx", .min_signal = 5, .max_signal = 5, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras3_rx", .min_signal = 6, .max_signal = 6, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras3_tx", .min_signal = 7, .max_signal = 7, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras4_rx", .min_signal = 8, .max_signal = 8, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras4_tx", .min_signal = 9, .max_signal = 9, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_rx", .min_signal = 10, .max_signal = 10, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras5_tx", .min_signal = 11, .max_signal = 11, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_rx", .min_signal = 12, .max_signal = 12, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras6_tx", .min_signal = 13, .max_signal = 13, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_rx", .min_signal = 14, .max_signal = 14, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ras7_tx", .min_signal = 15, .max_signal = 15, .muxval = 1, - .cctl = 0, .periph_buses = PL08X_AHB1, }, { .bus_id = "ext0_rx", .min_signal = 0, .max_signal = 0, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext0_tx", .min_signal = 1, .max_signal = 1, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext1_rx", .min_signal = 2, .max_signal = 2, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext1_tx", .min_signal = 3, .max_signal = 3, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext2_rx", .min_signal = 4, .max_signal = 4, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext2_tx", .min_signal = 5, .max_signal = 5, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext3_rx", .min_signal = 6, .max_signal = 6, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext3_tx", .min_signal = 7, .max_signal = 7, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext4_rx", .min_signal = 8, .max_signal = 8, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext4_tx", .min_signal = 9, .max_signal = 9, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext5_rx", .min_signal = 10, .max_signal = 10, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext5_tx", .min_signal = 11, .max_signal = 11, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext6_rx", .min_signal = 12, .max_signal = 12, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext6_tx", .min_signal = 13, .max_signal = 13, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext7_rx", .min_signal = 14, .max_signal = 14, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, { .bus_id = "ext7_tx", .min_signal = 15, .max_signal = 15, .muxval = 2, - .cctl = 0, .periph_buses = PL08X_AHB2, }, }; @@ -373,7 +325,8 @@ static struct pl08x_channel_data spear600_dma_info[] = { struct pl08x_platform_data pl080_plat_data = { .memcpy_channel = { .bus_id = "memcpy", - .cctl = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \ + .cctl_memcpy = + (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \ PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT | \ PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT | \ PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT | \ diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 50b9a839e9c3..f7397789e4e8 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -235,8 +235,6 @@ struct pl08x_dma_chan { const char *name; const struct pl08x_channel_data *cd; struct dma_slave_config cfg; - u32 src_cctl; - u32 dst_cctl; struct list_head pend_list; struct pl08x_txd *at; spinlock_t lock; @@ -1235,30 +1233,15 @@ static int dma_set_runtime_config(struct dma_chan *chan, struct dma_slave_config *config) { struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); - struct pl08x_driver_data *pl08x = plchan->host; - u32 src_cctl, dst_cctl; if (!plchan->slave) return -EINVAL; - dst_cctl = pl08x_get_cctl(plchan, config->dst_addr_width, - config->dst_maxburst); - if (dst_cctl == ~0 && config->direction == DMA_MEM_TO_DEV) { - dev_err(&pl08x->adev->dev, - "bad runtime_config: alien address width (M2D)\n"); + /* Reject definitely invalid configurations */ + if (config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || + config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) return -EINVAL; - } - src_cctl = pl08x_get_cctl(plchan, config->src_addr_width, - config->src_maxburst); - if (src_cctl == ~0 && config->direction == DMA_DEV_TO_MEM) { - dev_err(&pl08x->adev->dev, - "bad runtime_config: alien address width (D2M)\n"); - return -EINVAL; - } - - plchan->dst_cctl = dst_cctl; - plchan->src_cctl = src_cctl; plchan->cfg = *config; return 0; @@ -1407,7 +1390,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy( /* Set platform data for m2m */ txd->ccfg |= PL080_FLOW_MEM2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT; - txd->cctl = pl08x->pd->memcpy_channel.cctl & + txd->cctl = pl08x->pd->memcpy_channel.cctl_memcpy & ~(PL080_CONTROL_DST_AHB2 | PL080_CONTROL_SRC_AHB2); /* Both to be incremented or the code will break */ @@ -1434,10 +1417,11 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( struct pl08x_txd *txd; struct pl08x_sg *dsg; struct scatterlist *sg; + enum dma_slave_buswidth addr_width; dma_addr_t slave_addr; int ret, tmp; u8 src_buses, dst_buses; - u32 cctl; + u32 maxburst, cctl; dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n", __func__, sg_dma_len(sgl), plchan->name); @@ -1456,13 +1440,17 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( txd->direction = direction; if (direction == DMA_MEM_TO_DEV) { - cctl = plchan->dst_cctl | PL080_CONTROL_SRC_INCR; + cctl = PL080_CONTROL_SRC_INCR; slave_addr = plchan->cfg.dst_addr; + addr_width = plchan->cfg.dst_addr_width; + maxburst = plchan->cfg.dst_maxburst; src_buses = pl08x->mem_buses; dst_buses = plchan->cd->periph_buses; } else if (direction == DMA_DEV_TO_MEM) { - cctl = plchan->src_cctl | PL080_CONTROL_DST_INCR; + cctl = PL080_CONTROL_DST_INCR; slave_addr = plchan->cfg.src_addr; + addr_width = plchan->cfg.src_addr_width; + maxburst = plchan->cfg.src_maxburst; src_buses = plchan->cd->periph_buses; dst_buses = pl08x->mem_buses; } else { @@ -1472,6 +1460,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( return NULL; } + cctl |= pl08x_get_cctl(plchan, addr_width, maxburst); if (cctl == ~0) { pl08x_free_txd(pl08x, txd); dev_err(&pl08x->adev->dev, @@ -1774,14 +1763,10 @@ static irqreturn_t pl08x_irq(int irq, void *dev) static void pl08x_dma_slave_init(struct pl08x_dma_chan *chan) { - u32 cctl = pl08x_cctl(chan->cd->cctl); - chan->slave = true; chan->name = chan->cd->bus_id; chan->cfg.src_addr = chan->cd->addr; chan->cfg.dst_addr = chan->cd->addr; - chan->src_cctl = cctl; - chan->dst_cctl = cctl; } /* diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 158ce2634b01..2a5f64a11b77 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -47,7 +47,8 @@ enum { * devices with static assignments * @muxval: a number usually used to poke into some mux regiser to * mux in the signal to this channel - * @cctl_opt: default options for the channel control register + * @cctl_memcpy: options for the channel control register for memcpy + * *** not used for slave channels *** * @addr: source/target address in physical memory for this DMA channel, * can be the address of a FIFO register for burst requests for example. * This can be left undefined if the PrimeCell API is used for configuring @@ -62,7 +63,7 @@ struct pl08x_channel_data { int min_signal; int max_signal; u32 muxval; - u32 cctl; + u32 cctl_memcpy; dma_addr_t addr; bool single; u8 periph_buses; -- cgit v1.2.3 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') 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 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') 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 From a618e89f1e6fb3cdfc8ef0ad54a0d57830bf8881 Mon Sep 17 00:00:00 2001 From: Glauber Costa Date: Thu, 14 Jun 2012 16:17:21 +0400 Subject: slab: rename gfpflags to allocflags A consistent name with slub saves us an acessor function. In both caches, this field represents the same thing. We would like to use it from the mem_cgroup code. Signed-off-by: Glauber Costa Acked-by: Christoph Lameter CC: Pekka Enberg Signed-off-by: Pekka Enberg --- include/linux/slab_def.h | 2 +- mm/slab.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h index 1d93f27d81de..0c634fa376c9 100644 --- a/include/linux/slab_def.h +++ b/include/linux/slab_def.h @@ -39,7 +39,7 @@ struct kmem_cache { unsigned int gfporder; /* force GFP flags, e.g. GFP_DMA */ - gfp_t gfpflags; + gfp_t allocflags; size_t colour; /* cache colouring range */ unsigned int colour_off; /* colour offset */ diff --git a/mm/slab.c b/mm/slab.c index dd607a8e6706..bb7965253159 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1771,7 +1771,7 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid) flags |= __GFP_COMP; #endif - flags |= cachep->gfpflags; + flags |= cachep->allocflags; if (cachep->flags & SLAB_RECLAIM_ACCOUNT) flags |= __GFP_RECLAIMABLE; @@ -2482,9 +2482,9 @@ kmem_cache_create (const char *name, size_t size, size_t align, cachep->colour = left_over / cachep->colour_off; cachep->slab_size = slab_size; cachep->flags = flags; - cachep->gfpflags = 0; + cachep->allocflags = 0; if (CONFIG_ZONE_DMA_FLAG && (flags & SLAB_CACHE_DMA)) - cachep->gfpflags |= GFP_DMA; + cachep->allocflags |= GFP_DMA; cachep->size = size; cachep->reciprocal_buffer_size = reciprocal_value(size); @@ -2831,9 +2831,9 @@ static void kmem_flagcheck(struct kmem_cache *cachep, gfp_t flags) { if (CONFIG_ZONE_DMA_FLAG) { if (flags & GFP_DMA) - BUG_ON(!(cachep->gfpflags & GFP_DMA)); + BUG_ON(!(cachep->allocflags & GFP_DMA)); else - BUG_ON(cachep->gfpflags & GFP_DMA); + BUG_ON(cachep->allocflags & GFP_DMA); } } -- cgit v1.2.3 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') 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 From 5b063b87deba33ed1676db9d16c52ede662132d8 Mon Sep 17 00:00:00 2001 From: Alexandre Pereira da Silva Date: Thu, 14 Jun 2012 10:10:32 -0300 Subject: spi/pl022: cleanup pl022 header documentation Remove the unused fields from struct ssp_config_chip that was removed before Add bus_id documentation to pl022_ssp_controller Signed-off-by: Alexandre Pereira da Silva Signed-off-by: Roland Stigge --- include/linux/amba/pl022.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/amba/pl022.h b/include/linux/amba/pl022.h index 76dd1b199a1b..fe1d7b283cb6 100644 --- a/include/linux/amba/pl022.h +++ b/include/linux/amba/pl022.h @@ -231,6 +231,7 @@ enum ssp_chip_select { struct dma_chan; /** * struct pl022_ssp_master - device.platform_data for SPI controller devices. + * @bus_id: identifier for this bus * @num_chipselect: chipselects are used to distinguish individual * SPI slaves, and are numbered from zero to num_chipselects - 1. * each slave has a chipselect signal, but it's common that not @@ -259,19 +260,13 @@ struct pl022_ssp_controller { * struct ssp_config_chip - spi_board_info.controller_data for SPI * slave devices, copied to spi_device.controller_data. * - * @lbm: used for test purpose to internally connect RX and TX * @iface: Interface type(Motorola, TI, Microwire, Universal) * @hierarchy: sets whether interface is master or slave * @slave_tx_disable: SSPTXD is disconnected (in slave mode only) * @clk_freq: Tune freq parameters of SSP(when in master mode) - * @endian_rx: Endianess of Data in Rx FIFO - * @endian_tx: Endianess of Data in Tx FIFO - * @data_size: Width of data element(4 to 32 bits) * @com_mode: communication mode: polling, Interrupt or DMA * @rx_lev_trig: Rx FIFO watermark level (for IT & DMA mode) * @tx_lev_trig: Tx FIFO watermark level (for IT & DMA mode) - * @clk_phase: Motorola SPI interface Clock phase - * @clk_pol: Motorola SPI interface Clock polarity * @ctrl_len: Microwire interface: Control length * @wait_state: Microwire interface: Wait state * @duplex: Microwire interface: Full/Half duplex @@ -279,8 +274,6 @@ struct pl022_ssp_controller { * before sampling the incoming line * @cs_control: function pointer to board-specific function to * assert/deassert I/O port to control HW generation of devices chip-select. - * @dma_xfer_type: Type of DMA xfer (Mem-to-periph or Periph-to-Periph) - * @dma_config: DMA configuration for SSP controller and peripheral */ struct pl022_config_chip { enum ssp_interface iface; -- cgit v1.2.3 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') 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 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') 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 From cb831b537d50d21f6afb5dffbde4cf6523627461 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 2 Jul 2012 15:40:18 +0200 Subject: mac80211: remove tx_frags driver callback The implementation of tx_frags is buggy due to not handling queue stop, and there's no driver implementing it so remove it. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 15 --------------- net/mac80211/driver-ops.h | 8 -------- net/mac80211/main.c | 2 +- net/mac80211/tx.c | 7 ++----- 4 files changed, 3 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index b5da094468f1..dc2a97af95e7 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1896,19 +1896,6 @@ enum ieee80211_rate_control_changed { * The low-level driver should send the frame out based on * configuration in the TX control data. This handler should, * preferably, never fail and stop queues appropriately. - * This must be implemented if @tx_frags is not. - * Must be atomic. - * - * @tx_frags: Called to transmit multiple fragments of a single MSDU. - * This handler must consume all fragments, sending out some of - * them only is useless and it can't ask for some of them to be - * queued again. If the frame is not fragmented the queue has a - * single SKB only. To avoid issues with the networking stack - * when TX status is reported the frames should be removed from - * the skb queue. - * If this is used, the tx_info @vif and @sta pointers will be - * invalid -- you must not use them in that case. - * This must be implemented if @tx isn't. * Must be atomic. * * @start: Called before the first netdevice attached to the hardware @@ -2260,8 +2247,6 @@ enum ieee80211_rate_control_changed { */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); - void (*tx_frags)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, struct sk_buff_head *skbs); int (*start)(struct ieee80211_hw *hw); void (*stop)(struct ieee80211_hw *hw); #ifdef CONFIG_PM diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 44e8c1242781..5042151a3325 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -27,14 +27,6 @@ static inline void drv_tx(struct ieee80211_local *local, struct sk_buff *skb) local->ops->tx(&local->hw, skb); } -static inline void drv_tx_frags(struct ieee80211_local *local, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct sk_buff_head *skbs) -{ - local->ops->tx_frags(&local->hw, vif, sta, skbs); -} - static inline void drv_get_et_strings(struct ieee80211_sub_if_data *sdata, u32 sset, u8 *data) { diff --git a/net/mac80211/main.c b/net/mac80211/main.c index ab32c59be894..c794101f8987 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -587,7 +587,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN); - BUG_ON(!ops->tx && !ops->tx_frags); + BUG_ON(!ops->tx); BUG_ON(!ops->start); BUG_ON(!ops->stop); BUG_ON(!ops->config); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 4990f4fb5864..364a1e7b4afa 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1295,11 +1295,8 @@ static bool __ieee80211_tx(struct ieee80211_local *local, break; } - if (local->ops->tx_frags) - drv_tx_frags(local, vif, pubsta, skbs); - else - result = ieee80211_tx_frags(local, vif, pubsta, skbs, - txpending); + result = ieee80211_tx_frags(local, vif, pubsta, skbs, + txpending); ieee80211_tpt_led_trig_tx(local, fc, led_len); ieee80211_led_tx(local, 1); -- cgit v1.2.3 From a83eff0a82a7f3f14fea477fd41e6c082e7fc96a Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 23 May 2012 18:47:05 -0700 Subject: rcu: Add tracing for _rcu_barrier() This commit adds event tracing for _rcu_barrier() execution. This is defined only if RCU_TRACE=y. Signed-off-by: Paul E. McKenney Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- include/trace/events/rcu.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ kernel/rcutree.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index d274734b2aa4..5bde94d8585b 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h @@ -541,6 +541,50 @@ TRACE_EVENT(rcu_torture_read, __entry->rcutorturename, __entry->rhp) ); +/* + * Tracepoint for _rcu_barrier() execution. The string "s" describes + * the _rcu_barrier phase: + * "Begin": rcu_barrier_callback() started. + * "Check": rcu_barrier_callback() checking for piggybacking. + * "EarlyExit": rcu_barrier_callback() piggybacked, thus early exit. + * "Inc1": rcu_barrier_callback() piggyback check counter incremented. + * "Offline": rcu_barrier_callback() found offline CPU + * "OnlineQ": rcu_barrier_callback() found online CPU with callbacks. + * "OnlineNQ": rcu_barrier_callback() found online CPU, no callbacks. + * "IRQ": An rcu_barrier_callback() callback posted on remote CPU. + * "CB": An rcu_barrier_callback() invoked a callback, not the last. + * "LastCB": An rcu_barrier_callback() invoked the last callback. + * "Inc2": rcu_barrier_callback() piggyback check counter incremented. + * The "cpu" argument is the CPU or -1 if meaningless, the "cnt" argument + * is the count of remaining callbacks, and "done" is the piggybacking count. + */ +TRACE_EVENT(rcu_barrier, + + TP_PROTO(char *rcuname, char *s, int cpu, int cnt, unsigned long done), + + TP_ARGS(rcuname, s, cpu, cnt, done), + + TP_STRUCT__entry( + __field(char *, rcuname) + __field(char *, s) + __field(int, cpu) + __field(int, cnt) + __field(unsigned long, done) + ), + + TP_fast_assign( + __entry->rcuname = rcuname; + __entry->s = s; + __entry->cpu = cpu; + __entry->cnt = cnt; + __entry->done = done; + ), + + TP_printk("%s %s cpu %d remaining %d # %lu", + __entry->rcuname, __entry->s, __entry->cpu, __entry->cnt, + __entry->done) +); + #else /* #ifdef CONFIG_RCU_TRACE */ #define trace_rcu_grace_period(rcuname, gpnum, gpevent) do { } while (0) @@ -564,6 +608,7 @@ TRACE_EVENT(rcu_torture_read, #define trace_rcu_batch_end(rcuname, callbacks_invoked, cb, nr, iit, risk) \ do { } while (0) #define trace_rcu_torture_read(rcutorturename, rhp) do { } while (0) +#define trace_rcu_barrier(name, s, cpu, cnt, done) do { } while (0) #endif /* #else #ifdef CONFIG_RCU_TRACE */ diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 6bb5d562253f..dda43d826504 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -2259,6 +2259,17 @@ static int rcu_cpu_has_callbacks(int cpu) rcu_preempt_cpu_has_callbacks(cpu); } +/* + * Helper function for _rcu_barrier() tracing. If tracing is disabled, + * the compiler is expected to optimize this away. + */ +static void _rcu_barrier_trace(struct rcu_state *rsp, char *s, + int cpu, unsigned long done) +{ + trace_rcu_barrier(rsp->name, s, cpu, + atomic_read(&rsp->barrier_cpu_count), done); +} + /* * RCU callback function for _rcu_barrier(). If we are last, wake * up the task executing _rcu_barrier(). @@ -2268,8 +2279,12 @@ static void rcu_barrier_callback(struct rcu_head *rhp) struct rcu_data *rdp = container_of(rhp, struct rcu_data, barrier_head); struct rcu_state *rsp = rdp->rsp; - if (atomic_dec_and_test(&rsp->barrier_cpu_count)) + if (atomic_dec_and_test(&rsp->barrier_cpu_count)) { + _rcu_barrier_trace(rsp, "LastCB", -1, rsp->n_barrier_done); complete(&rsp->barrier_completion); + } else { + _rcu_barrier_trace(rsp, "CB", -1, rsp->n_barrier_done); + } } /* @@ -2280,6 +2295,7 @@ static void rcu_barrier_func(void *type) struct rcu_state *rsp = type; struct rcu_data *rdp = __this_cpu_ptr(rsp->rda); + _rcu_barrier_trace(rsp, "IRQ", -1, rsp->n_barrier_done); atomic_inc(&rsp->barrier_cpu_count); rsp->call(&rdp->barrier_head, rcu_barrier_callback); } @@ -2298,6 +2314,7 @@ static void _rcu_barrier(struct rcu_state *rsp) unsigned long snap_done; init_rcu_head_on_stack(&rd.barrier_head); + _rcu_barrier_trace(rsp, "Begin", -1, snap); /* Take mutex to serialize concurrent rcu_barrier() requests. */ mutex_lock(&rsp->barrier_mutex); @@ -2315,7 +2332,9 @@ static void _rcu_barrier(struct rcu_state *rsp) * value up to the next even number and adds two before comparing. */ snap_done = ACCESS_ONCE(rsp->n_barrier_done); + _rcu_barrier_trace(rsp, "Check", -1, snap_done); if (ULONG_CMP_GE(snap_done, ((snap + 1) & ~0x1) + 2)) { + _rcu_barrier_trace(rsp, "EarlyExit", -1, snap_done); smp_mb(); /* caller's subsequent code after above check. */ mutex_unlock(&rsp->barrier_mutex); return; @@ -2328,6 +2347,7 @@ static void _rcu_barrier(struct rcu_state *rsp) */ ACCESS_ONCE(rsp->n_barrier_done)++; WARN_ON_ONCE((rsp->n_barrier_done & 0x1) != 1); + _rcu_barrier_trace(rsp, "Inc1", -1, rsp->n_barrier_done); smp_mb(); /* Order ->n_barrier_done increment with below mechanism. */ /* @@ -2364,13 +2384,19 @@ static void _rcu_barrier(struct rcu_state *rsp) preempt_disable(); rdp = per_cpu_ptr(rsp->rda, cpu); if (cpu_is_offline(cpu)) { + _rcu_barrier_trace(rsp, "Offline", cpu, + rsp->n_barrier_done); preempt_enable(); while (cpu_is_offline(cpu) && ACCESS_ONCE(rdp->qlen)) schedule_timeout_interruptible(1); } else if (ACCESS_ONCE(rdp->qlen)) { + _rcu_barrier_trace(rsp, "OnlineQ", cpu, + rsp->n_barrier_done); smp_call_function_single(cpu, rcu_barrier_func, rsp, 1); preempt_enable(); } else { + _rcu_barrier_trace(rsp, "OnlineNQ", cpu, + rsp->n_barrier_done); preempt_enable(); } } @@ -2403,6 +2429,7 @@ static void _rcu_barrier(struct rcu_state *rsp) smp_mb(); /* Keep increment after above mechanism. */ ACCESS_ONCE(rsp->n_barrier_done)++; WARN_ON_ONCE((rsp->n_barrier_done & 0x1) != 0); + _rcu_barrier_trace(rsp, "Inc2", -1, rsp->n_barrier_done); smp_mb(); /* Keep increment before caller's subsequent code. */ /* Wait for all rcu_barrier_callback() callbacks to be invoked. */ -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 From 68cb2b559278858ef9f3a7722f95df88797c7840 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Jul 2012 15:20:37 +0200 Subject: ALSA: Convert to new pm_ops for PCI drivers Straightforward conversion to the new pm_ops from the legacy suspend/resume ops. Since we change vx222, vx_core and vxpocket have to be converted, too. Signed-off-by: Takashi Iwai --- include/sound/cs46xx.h | 3 +-- include/sound/trident.h | 3 +-- include/sound/vx_core.h | 2 +- include/sound/ymfpci.h | 3 +-- sound/drivers/vx/vx_core.c | 2 +- sound/pci/ali5451/ali5451.c | 24 +++++++++++++++--------- sound/pci/als300.c | 24 +++++++++++++++--------- sound/pci/als4000.c | 25 +++++++++++++++---------- sound/pci/atiixp.c | 24 +++++++++++++++--------- sound/pci/atiixp_modem.c | 25 +++++++++++++++---------- sound/pci/azt3328.c | 25 +++++++++++++++---------- sound/pci/ca0106/ca0106_main.c | 24 +++++++++++++++--------- sound/pci/cmipci.c | 24 +++++++++++++++--------- sound/pci/cs4281.c | 24 +++++++++++++++--------- sound/pci/cs46xx/cs46xx.c | 5 +++-- sound/pci/cs46xx/cs46xx_lib.c | 14 +++++++++----- sound/pci/cs5535audio/cs5535audio.c | 5 +++-- sound/pci/cs5535audio/cs5535audio.h | 5 +---- sound/pci/cs5535audio/cs5535audio_pm.c | 13 ++++++++----- sound/pci/ctxfi/ctatc.c | 4 ++-- sound/pci/ctxfi/ctatc.h | 2 +- sound/pci/ctxfi/cthardware.h | 2 +- sound/pci/ctxfi/cthw20k1.c | 4 ++-- sound/pci/ctxfi/cthw20k2.c | 4 ++-- sound/pci/ctxfi/xfi.c | 22 +++++++++++++--------- sound/pci/echoaudio/echoaudio.c | 22 +++++++++++++--------- sound/pci/emu10k1/emu10k1.c | 26 ++++++++++++++++---------- sound/pci/ens1370.c | 25 +++++++++++++++---------- sound/pci/es1938.c | 24 +++++++++++++++--------- sound/pci/es1968.c | 24 +++++++++++++++--------- sound/pci/fm801.c | 26 ++++++++++++++++---------- sound/pci/hda/hda_codec.c | 2 +- sound/pci/hda/hda_codec.h | 2 +- sound/pci/hda/hda_intel.c | 29 ++++++++++++++++++----------- sound/pci/hda/patch_analog.c | 2 +- sound/pci/hda/patch_cirrus.c | 2 +- sound/pci/hda/patch_conexant.c | 2 +- sound/pci/hda/patch_realtek.c | 2 +- sound/pci/hda/patch_sigmatel.c | 2 +- sound/pci/hda/patch_via.c | 2 +- sound/pci/ice1712/ice1724.c | 26 ++++++++++++++++---------- sound/pci/intel8x0.c | 24 +++++++++++++++--------- sound/pci/intel8x0m.c | 24 +++++++++++++++--------- sound/pci/maestro3.c | 24 +++++++++++++++--------- sound/pci/nm256/nm256.c | 24 +++++++++++++++--------- sound/pci/oxygen/oxygen.c | 5 +++-- sound/pci/oxygen/oxygen.h | 3 +-- sound/pci/oxygen/oxygen_lib.c | 17 ++++++++++------- sound/pci/oxygen/virtuoso.c | 5 +++-- sound/pci/riptide/riptide.c | 26 ++++++++++++++++---------- sound/pci/sis7019.c | 25 +++++++++++++++---------- sound/pci/trident/trident.c | 5 +++-- sound/pci/trident/trident_main.c | 14 +++++++++----- sound/pci/via82xx.c | 24 +++++++++++++++--------- sound/pci/via82xx_modem.c | 24 +++++++++++++++--------- sound/pci/vx222/vx222.c | 26 ++++++++++++++++---------- sound/pci/ymfpci/ymfpci.c | 5 +++-- sound/pci/ymfpci/ymfpci_main.c | 14 +++++++++----- sound/pcmcia/vx/vxpocket.c | 2 +- 59 files changed, 496 insertions(+), 325 deletions(-) (limited to 'include') diff --git a/include/sound/cs46xx.h b/include/sound/cs46xx.h index e3005a674a24..34a2dd1614fa 100644 --- a/include/sound/cs46xx.h +++ b/include/sound/cs46xx.h @@ -1730,8 +1730,7 @@ int snd_cs46xx_create(struct snd_card *card, struct pci_dev *pci, int external_amp, int thinkpad, struct snd_cs46xx **rcodec); -int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state); -int snd_cs46xx_resume(struct pci_dev *pci); +extern const struct dev_pm_ops snd_cs46xx_pm; int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); diff --git a/include/sound/trident.h b/include/sound/trident.h index 9f191a0a1e19..06f0478103db 100644 --- a/include/sound/trident.h +++ b/include/sound/trident.h @@ -430,8 +430,7 @@ void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voi void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice); void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice); void snd_trident_write_voice_regs(struct snd_trident * trident, struct snd_trident_voice *voice); -int snd_trident_suspend(struct pci_dev *pci, pm_message_t state); -int snd_trident_resume(struct pci_dev *pci); +extern const struct dev_pm_ops snd_trident_pm; /* TLB memory allocation */ struct snd_util_memblk *snd_trident_alloc_pages(struct snd_trident *trident, diff --git a/include/sound/vx_core.h b/include/sound/vx_core.h index 5456343ebe4c..4f67c762cd74 100644 --- a/include/sound/vx_core.h +++ b/include/sound/vx_core.h @@ -341,7 +341,7 @@ int vx_change_frequency(struct vx_core *chip); /* * PM */ -int snd_vx_suspend(struct vx_core *card, pm_message_t state); +int snd_vx_suspend(struct vx_core *card); int snd_vx_resume(struct vx_core *card); /* diff --git a/include/sound/ymfpci.h b/include/sound/ymfpci.h index 41199664666b..238f118de6e1 100644 --- a/include/sound/ymfpci.h +++ b/include/sound/ymfpci.h @@ -377,8 +377,7 @@ int snd_ymfpci_create(struct snd_card *card, struct snd_ymfpci ** rcodec); void snd_ymfpci_free_gameport(struct snd_ymfpci *chip); -int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state); -int snd_ymfpci_resume(struct pci_dev *pci); +extern const struct dev_pm_ops snd_ymfpci_pm; int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c index b8e515999bc2..de5055a3b0d0 100644 --- a/sound/drivers/vx/vx_core.c +++ b/sound/drivers/vx/vx_core.c @@ -725,7 +725,7 @@ EXPORT_SYMBOL(snd_vx_dsp_load); /* * suspend */ -int snd_vx_suspend(struct vx_core *chip, pm_message_t state) +int snd_vx_suspend(struct vx_core *chip) { unsigned int i; diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 9dfc27bf6cc6..ee895f3c8605 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -1884,9 +1884,10 @@ static int __devinit snd_ali_mixer(struct snd_ali * codec) } #ifdef CONFIG_PM -static int ali_suspend(struct pci_dev *pci, pm_message_t state) +static int ali_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ali *chip = card->private_data; struct snd_ali_image *im; int i, j; @@ -1929,13 +1930,14 @@ static int ali_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int ali_resume(struct pci_dev *pci) +static int ali_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ali *chip = card->private_data; struct snd_ali_image *im; int i, j; @@ -1982,6 +1984,11 @@ static int ali_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume); +#define ALI_PM_OPS &ali_pm +#else +#define ALI_PM_OPS NULL #endif /* CONFIG_PM */ static int snd_ali_free(struct snd_ali * codec) @@ -2299,10 +2306,9 @@ static struct pci_driver ali5451_driver = { .id_table = snd_ali_ids, .probe = snd_ali_probe, .remove = __devexit_p(snd_ali_remove), -#ifdef CONFIG_PM - .suspend = ali_suspend, - .resume = ali_resume, -#endif + .driver = { + .pm = ALI_PM_OPS, + }, }; module_pci_driver(ali5451_driver); diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 59d65388faf5..68c4469c6d19 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -766,9 +766,10 @@ static int __devinit snd_als300_create(struct snd_card *card, } #ifdef CONFIG_PM -static int snd_als300_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_als300_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_als300 *chip = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); @@ -777,13 +778,14 @@ static int snd_als300_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_als300_resume(struct pci_dev *pci) +static int snd_als300_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_als300 *chip = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -802,6 +804,11 @@ static int snd_als300_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(snd_als300_pm, snd_als300_suspend, snd_als300_resume); +#define SND_ALS300_PM_OPS &snd_als300_pm +#else +#define SND_ALS300_PM_OPS NULL #endif static int __devinit snd_als300_probe(struct pci_dev *pci, @@ -857,10 +864,9 @@ static struct pci_driver als300_driver = { .id_table = snd_als300_ids, .probe = snd_als300_probe, .remove = __devexit_p(snd_als300_remove), -#ifdef CONFIG_PM - .suspend = snd_als300_suspend, - .resume = snd_als300_resume, -#endif + .driver = { + .pm = SND_ALS300_PM_OPS, + }, }; module_pci_driver(als300_driver); diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index 7d7f2598c748..0eeca49c5754 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -988,9 +988,10 @@ static void __devexit snd_card_als4000_remove(struct pci_dev *pci) } #ifdef CONFIG_PM -static int snd_als4000_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_als4000_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_card_als4000 *acard = card->private_data; struct snd_sb *chip = acard->chip; @@ -1001,13 +1002,14 @@ static int snd_als4000_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_als4000_resume(struct pci_dev *pci) +static int snd_als4000_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_card_als4000 *acard = card->private_data; struct snd_sb *chip = acard->chip; @@ -1033,18 +1035,21 @@ static int snd_als4000_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(snd_als4000_pm, snd_als4000_suspend, snd_als4000_resume); +#define SND_ALS4000_PM_OPS &snd_als4000_pm +#else +#define SND_ALS4000_PM_OPS NULL +#endif /* CONFIG_PM */ static struct pci_driver als4000_driver = { .name = KBUILD_MODNAME, .id_table = snd_als4000_ids, .probe = snd_card_als4000_probe, .remove = __devexit_p(snd_card_als4000_remove), -#ifdef CONFIG_PM - .suspend = snd_als4000_suspend, - .resume = snd_als4000_resume, -#endif + .driver = { + .pm = SND_ALS4000_PM_OPS, + }, }; module_pci_driver(als4000_driver); diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 156a94f8a123..31020d2a868b 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1462,9 +1462,10 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp *chip, int clock, /* * power management */ -static int snd_atiixp_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_atiixp_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct atiixp *chip = card->private_data; int i; @@ -1484,13 +1485,14 @@ static int snd_atiixp_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_atiixp_resume(struct pci_dev *pci) +static int snd_atiixp_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct atiixp *chip = card->private_data; int i; @@ -1526,6 +1528,11 @@ static int snd_atiixp_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); +#define SND_ATIIXP_PM_OPS &snd_atiixp_pm +#else +#define SND_ATIIXP_PM_OPS NULL #endif /* CONFIG_PM */ @@ -1705,10 +1712,9 @@ static struct pci_driver atiixp_driver = { .id_table = snd_atiixp_ids, .probe = snd_atiixp_probe, .remove = __devexit_p(snd_atiixp_remove), -#ifdef CONFIG_PM - .suspend = snd_atiixp_suspend, - .resume = snd_atiixp_resume, -#endif + .driver = { + .pm = SND_ATIIXP_PM_OPS, + }, }; module_pci_driver(atiixp_driver); diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 30a4fd96ce73..79e204ec623f 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -1117,9 +1117,10 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock) /* * power management */ -static int snd_atiixp_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_atiixp_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct atiixp_modem *chip = card->private_data; int i; @@ -1133,13 +1134,14 @@ static int snd_atiixp_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_atiixp_resume(struct pci_dev *pci) +static int snd_atiixp_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct atiixp_modem *chip = card->private_data; int i; @@ -1162,8 +1164,12 @@ static int snd_atiixp_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); +#define SND_ATIIXP_PM_OPS &snd_atiixp_pm +#else +#define SND_ATIIXP_PM_OPS NULL +#endif /* CONFIG_PM */ #ifdef CONFIG_PROC_FS /* @@ -1336,10 +1342,9 @@ static struct pci_driver atiixp_modem_driver = { .id_table = snd_atiixp_ids, .probe = snd_atiixp_probe, .remove = __devexit_p(snd_atiixp_remove), -#ifdef CONFIG_PM - .suspend = snd_atiixp_suspend, - .resume = snd_atiixp_resume, -#endif + .driver = { + .pm = SND_ATIIXP_PM_OPS, + }, }; module_pci_driver(atiixp_modem_driver); diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index f0b4d7493af5..4dddd871548b 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -2794,9 +2794,10 @@ snd_azf3328_resume_ac97(const struct snd_azf3328 *chip) } static int -snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state) +snd_azf3328_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_azf3328 *chip = card->private_data; u16 *saved_regs_ctrl_u16; @@ -2824,14 +2825,15 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } static int -snd_azf3328_resume(struct pci_dev *pci) +snd_azf3328_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); const struct snd_azf3328 *chip = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -2859,18 +2861,21 @@ snd_azf3328_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume); +#define SND_AZF3328_PM_OPS &snd_azf3328_pm +#else +#define SND_AZF3328_PM_OPS NULL +#endif /* CONFIG_PM */ static struct pci_driver azf3328_driver = { .name = KBUILD_MODNAME, .id_table = snd_azf3328_ids, .probe = snd_azf3328_probe, .remove = __devexit_p(snd_azf3328_remove), -#ifdef CONFIG_PM - .suspend = snd_azf3328_suspend, - .resume = snd_azf3328_resume, -#endif + .driver = { + .pm = SND_AZF3328_PM_OPS, + }, }; module_pci_driver(azf3328_driver); diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index e76d68a7081f..83277b747b36 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1872,9 +1872,10 @@ static void __devexit snd_ca0106_remove(struct pci_dev *pci) } #ifdef CONFIG_PM -static int snd_ca0106_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_ca0106_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ca0106 *chip = card->private_data; int i; @@ -1889,13 +1890,14 @@ static int snd_ca0106_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_ca0106_resume(struct pci_dev *pci) +static int snd_ca0106_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ca0106 *chip = card->private_data; int i; @@ -1922,6 +1924,11 @@ static int snd_ca0106_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(snd_ca0106_pm, snd_ca0106_suspend, snd_ca0106_resume); +#define SND_CA0106_PM_OPS &snd_ca0106_pm +#else +#define SND_CA0106_PM_OPS NULL #endif // PCI IDs @@ -1937,10 +1944,9 @@ static struct pci_driver ca0106_driver = { .id_table = snd_ca0106_ids, .probe = snd_ca0106_probe, .remove = __devexit_p(snd_ca0106_remove), -#ifdef CONFIG_PM - .suspend = snd_ca0106_suspend, - .resume = snd_ca0106_resume, -#endif + .driver = { + .pm = SND_CA0106_PM_OPS, + }, }; module_pci_driver(ca0106_driver); diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 3815bd4c6779..b7d6f2b886ef 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -3338,9 +3338,10 @@ static unsigned char saved_mixers[] = { SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, }; -static int snd_cmipci_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_cmipci_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct cmipci *cm = card->private_data; int i; @@ -3361,13 +3362,14 @@ static int snd_cmipci_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_cmipci_resume(struct pci_dev *pci) +static int snd_cmipci_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct cmipci *cm = card->private_data; int i; @@ -3396,6 +3398,11 @@ static int snd_cmipci_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(snd_cmipci_pm, snd_cmipci_suspend, snd_cmipci_resume); +#define SND_CMIPCI_PM_OPS &snd_cmipci_pm +#else +#define SND_CMIPCI_PM_OPS NULL #endif /* CONFIG_PM */ static struct pci_driver cmipci_driver = { @@ -3403,10 +3410,9 @@ static struct pci_driver cmipci_driver = { .id_table = snd_cmipci_ids, .probe = snd_cmipci_probe, .remove = __devexit_p(snd_cmipci_remove), -#ifdef CONFIG_PM - .suspend = snd_cmipci_suspend, - .resume = snd_cmipci_resume, -#endif + .driver = { + .pm = SND_CMIPCI_PM_OPS, + }, }; module_pci_driver(cmipci_driver); diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 33506ee569bd..45a8317085f4 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1997,9 +1997,10 @@ static int saved_regs[SUSPEND_REGISTERS] = { #define CLKCR1_CKRA 0x00010000L -static int cs4281_suspend(struct pci_dev *pci, pm_message_t state) +static int cs4281_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct cs4281 *chip = card->private_data; u32 ulCLK; unsigned int i; @@ -2040,13 +2041,14 @@ static int cs4281_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int cs4281_resume(struct pci_dev *pci) +static int cs4281_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct cs4281 *chip = card->private_data; unsigned int i; u32 ulCLK; @@ -2082,6 +2084,11 @@ static int cs4281_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(cs4281_pm, cs4281_suspend, cs4281_resume); +#define CS4281_PM_OPS &cs4281_pm +#else +#define CS4281_PM_OPS NULL #endif /* CONFIG_PM */ static struct pci_driver cs4281_driver = { @@ -2089,10 +2096,9 @@ static struct pci_driver cs4281_driver = { .id_table = snd_cs4281_ids, .probe = snd_cs4281_probe, .remove = __devexit_p(snd_cs4281_remove), -#ifdef CONFIG_PM - .suspend = cs4281_suspend, - .resume = cs4281_resume, -#endif + .driver = { + .pm = CS4281_PM_OPS, + }, }; module_pci_driver(cs4281_driver); diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 6cc7404e0e8f..00e03bc9a762 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -167,8 +167,9 @@ static struct pci_driver cs46xx_driver = { .probe = snd_card_cs46xx_probe, .remove = __devexit_p(snd_card_cs46xx_remove), #ifdef CONFIG_PM - .suspend = snd_cs46xx_suspend, - .resume = snd_cs46xx_resume, + .driver = { + .pm = &snd_cs46xx_pm, + }, #endif }; diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 4fa53161b094..28b9747becc9 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -3599,9 +3599,10 @@ static unsigned int saved_regs[] = { BA1_CVOL, }; -int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_cs46xx_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_cs46xx *chip = card->private_data; int i, amp_saved; @@ -3628,13 +3629,14 @@ int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -int snd_cs46xx_resume(struct pci_dev *pci) +static int snd_cs46xx_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_cs46xx *chip = card->private_data; int amp_saved; #ifdef CONFIG_SND_CS46XX_NEW_DSP @@ -3707,6 +3709,8 @@ int snd_cs46xx_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +SIMPLE_DEV_PM_OPS(snd_cs46xx_pm, snd_cs46xx_suspend, snd_cs46xx_resume); #endif /* CONFIG_PM */ diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 2c9697cf0a1a..51f64ba5facf 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -400,8 +400,9 @@ static struct pci_driver cs5535audio_driver = { .probe = snd_cs5535audio_probe, .remove = __devexit_p(snd_cs5535audio_remove), #ifdef CONFIG_PM - .suspend = snd_cs5535audio_suspend, - .resume = snd_cs5535audio_resume, + .driver = { + .pm = &snd_cs5535audio_pm, + }, #endif }; diff --git a/sound/pci/cs5535audio/cs5535audio.h b/sound/pci/cs5535audio/cs5535audio.h index 51966d782a3c..bb3cc641130c 100644 --- a/sound/pci/cs5535audio/cs5535audio.h +++ b/sound/pci/cs5535audio/cs5535audio.h @@ -94,10 +94,7 @@ struct cs5535audio { struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS]; }; -#ifdef CONFIG_PM -int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state); -int snd_cs5535audio_resume(struct pci_dev *pci); -#endif +extern const struct dev_pm_ops snd_cs5535audio_pm; #ifdef CONFIG_OLPC void __devinit olpc_prequirks(struct snd_card *card, diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c index 185b00088320..6c34def5986d 100644 --- a/sound/pci/cs5535audio/cs5535audio_pm.c +++ b/sound/pci/cs5535audio/cs5535audio_pm.c @@ -55,9 +55,10 @@ static void snd_cs5535audio_stop_hardware(struct cs5535audio *cs5535au) } -int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_cs5535audio_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct cs5535audio *cs5535au = card->private_data; int i; @@ -77,13 +78,14 @@ int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state) return -EIO; } pci_disable_device(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -int snd_cs5535audio_resume(struct pci_dev *pci) +static int snd_cs5535audio_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct cs5535audio *cs5535au = card->private_data; u32 tmp; int timeout; @@ -129,3 +131,4 @@ int snd_cs5535audio_resume(struct pci_dev *pci) return 0; } +SIMPLE_DEV_PM_OPS(snd_cs5535audio_pm, snd_cs5535audio_suspend, snd_cs5535audio_resume); diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index d8a4423539ce..8e40262d4117 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -1537,7 +1537,7 @@ static void atc_connect_resources(struct ct_atc *atc) } #ifdef CONFIG_PM -static int atc_suspend(struct ct_atc *atc, pm_message_t state) +static int atc_suspend(struct ct_atc *atc) { int i; struct hw *hw = atc->hw; @@ -1553,7 +1553,7 @@ static int atc_suspend(struct ct_atc *atc, pm_message_t state) atc_release_resources(atc); - hw->suspend(hw, state); + hw->suspend(hw); return 0; } diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index 3a0def656af0..653e813ad142 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -144,7 +144,7 @@ struct ct_atc { struct ct_timer *timer; #ifdef CONFIG_PM - int (*suspend)(struct ct_atc *atc, pm_message_t state); + int (*suspend)(struct ct_atc *atc); int (*resume)(struct ct_atc *atc); #define NUM_PCMS (NUM_CTALSADEVS - 1) struct snd_pcm *pcms[NUM_PCMS]; diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h index 908315bec3b4..c56fe533b3f3 100644 --- a/sound/pci/ctxfi/cthardware.h +++ b/sound/pci/ctxfi/cthardware.h @@ -73,7 +73,7 @@ struct hw { int (*card_stop)(struct hw *hw); int (*pll_init)(struct hw *hw, unsigned int rsr); #ifdef CONFIG_PM - int (*suspend)(struct hw *hw, pm_message_t state); + int (*suspend)(struct hw *hw); int (*resume)(struct hw *hw, struct card_conf *info); #endif int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source); diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index a7df19791f5a..dc1969bc67d4 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -2086,7 +2086,7 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) } #ifdef CONFIG_PM -static int hw_suspend(struct hw *hw, pm_message_t state) +static int hw_suspend(struct hw *hw) { struct pci_dev *pci = hw->pci; @@ -2099,7 +2099,7 @@ static int hw_suspend(struct hw *hw, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index d6c54b524bfa..9d1231dc4ae2 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -2202,7 +2202,7 @@ static int hw_card_init(struct hw *hw, struct card_conf *info) } #ifdef CONFIG_PM -static int hw_suspend(struct hw *hw, pm_message_t state) +static int hw_suspend(struct hw *hw) { struct pci_dev *pci = hw->pci; @@ -2210,7 +2210,7 @@ static int hw_suspend(struct hw *hw, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index 75aa2c338410..e002183ef8b2 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -126,21 +126,26 @@ static void __devexit ct_card_remove(struct pci_dev *pci) } #ifdef CONFIG_PM -static int ct_card_suspend(struct pci_dev *pci, pm_message_t state) +static int ct_card_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct snd_card *card = dev_get_drvdata(dev); struct ct_atc *atc = card->private_data; - return atc->suspend(atc, state); + return atc->suspend(atc); } -static int ct_card_resume(struct pci_dev *pci) +static int ct_card_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct snd_card *card = dev_get_drvdata(dev); struct ct_atc *atc = card->private_data; return atc->resume(atc); } + +static SIMPLE_DEV_PM_OPS(ct_card_pm, ct_card_suspend, ct_card_resume); +#define CT_CARD_PM_OPS &ct_card_pm +#else +#define CT_CARD_PM_OPS NULL #endif static struct pci_driver ct_driver = { @@ -148,10 +153,9 @@ static struct pci_driver ct_driver = { .id_table = ct_pci_dev_ids, .probe = ct_card_probe, .remove = __devexit_p(ct_card_remove), -#ifdef CONFIG_PM - .suspend = ct_card_suspend, - .resume = ct_card_resume, -#endif + .driver = { + .pm = CT_CARD_PM_OPS, + }, }; module_pci_driver(ct_driver); diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 0f8eda1dafdb..0ff754f180d0 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -2205,9 +2205,10 @@ ctl_error: #if defined(CONFIG_PM) -static int snd_echo_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_echo_suspend(struct device *dev) { - struct echoaudio *chip = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct echoaudio *chip = dev_get_drvdata(dev); DE_INIT(("suspend start\n")); snd_pcm_suspend_all(chip->analog_pcm); @@ -2242,9 +2243,10 @@ static int snd_echo_suspend(struct pci_dev *pci, pm_message_t state) -static int snd_echo_resume(struct pci_dev *pci) +static int snd_echo_resume(struct device *dev) { - struct echoaudio *chip = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct echoaudio *chip = dev_get_drvdata(dev); struct comm_page *commpage, *commpage_bak; u32 pipe_alloc_mask; int err; @@ -2307,10 +2309,13 @@ static int snd_echo_resume(struct pci_dev *pci) return 0; } +static SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume); +#define SND_ECHO_PM_OPS &snd_echo_pm +#else +#define SND_ECHO_PM_OPS NULL #endif /* CONFIG_PM */ - static void __devexit snd_echo_remove(struct pci_dev *pci) { struct echoaudio *chip; @@ -2333,10 +2338,9 @@ static struct pci_driver echo_driver = { .id_table = snd_echo_ids, .probe = snd_echo_probe, .remove = __devexit_p(snd_echo_remove), -#ifdef CONFIG_PM - .suspend = snd_echo_suspend, - .resume = snd_echo_resume, -#endif /* CONFIG_PM */ + .driver = { + .pm = SND_ECHO_PM_OPS, + }, }; module_pci_driver(echo_driver); diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 7fdbbe4d9965..ddac4e6d660d 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -207,9 +207,10 @@ static void __devexit snd_card_emu10k1_remove(struct pci_dev *pci) #ifdef CONFIG_PM -static int snd_emu10k1_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_emu10k1_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_emu10k1 *emu = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); @@ -231,13 +232,14 @@ static int snd_emu10k1_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_emu10k1_resume(struct pci_dev *pci) +static int snd_emu10k1_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_emu10k1 *emu = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -261,17 +263,21 @@ static int snd_emu10k1_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -#endif + +static SIMPLE_DEV_PM_OPS(snd_emu10k1_pm, snd_emu10k1_suspend, snd_emu10k1_resume); +#define SND_EMU10K1_PM_OPS &snd_emu10k1_pm +#else +#define SND_EMU10K1_PM_OPS NULL +#endif /* CONFIG_PM */ static struct pci_driver emu10k1_driver = { .name = KBUILD_MODNAME, .id_table = snd_emu10k1_ids, .probe = snd_card_emu10k1_probe, .remove = __devexit_p(snd_card_emu10k1_remove), -#ifdef CONFIG_PM - .suspend = snd_emu10k1_suspend, - .resume = snd_emu10k1_resume, -#endif + .driver = { + .pm = SND_EMU10K1_PM_OPS, + }, }; module_pci_driver(emu10k1_driver); diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 3821c81d1c99..f7e6f73186e1 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -2033,9 +2033,10 @@ static void snd_ensoniq_chip_init(struct ensoniq *ensoniq) } #ifdef CONFIG_PM -static int snd_ensoniq_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_ensoniq_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct ensoniq *ensoniq = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); @@ -2058,13 +2059,14 @@ static int snd_ensoniq_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_ensoniq_resume(struct pci_dev *pci) +static int snd_ensoniq_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct ensoniq *ensoniq = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -2087,8 +2089,12 @@ static int snd_ensoniq_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(snd_ensoniq_pm, snd_ensoniq_suspend, snd_ensoniq_resume); +#define SND_ENSONIQ_PM_OPS &snd_ensoniq_pm +#else +#define SND_ENSONIQ_PM_OPS NULL +#endif /* CONFIG_PM */ static int __devinit snd_ensoniq_create(struct snd_card *card, struct pci_dev *pci, @@ -2493,10 +2499,9 @@ static struct pci_driver ens137x_driver = { .id_table = snd_audiopci_ids, .probe = snd_audiopci_probe, .remove = __devexit_p(snd_audiopci_remove), -#ifdef CONFIG_PM - .suspend = snd_ensoniq_suspend, - .resume = snd_ensoniq_resume, -#endif + .driver = { + .pm = SND_ENSONIQ_PM_OPS, + }, }; module_pci_driver(ens137x_driver); diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 82c8d8c5c52a..227dff70069f 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1474,9 +1474,10 @@ static unsigned char saved_regs[SAVED_REG_SIZE+1] = { }; -static int es1938_suspend(struct pci_dev *pci, pm_message_t state) +static int es1938_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct es1938 *chip = card->private_data; unsigned char *s, *d; @@ -1494,13 +1495,14 @@ static int es1938_suspend(struct pci_dev *pci, pm_message_t state) } pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int es1938_resume(struct pci_dev *pci) +static int es1938_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct es1938 *chip = card->private_data; unsigned char *s, *d; @@ -1534,6 +1536,11 @@ static int es1938_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(es1938_pm, es1938_suspend, es1938_resume); +#define ES1938_PM_OPS &es1938_pm +#else +#define ES1938_PM_OPS NULL #endif /* CONFIG_PM */ #ifdef SUPPORT_JOYSTICK @@ -1887,10 +1894,9 @@ static struct pci_driver es1938_driver = { .id_table = snd_es1938_ids, .probe = snd_es1938_probe, .remove = __devexit_p(snd_es1938_remove), -#ifdef CONFIG_PM - .suspend = es1938_suspend, - .resume = es1938_resume, -#endif + .driver = { + .pm = ES1938_PM_OPS, + }, }; module_pci_driver(es1938_driver); diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 52b5c0bf90c1..fb4c90b99c00 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2381,9 +2381,10 @@ static void snd_es1968_start_irq(struct es1968 *chip) /* * PM support */ -static int es1968_suspend(struct pci_dev *pci, pm_message_t state) +static int es1968_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct es1968 *chip = card->private_data; if (! chip->do_pm) @@ -2398,13 +2399,14 @@ static int es1968_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int es1968_resume(struct pci_dev *pci) +static int es1968_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct es1968 *chip = card->private_data; struct esschan *es; @@ -2454,6 +2456,11 @@ static int es1968_resume(struct pci_dev *pci) chip->in_suspend = 0; return 0; } + +static SIMPLE_DEV_PM_OPS(es1968_pm, es1968_suspend, es1968_resume); +#define ES1968_PM_OPS &es1968_pm +#else +#define ES1968_PM_OPS NULL #endif /* CONFIG_PM */ #ifdef SUPPORT_JOYSTICK @@ -2903,10 +2910,9 @@ static struct pci_driver es1968_driver = { .id_table = snd_es1968_ids, .probe = snd_es1968_probe, .remove = __devexit_p(snd_es1968_remove), -#ifdef CONFIG_PM - .suspend = es1968_suspend, - .resume = es1968_resume, -#endif + .driver = { + .pm = ES1968_PM_OPS, + }, }; module_pci_driver(es1968_driver); diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index b32e8024ea86..522c8706f244 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1369,9 +1369,10 @@ static unsigned char saved_regs[] = { FM801_CODEC_CTRL, FM801_I2S_MODE, FM801_VOLUME, FM801_GEN_CTRL, }; -static int snd_fm801_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_fm801_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct fm801 *chip = card->private_data; int i; @@ -1385,13 +1386,14 @@ static int snd_fm801_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_fm801_resume(struct pci_dev *pci) +static int snd_fm801_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct fm801 *chip = card->private_data; int i; @@ -1414,17 +1416,21 @@ static int snd_fm801_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -#endif + +static SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume); +#define SND_FM801_PM_OPS &snd_fm801_pm +#else +#define SND_FM801_PM_OPS NULL +#endif /* CONFIG_PM */ static struct pci_driver fm801_driver = { .name = KBUILD_MODNAME, .id_table = snd_fm801_ids, .probe = snd_card_fm801_probe, .remove = __devexit_p(snd_card_fm801_remove), -#ifdef CONFIG_PM - .suspend = snd_fm801_suspend, - .resume = snd_fm801_resume, -#endif + .driver = { + .pm = SND_FM801_PM_OPS, + }, }; module_pci_driver(fm801_driver); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 51cb2a2e4fce..f4c274c5144b 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3545,7 +3545,7 @@ static inline void hda_exec_init_verbs(struct hda_codec *codec) {} static void hda_call_codec_suspend(struct hda_codec *codec) { if (codec->patch_ops.suspend) - codec->patch_ops.suspend(codec, PMSG_SUSPEND); + codec->patch_ops.suspend(codec); hda_cleanup_all_streams(codec); hda_set_power_state(codec, codec->afg ? codec->afg : codec->mfg, diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 2fdaadbb4326..5aab408dcb6a 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -703,7 +703,7 @@ struct hda_codec_ops { void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state); #ifdef CONFIG_PM - int (*suspend)(struct hda_codec *codec, pm_message_t state); + int (*suspend)(struct hda_codec *codec); int (*resume)(struct hda_codec *codec); #endif #ifdef CONFIG_SND_HDA_POWER_SAVE diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 7757536b9d5f..a69ec7448714 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2403,9 +2403,10 @@ static void azx_power_notify(struct hda_bus *bus) * power management */ -static int azx_suspend(struct pci_dev *pci, pm_message_t state) +static int azx_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; struct azx_pcm *p; @@ -2424,13 +2425,14 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_msi(chip->pci); pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int azx_resume(struct pci_dev *pci) +static int azx_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -2455,6 +2457,12 @@ static int azx_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } +static SIMPLE_DEV_PM_OPS(azx_pm, azx_suspend, azx_resume); +#define AZX_PM_OPS &azx_pm +#else +#define azx_suspend(dev) +#define azx_resume(dev) +#define AZX_PM_OPS NULL #endif /* CONFIG_PM */ @@ -2521,13 +2529,13 @@ static void azx_vs_set_state(struct pci_dev *pci, disabled ? "Disabling" : "Enabling", pci_name(chip->pci)); if (disabled) { - azx_suspend(pci, PMSG_FREEZE); + azx_suspend(&pci->dev); chip->disabled = true; snd_hda_lock_devices(chip->bus); } else { snd_hda_unlock_devices(chip->bus); chip->disabled = false; - azx_resume(pci); + azx_resume(&pci->dev); } } } @@ -3398,10 +3406,9 @@ static struct pci_driver azx_driver = { .id_table = azx_ids, .probe = azx_probe, .remove = __devexit_p(azx_remove), -#ifdef CONFIG_PM - .suspend = azx_suspend, - .resume = azx_resume, -#endif + .driver = { + .pm = AZX_PM_OPS, + }, }; module_pci_driver(azx_driver); diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index d8b2d6dee986..0208fa121e5a 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -642,7 +642,7 @@ static void ad198x_free(struct hda_codec *codec) } #ifdef CONFIG_PM -static int ad198x_suspend(struct hda_codec *codec, pm_message_t state) +static int ad198x_suspend(struct hda_codec *codec) { ad198x_shutup(codec); return 0; diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 9647ed4d7929..0c4c1a61b378 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -1892,7 +1892,7 @@ static int cs421x_parse_auto_config(struct hda_codec *codec) Manage PDREF, when transitioning to D3hot (DAC,ADC) -> D3, PDREF=1, AFG->D3 */ -static int cs421x_suspend(struct hda_codec *codec, pm_message_t state) +static int cs421x_suspend(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; unsigned int coef; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 2bf99fc1cbf2..14361184ae1e 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -554,7 +554,7 @@ static int conexant_build_controls(struct hda_codec *codec) } #ifdef CONFIG_SND_HDA_POWER_SAVE -static int conexant_suspend(struct hda_codec *codec, pm_message_t state) +static int conexant_suspend(struct hda_codec *codec) { snd_hda_shutup_pins(codec); return 0; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5ccf10a4d593..ab2c729b88ea 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2300,7 +2300,7 @@ static void alc_power_eapd(struct hda_codec *codec) alc_auto_setup_eapd(codec, false); } -static int alc_suspend(struct hda_codec *codec, pm_message_t state) +static int alc_suspend(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; alc_shutup(codec); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 07675282015a..a1596a3b171c 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4997,7 +4997,7 @@ static int stac92xx_resume(struct hda_codec *codec) return 0; } -static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) +static int stac92xx_suspend(struct hda_codec *codec) { stac92xx_shutup(codec); return 0; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 82b368068e08..90645560ed39 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1748,7 +1748,7 @@ static void via_unsol_event(struct hda_codec *codec, } #ifdef CONFIG_PM -static int via_suspend(struct hda_codec *codec, pm_message_t state) +static int via_suspend(struct hda_codec *codec) { struct via_spec *spec = codec->spec; vt1708_stop_hp_work(spec); diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index a01a00d1cf4d..bed9f34f4efe 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -2793,9 +2793,10 @@ static void __devexit snd_vt1724_remove(struct pci_dev *pci) } #ifdef CONFIG_PM -static int snd_vt1724_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_vt1724_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ice1712 *ice = card->private_data; if (!ice->pm_suspend_enabled) @@ -2820,13 +2821,14 @@ static int snd_vt1724_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_vt1724_resume(struct pci_dev *pci) +static int snd_vt1724_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ice1712 *ice = card->private_data; if (!ice->pm_suspend_enabled) @@ -2871,17 +2873,21 @@ static int snd_vt1724_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -#endif + +static SIMPLE_DEV_PM_OPS(snd_vt1724_pm, snd_vt1724_suspend, snd_vt1724_resume); +#define SND_VT1724_PM_OPS &snd_vt1724_pm +#else +#define SND_VT1724_PM_OPS NULL +#endif /* CONFIG_PM */ static struct pci_driver vt1724_driver = { .name = KBUILD_MODNAME, .id_table = snd_vt1724_ids, .probe = snd_vt1724_probe, .remove = __devexit_p(snd_vt1724_remove), -#ifdef CONFIG_PM - .suspend = snd_vt1724_suspend, - .resume = snd_vt1724_resume, -#endif + .driver = { + .pm = SND_VT1724_PM_OPS, + }, }; module_pci_driver(vt1724_driver); diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index f4e2dd4da8cf..cd553f592e2d 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -2624,9 +2624,10 @@ static int snd_intel8x0_free(struct intel8x0 *chip) /* * power management */ -static int intel8x0_suspend(struct pci_dev *pci, pm_message_t state) +static int intel8x0_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct intel8x0 *chip = card->private_data; int i; @@ -2658,13 +2659,14 @@ static int intel8x0_suspend(struct pci_dev *pci, pm_message_t state) /* The call below may disable built-in speaker on some laptops * after S2RAM. So, don't touch it. */ - /* pci_set_power_state(pci, pci_choose_state(pci, state)); */ + /* pci_set_power_state(pci, PCI_D3hot); */ return 0; } -static int intel8x0_resume(struct pci_dev *pci) +static int intel8x0_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct intel8x0 *chip = card->private_data; int i; @@ -2734,6 +2736,11 @@ static int intel8x0_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(intel8x0_pm, intel8x0_suspend, intel8x0_resume); +#define INTEL8X0_PM_OPS &intel8x0_pm +#else +#define INTEL8X0_PM_OPS NULL #endif /* CONFIG_PM */ #define INTEL8X0_TESTBUF_SIZE 32768 /* enough large for one shot */ @@ -3343,10 +3350,9 @@ static struct pci_driver intel8x0_driver = { .id_table = snd_intel8x0_ids, .probe = snd_intel8x0_probe, .remove = __devexit_p(snd_intel8x0_remove), -#ifdef CONFIG_PM - .suspend = intel8x0_suspend, - .resume = intel8x0_resume, -#endif + .driver = { + .pm = INTEL8X0_PM_OPS, + }, }; module_pci_driver(intel8x0_driver); diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index fc27a6a69e77..da44bb3f8e7a 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -1012,9 +1012,10 @@ static int snd_intel8x0m_free(struct intel8x0m *chip) /* * power management */ -static int intel8x0m_suspend(struct pci_dev *pci, pm_message_t state) +static int intel8x0m_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct intel8x0m *chip = card->private_data; int i; @@ -1028,13 +1029,14 @@ static int intel8x0m_suspend(struct pci_dev *pci, pm_message_t state) } pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int intel8x0m_resume(struct pci_dev *pci) +static int intel8x0m_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct intel8x0m *chip = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -1060,6 +1062,11 @@ static int intel8x0m_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(intel8x0m_pm, intel8x0m_suspend, intel8x0m_resume); +#define INTEL8X0M_PM_OPS &intel8x0m_pm +#else +#define INTEL8X0M_PM_OPS NULL #endif /* CONFIG_PM */ #ifdef CONFIG_PROC_FS @@ -1329,10 +1336,9 @@ static struct pci_driver intel8x0m_driver = { .id_table = snd_intel8x0m_ids, .probe = snd_intel8x0m_probe, .remove = __devexit_p(snd_intel8x0m_remove), -#ifdef CONFIG_PM - .suspend = intel8x0m_suspend, - .resume = intel8x0m_resume, -#endif + .driver = { + .pm = INTEL8X0M_PM_OPS, + }, }; module_pci_driver(intel8x0m_driver); diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index deef21399586..36008a943aa3 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2459,9 +2459,10 @@ static int snd_m3_free(struct snd_m3 *chip) * APM support */ #ifdef CONFIG_PM -static int m3_suspend(struct pci_dev *pci, pm_message_t state) +static int m3_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_m3 *chip = card->private_data; int i, dsp_index; @@ -2489,13 +2490,14 @@ static int m3_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int m3_resume(struct pci_dev *pci) +static int m3_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_m3 *chip = card->private_data; int i, dsp_index; @@ -2546,6 +2548,11 @@ static int m3_resume(struct pci_dev *pci) chip->in_suspend = 0; return 0; } + +static SIMPLE_DEV_PM_OPS(m3_pm, m3_suspend, m3_resume); +#define M3_PM_OPS &m3_pm +#else +#define M3_PM_OPS NULL #endif /* CONFIG_PM */ #ifdef CONFIG_SND_MAESTRO3_INPUT @@ -2842,10 +2849,9 @@ static struct pci_driver m3_driver = { .id_table = snd_m3_ids, .probe = snd_m3_probe, .remove = __devexit_p(snd_m3_remove), -#ifdef CONFIG_PM - .suspend = m3_suspend, - .resume = m3_resume, -#endif + .driver = { + .pm = M3_PM_OPS, + }, }; module_pci_driver(m3_driver); diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 8159b05ee94d..465cff25b146 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1382,9 +1382,10 @@ snd_nm256_peek_for_sig(struct nm256 *chip) * APM event handler, so the card is properly reinitialized after a power * event. */ -static int nm256_suspend(struct pci_dev *pci, pm_message_t state) +static int nm256_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct nm256 *chip = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); @@ -1393,13 +1394,14 @@ static int nm256_suspend(struct pci_dev *pci, pm_message_t state) chip->coeffs_current = 0; pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int nm256_resume(struct pci_dev *pci) +static int nm256_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct nm256 *chip = card->private_data; int i; @@ -1434,6 +1436,11 @@ static int nm256_resume(struct pci_dev *pci) chip->in_resume = 0; return 0; } + +static SIMPLE_DEV_PM_OPS(nm256_pm, nm256_suspend, nm256_resume); +#define NM256_PM_OPS &nm256_pm +#else +#define NM256_PM_OPS NULL #endif /* CONFIG_PM */ static int snd_nm256_free(struct nm256 *chip) @@ -1747,10 +1754,9 @@ static struct pci_driver nm256_driver = { .id_table = snd_nm256_ids, .probe = snd_nm256_probe, .remove = __devexit_p(snd_nm256_remove), -#ifdef CONFIG_PM - .suspend = nm256_suspend, - .resume = nm256_resume, -#endif + .driver = { + .pm = NM256_PM_OPS, + }, }; module_pci_driver(nm256_driver); diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index 610275bfbaeb..37520a2b4dcf 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -873,8 +873,9 @@ static struct pci_driver oxygen_driver = { .probe = generic_oxygen_probe, .remove = __devexit_p(oxygen_pci_remove), #ifdef CONFIG_PM - .suspend = oxygen_pci_suspend, - .resume = oxygen_pci_resume, + .driver = { + .pm = &oxygen_pci_pm, + }, #endif }; diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index f53897a708b4..7112a89fb8bd 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -162,8 +162,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, ); void oxygen_pci_remove(struct pci_dev *pci); #ifdef CONFIG_PM -int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state); -int oxygen_pci_resume(struct pci_dev *pci); +extern const struct dev_pm_ops oxygen_pci_pm; #endif void oxygen_pci_shutdown(struct pci_dev *pci); diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 92e2d67f16a1..ab8738e21ad1 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -727,9 +727,10 @@ void oxygen_pci_remove(struct pci_dev *pci) EXPORT_SYMBOL(oxygen_pci_remove); #ifdef CONFIG_PM -int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state) +static int oxygen_pci_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct oxygen *chip = card->private_data; unsigned int i, saved_interrupt_mask; @@ -756,10 +757,9 @@ int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -EXPORT_SYMBOL(oxygen_pci_suspend); static const u32 registers_to_restore[OXYGEN_IO_SIZE / 32] = { 0xffffffff, 0x00ff077f, 0x00011d08, 0x007f00ff, @@ -787,9 +787,10 @@ static void oxygen_restore_ac97(struct oxygen *chip, unsigned int codec) chip->saved_ac97_registers[codec][i]); } -int oxygen_pci_resume(struct pci_dev *pci) +static int oxygen_pci_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct oxygen *chip = card->private_data; unsigned int i; @@ -820,7 +821,9 @@ int oxygen_pci_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } -EXPORT_SYMBOL(oxygen_pci_resume); + +SIMPLE_DEV_PM_OPS(oxygen_pci_pm, oxygen_pci_suspend, oxygen_pci_resume); +EXPORT_SYMBOL(oxygen_pci_pm); #endif /* CONFIG_PM */ void oxygen_pci_shutdown(struct pci_dev *pci) diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index 19962c6d38c3..d3b606b69f3b 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -94,8 +94,9 @@ static struct pci_driver xonar_driver = { .probe = xonar_probe, .remove = __devexit_p(oxygen_pci_remove), #ifdef CONFIG_PM - .suspend = oxygen_pci_suspend, - .resume = oxygen_pci_resume, + .driver = { + .pm = &oxygen_pci_pm, + }, #endif .shutdown = oxygen_pci_shutdown, }; diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index cbeb3f77350c..760ee467cd9a 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1151,9 +1151,10 @@ static void riptide_handleirq(unsigned long dev_id) } #ifdef CONFIG_PM -static int riptide_suspend(struct pci_dev *pci, pm_message_t state) +static int riptide_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_riptide *chip = card->private_data; chip->in_suspend = 1; @@ -1162,13 +1163,14 @@ static int riptide_suspend(struct pci_dev *pci, pm_message_t state) snd_ac97_suspend(chip->ac97); pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int riptide_resume(struct pci_dev *pci) +static int riptide_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_riptide *chip = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -1186,7 +1188,12 @@ static int riptide_resume(struct pci_dev *pci) chip->in_suspend = 0; return 0; } -#endif + +static SIMPLE_DEV_PM_OPS(riptide_pm, riptide_suspend, riptide_resume); +#define RIPTIDE_PM_OPS &riptide_pm +#else +#define RIPTIDE_PM_OPS NULL +#endif /* CONFIG_PM */ static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip) { @@ -2180,10 +2187,9 @@ static struct pci_driver driver = { .id_table = snd_riptide_ids, .probe = snd_card_riptide_probe, .remove = __devexit_p(snd_card_riptide_remove), -#ifdef CONFIG_PM - .suspend = riptide_suspend, - .resume = riptide_resume, -#endif + .driver = { + .pm = RIPTIDE_PM_OPS, + }, }; #ifdef SUPPORT_JOYSTICK diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index 1552642765d6..512434efcc31 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -1209,9 +1209,10 @@ static int sis_chip_init(struct sis7019 *sis) } #ifdef CONFIG_PM -static int sis_suspend(struct pci_dev *pci, pm_message_t state) +static int sis_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct sis7019 *sis = card->private_data; void __iomem *ioaddr = sis->ioaddr; int i; @@ -1241,13 +1242,14 @@ static int sis_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int sis_resume(struct pci_dev *pci) +static int sis_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct sis7019 *sis = card->private_data; void __iomem *ioaddr = sis->ioaddr; int i; @@ -1298,6 +1300,11 @@ error: snd_card_disconnect(card); return -EIO; } + +static SIMPLE_DEV_PM_OPS(sis_pm, sis_suspend, sis_resume); +#define SIS_PM_OPS &sis_pm +#else +#define SIS_PM_OPS NULL #endif /* CONFIG_PM */ static int sis_alloc_suspend(struct sis7019 *sis) @@ -1481,11 +1488,9 @@ static struct pci_driver sis7019_driver = { .id_table = snd_sis7019_ids, .probe = snd_sis7019_probe, .remove = __devexit_p(snd_sis7019_remove), - -#ifdef CONFIG_PM - .suspend = sis_suspend, - .resume = sis_resume, -#endif + .driver = { + .pm = SIS_PM_OPS, + }, }; module_pci_driver(sis7019_driver); diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index 611983ec7321..f61346a555bb 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -178,8 +178,9 @@ static struct pci_driver trident_driver = { .probe = snd_trident_probe, .remove = __devexit_p(snd_trident_remove), #ifdef CONFIG_PM - .suspend = snd_trident_suspend, - .resume = snd_trident_resume, + .driver = { + .pm = &snd_trident_pm, + }, #endif }; diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 61d3c0e8d4ce..b4430c093bad 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -3920,9 +3920,10 @@ static void snd_trident_clear_voices(struct snd_trident * trident, unsigned shor } #ifdef CONFIG_PM -int snd_trident_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_trident_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_trident *trident = card->private_data; trident->in_suspend = 1; @@ -3936,13 +3937,14 @@ int snd_trident_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -int snd_trident_resume(struct pci_dev *pci) +static int snd_trident_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_trident *trident = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -3979,4 +3981,6 @@ int snd_trident_resume(struct pci_dev *pci) trident->in_suspend = 0; return 0; } + +SIMPLE_DEV_PM_OPS(snd_trident_pm, snd_trident_suspend, snd_trident_resume); #endif /* CONFIG_PM */ diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index b5afab48943e..0eb7245dd362 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -2242,9 +2242,10 @@ static int snd_via82xx_chip_init(struct via82xx *chip) /* * power management */ -static int snd_via82xx_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_via82xx_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct via82xx *chip = card->private_data; int i; @@ -2265,13 +2266,14 @@ static int snd_via82xx_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_via82xx_resume(struct pci_dev *pci) +static int snd_via82xx_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct via82xx *chip = card->private_data; int i; @@ -2306,6 +2308,11 @@ static int snd_via82xx_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); +#define SND_VIA82XX_PM_OPS &snd_via82xx_pm +#else +#define SND_VIA82XX_PM_OPS NULL #endif /* CONFIG_PM */ static int snd_via82xx_free(struct via82xx *chip) @@ -2624,10 +2631,9 @@ static struct pci_driver via82xx_driver = { .id_table = snd_via82xx_ids, .probe = snd_via82xx_probe, .remove = __devexit_p(snd_via82xx_remove), -#ifdef CONFIG_PM - .suspend = snd_via82xx_suspend, - .resume = snd_via82xx_resume, -#endif + .driver = { + .pm = SND_VIA82XX_PM_OPS, + }, }; module_pci_driver(via82xx_driver); diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index 59fd47ed0a31..e886bc16999d 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -1023,9 +1023,10 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip) /* * power management */ -static int snd_via82xx_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_via82xx_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct via82xx_modem *chip = card->private_data; int i; @@ -1039,13 +1040,14 @@ static int snd_via82xx_suspend(struct pci_dev *pci, pm_message_t state) pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -static int snd_via82xx_resume(struct pci_dev *pci) +static int snd_via82xx_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct via82xx_modem *chip = card->private_data; int i; @@ -1069,6 +1071,11 @@ static int snd_via82xx_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +static SIMPLE_DEV_PM_OPS(snd_via82xx_pm, snd_via82xx_suspend, snd_via82xx_resume); +#define SND_VIA82XX_PM_OPS &snd_via82xx_pm +#else +#define SND_VIA82XX_PM_OPS NULL #endif /* CONFIG_PM */ static int snd_via82xx_free(struct via82xx_modem *chip) @@ -1228,10 +1235,9 @@ static struct pci_driver via82xx_modem_driver = { .id_table = snd_via82xx_modem_ids, .probe = snd_via82xx_probe, .remove = __devexit_p(snd_via82xx_remove), -#ifdef CONFIG_PM - .suspend = snd_via82xx_suspend, - .resume = snd_via82xx_resume, -#endif + .driver = { + .pm = SND_VIA82XX_PM_OPS, + }, }; module_pci_driver(via82xx_modem_driver); diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index 1ea1f656a5dc..b89e7a86e9d8 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -258,22 +258,24 @@ static void __devexit snd_vx222_remove(struct pci_dev *pci) } #ifdef CONFIG_PM -static int snd_vx222_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_vx222_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_vx222 *vx = card->private_data; int err; - err = snd_vx_suspend(&vx->core, state); + err = snd_vx_suspend(&vx->core); pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return err; } -static int snd_vx222_resume(struct pci_dev *pci) +static int snd_vx222_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_vx222 *vx = card->private_data; pci_set_power_state(pci, PCI_D0); @@ -287,6 +289,11 @@ static int snd_vx222_resume(struct pci_dev *pci) pci_set_master(pci); return snd_vx_resume(&vx->core); } + +static SIMPLE_DEV_PM_OPS(snd_vx222_pm, snd_vx222_suspend, snd_vx222_resume); +#define SND_VX222_PM_OPS &snd_vx222_pm +#else +#define SND_VX222_PM_OPS NULL #endif static struct pci_driver vx222_driver = { @@ -294,10 +301,9 @@ static struct pci_driver vx222_driver = { .id_table = snd_vx222_ids, .probe = snd_vx222_probe, .remove = __devexit_p(snd_vx222_remove), -#ifdef CONFIG_PM - .suspend = snd_vx222_suspend, - .resume = snd_vx222_resume, -#endif + .driver = { + .pm = SND_VX222_PM_OPS, + }, }; module_pci_driver(vx222_driver); diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 9a1d01d653a7..7e20ddb9123a 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -356,8 +356,9 @@ static struct pci_driver ymfpci_driver = { .probe = snd_card_ymfpci_probe, .remove = __devexit_p(snd_card_ymfpci_remove), #ifdef CONFIG_PM - .suspend = snd_ymfpci_suspend, - .resume = snd_ymfpci_resume, + .driver = { + .pm = &snd_ymfpci_pm, + }, #endif }; diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index a8159b81e9c4..c706901d6ff6 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -2302,9 +2302,10 @@ static int saved_regs_index[] = { }; #define YDSXGR_NUM_SAVED_REGS ARRAY_SIZE(saved_regs_index) -int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state) +static int snd_ymfpci_suspend(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ymfpci *chip = card->private_data; unsigned int i; @@ -2326,13 +2327,14 @@ int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state) snd_ymfpci_disable_dsp(chip); pci_disable_device(pci); pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); + pci_set_power_state(pci, PCI_D3hot); return 0; } -int snd_ymfpci_resume(struct pci_dev *pci) +static int snd_ymfpci_resume(struct device *dev) { - struct snd_card *card = pci_get_drvdata(pci); + struct pci_dev *pci = to_pci_dev(dev); + struct snd_card *card = dev_get_drvdata(dev); struct snd_ymfpci *chip = card->private_data; unsigned int i; @@ -2370,6 +2372,8 @@ int snd_ymfpci_resume(struct pci_dev *pci) snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } + +SIMPLE_DEV_PM_OPS(snd_ymfpci_pm, snd_ymfpci_suspend, snd_ymfpci_resume); #endif /* CONFIG_PM */ int __devinit snd_ymfpci_create(struct snd_card *card, diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index 512f0b472375..8f9350475c7b 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -260,7 +260,7 @@ static int vxp_suspend(struct pcmcia_device *link) snd_printdd(KERN_DEBUG "SUSPEND\n"); if (chip) { snd_printdd(KERN_DEBUG "snd_vx_suspend calling\n"); - snd_vx_suspend(chip, PMSG_SUSPEND); + snd_vx_suspend(chip); } return 0; -- cgit v1.2.3 From 81fcb170852d58d7ebd8101a8ef970c82056426e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Jul 2012 16:37:05 +0200 Subject: ALSA: Move some headers to local directories from include/sound This is a bit clean up of public sound header directory. Some header files in include/sound aren't really necessary to be located there but can be moved to their local directories gracefully. Signed-off-by: Takashi Iwai --- include/sound/cs46xx.h | 1744 ------------------------------ include/sound/cs46xx_dsp_scb_types.h | 1213 --------------------- include/sound/cs46xx_dsp_spos.h | 234 ---- include/sound/cs46xx_dsp_task_types.h | 252 ----- include/sound/trident.h | 444 -------- include/sound/ymfpci.h | 389 ------- sound/pci/cs46xx/cs46xx.c | 2 +- sound/pci/cs46xx/cs46xx.h | 1744 ++++++++++++++++++++++++++++++ sound/pci/cs46xx/cs46xx_dsp_scb_types.h | 1213 +++++++++++++++++++++ sound/pci/cs46xx/cs46xx_dsp_spos.h | 234 ++++ sound/pci/cs46xx/cs46xx_dsp_task_types.h | 252 +++++ sound/pci/cs46xx/cs46xx_lib.c | 2 +- sound/pci/cs46xx/dsp_spos.c | 2 +- sound/pci/cs46xx/dsp_spos_scb_lib.c | 2 +- sound/pci/trident/trident.c | 2 +- sound/pci/trident/trident.h | 444 ++++++++ sound/pci/trident/trident_main.c | 2 +- sound/pci/trident/trident_memory.c | 2 +- sound/pci/ymfpci/ymfpci.c | 2 +- sound/pci/ymfpci/ymfpci.h | 389 +++++++ sound/pci/ymfpci/ymfpci_main.c | 2 +- 21 files changed, 4285 insertions(+), 4285 deletions(-) delete mode 100644 include/sound/cs46xx.h delete mode 100644 include/sound/cs46xx_dsp_scb_types.h delete mode 100644 include/sound/cs46xx_dsp_spos.h delete mode 100644 include/sound/cs46xx_dsp_task_types.h delete mode 100644 include/sound/trident.h delete mode 100644 include/sound/ymfpci.h create mode 100644 sound/pci/cs46xx/cs46xx.h create mode 100644 sound/pci/cs46xx/cs46xx_dsp_scb_types.h create mode 100644 sound/pci/cs46xx/cs46xx_dsp_spos.h create mode 100644 sound/pci/cs46xx/cs46xx_dsp_task_types.h create mode 100644 sound/pci/trident/trident.h create mode 100644 sound/pci/ymfpci/ymfpci.h (limited to 'include') diff --git a/include/sound/cs46xx.h b/include/sound/cs46xx.h deleted file mode 100644 index 34a2dd1614fa..000000000000 --- a/include/sound/cs46xx.h +++ /dev/null @@ -1,1744 +0,0 @@ -#ifndef __SOUND_CS46XX_H -#define __SOUND_CS46XX_H - -/* - * Copyright (c) by Jaroslav Kysela , - * Cirrus Logic, Inc. - * Definitions for Cirrus Logic CS46xx chips - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "pcm.h" -#include "pcm-indirect.h" -#include "rawmidi.h" -#include "ac97_codec.h" -#include "cs46xx_dsp_spos.h" - -/* - * Direct registers - */ - -/* - * The following define the offsets of the registers accessed via base address - * register zero on the CS46xx part. - */ -#define BA0_HISR 0x00000000 -#define BA0_HSR0 0x00000004 -#define BA0_HICR 0x00000008 -#define BA0_DMSR 0x00000100 -#define BA0_HSAR 0x00000110 -#define BA0_HDAR 0x00000114 -#define BA0_HDMR 0x00000118 -#define BA0_HDCR 0x0000011C -#define BA0_PFMC 0x00000200 -#define BA0_PFCV1 0x00000204 -#define BA0_PFCV2 0x00000208 -#define BA0_PCICFG00 0x00000300 -#define BA0_PCICFG04 0x00000304 -#define BA0_PCICFG08 0x00000308 -#define BA0_PCICFG0C 0x0000030C -#define BA0_PCICFG10 0x00000310 -#define BA0_PCICFG14 0x00000314 -#define BA0_PCICFG18 0x00000318 -#define BA0_PCICFG1C 0x0000031C -#define BA0_PCICFG20 0x00000320 -#define BA0_PCICFG24 0x00000324 -#define BA0_PCICFG28 0x00000328 -#define BA0_PCICFG2C 0x0000032C -#define BA0_PCICFG30 0x00000330 -#define BA0_PCICFG34 0x00000334 -#define BA0_PCICFG38 0x00000338 -#define BA0_PCICFG3C 0x0000033C -#define BA0_CLKCR1 0x00000400 -#define BA0_CLKCR2 0x00000404 -#define BA0_PLLM 0x00000408 -#define BA0_PLLCC 0x0000040C -#define BA0_FRR 0x00000410 -#define BA0_CFL1 0x00000414 -#define BA0_CFL2 0x00000418 -#define BA0_SERMC1 0x00000420 -#define BA0_SERMC2 0x00000424 -#define BA0_SERC1 0x00000428 -#define BA0_SERC2 0x0000042C -#define BA0_SERC3 0x00000430 -#define BA0_SERC4 0x00000434 -#define BA0_SERC5 0x00000438 -#define BA0_SERBSP 0x0000043C -#define BA0_SERBST 0x00000440 -#define BA0_SERBCM 0x00000444 -#define BA0_SERBAD 0x00000448 -#define BA0_SERBCF 0x0000044C -#define BA0_SERBWP 0x00000450 -#define BA0_SERBRP 0x00000454 -#ifndef NO_CS4612 -#define BA0_ASER_FADDR 0x00000458 -#endif -#define BA0_ACCTL 0x00000460 -#define BA0_ACSTS 0x00000464 -#define BA0_ACOSV 0x00000468 -#define BA0_ACCAD 0x0000046C -#define BA0_ACCDA 0x00000470 -#define BA0_ACISV 0x00000474 -#define BA0_ACSAD 0x00000478 -#define BA0_ACSDA 0x0000047C -#define BA0_JSPT 0x00000480 -#define BA0_JSCTL 0x00000484 -#define BA0_JSC1 0x00000488 -#define BA0_JSC2 0x0000048C -#define BA0_MIDCR 0x00000490 -#define BA0_MIDSR 0x00000494 -#define BA0_MIDWP 0x00000498 -#define BA0_MIDRP 0x0000049C -#define BA0_JSIO 0x000004A0 -#ifndef NO_CS4612 -#define BA0_ASER_MASTER 0x000004A4 -#endif -#define BA0_CFGI 0x000004B0 -#define BA0_SSVID 0x000004B4 -#define BA0_GPIOR 0x000004B8 -#ifndef NO_CS4612 -#define BA0_EGPIODR 0x000004BC -#define BA0_EGPIOPTR 0x000004C0 -#define BA0_EGPIOTR 0x000004C4 -#define BA0_EGPIOWR 0x000004C8 -#define BA0_EGPIOSR 0x000004CC -#define BA0_SERC6 0x000004D0 -#define BA0_SERC7 0x000004D4 -#define BA0_SERACC 0x000004D8 -#define BA0_ACCTL2 0x000004E0 -#define BA0_ACSTS2 0x000004E4 -#define BA0_ACOSV2 0x000004E8 -#define BA0_ACCAD2 0x000004EC -#define BA0_ACCDA2 0x000004F0 -#define BA0_ACISV2 0x000004F4 -#define BA0_ACSAD2 0x000004F8 -#define BA0_ACSDA2 0x000004FC -#define BA0_IOTAC0 0x00000500 -#define BA0_IOTAC1 0x00000504 -#define BA0_IOTAC2 0x00000508 -#define BA0_IOTAC3 0x0000050C -#define BA0_IOTAC4 0x00000510 -#define BA0_IOTAC5 0x00000514 -#define BA0_IOTAC6 0x00000518 -#define BA0_IOTAC7 0x0000051C -#define BA0_IOTAC8 0x00000520 -#define BA0_IOTAC9 0x00000524 -#define BA0_IOTAC10 0x00000528 -#define BA0_IOTAC11 0x0000052C -#define BA0_IOTFR0 0x00000540 -#define BA0_IOTFR1 0x00000544 -#define BA0_IOTFR2 0x00000548 -#define BA0_IOTFR3 0x0000054C -#define BA0_IOTFR4 0x00000550 -#define BA0_IOTFR5 0x00000554 -#define BA0_IOTFR6 0x00000558 -#define BA0_IOTFR7 0x0000055C -#define BA0_IOTFIFO 0x00000580 -#define BA0_IOTRRD 0x00000584 -#define BA0_IOTFP 0x00000588 -#define BA0_IOTCR 0x0000058C -#define BA0_DPCID 0x00000590 -#define BA0_DPCIA 0x00000594 -#define BA0_DPCIC 0x00000598 -#define BA0_PCPCIR 0x00000600 -#define BA0_PCPCIG 0x00000604 -#define BA0_PCPCIEN 0x00000608 -#define BA0_EPCIPMC 0x00000610 -#endif - -/* - * The following define the offsets of the registers and memories accessed via - * base address register one on the CS46xx part. - */ -#define BA1_SP_DMEM0 0x00000000 -#define BA1_SP_DMEM1 0x00010000 -#define BA1_SP_PMEM 0x00020000 -#define BA1_SP_REG 0x00030000 -#define BA1_SPCR 0x00030000 -#define BA1_DREG 0x00030004 -#define BA1_DSRWP 0x00030008 -#define BA1_TWPR 0x0003000C -#define BA1_SPWR 0x00030010 -#define BA1_SPIR 0x00030014 -#define BA1_FGR1 0x00030020 -#define BA1_SPCS 0x00030028 -#define BA1_SDSR 0x0003002C -#define BA1_FRMT 0x00030030 -#define BA1_FRCC 0x00030034 -#define BA1_FRSC 0x00030038 -#define BA1_OMNI_MEM 0x000E0000 - - -/* - * The following defines are for the flags in the host interrupt status - * register. - */ -#define HISR_VC_MASK 0x0000FFFF -#define HISR_VC0 0x00000001 -#define HISR_VC1 0x00000002 -#define HISR_VC2 0x00000004 -#define HISR_VC3 0x00000008 -#define HISR_VC4 0x00000010 -#define HISR_VC5 0x00000020 -#define HISR_VC6 0x00000040 -#define HISR_VC7 0x00000080 -#define HISR_VC8 0x00000100 -#define HISR_VC9 0x00000200 -#define HISR_VC10 0x00000400 -#define HISR_VC11 0x00000800 -#define HISR_VC12 0x00001000 -#define HISR_VC13 0x00002000 -#define HISR_VC14 0x00004000 -#define HISR_VC15 0x00008000 -#define HISR_INT0 0x00010000 -#define HISR_INT1 0x00020000 -#define HISR_DMAI 0x00040000 -#define HISR_FROVR 0x00080000 -#define HISR_MIDI 0x00100000 -#ifdef NO_CS4612 -#define HISR_RESERVED 0x0FE00000 -#else -#define HISR_SBINT 0x00200000 -#define HISR_RESERVED 0x0FC00000 -#endif -#define HISR_H0P 0x40000000 -#define HISR_INTENA 0x80000000 - -/* - * The following defines are for the flags in the host signal register 0. - */ -#define HSR0_VC_MASK 0xFFFFFFFF -#define HSR0_VC16 0x00000001 -#define HSR0_VC17 0x00000002 -#define HSR0_VC18 0x00000004 -#define HSR0_VC19 0x00000008 -#define HSR0_VC20 0x00000010 -#define HSR0_VC21 0x00000020 -#define HSR0_VC22 0x00000040 -#define HSR0_VC23 0x00000080 -#define HSR0_VC24 0x00000100 -#define HSR0_VC25 0x00000200 -#define HSR0_VC26 0x00000400 -#define HSR0_VC27 0x00000800 -#define HSR0_VC28 0x00001000 -#define HSR0_VC29 0x00002000 -#define HSR0_VC30 0x00004000 -#define HSR0_VC31 0x00008000 -#define HSR0_VC32 0x00010000 -#define HSR0_VC33 0x00020000 -#define HSR0_VC34 0x00040000 -#define HSR0_VC35 0x00080000 -#define HSR0_VC36 0x00100000 -#define HSR0_VC37 0x00200000 -#define HSR0_VC38 0x00400000 -#define HSR0_VC39 0x00800000 -#define HSR0_VC40 0x01000000 -#define HSR0_VC41 0x02000000 -#define HSR0_VC42 0x04000000 -#define HSR0_VC43 0x08000000 -#define HSR0_VC44 0x10000000 -#define HSR0_VC45 0x20000000 -#define HSR0_VC46 0x40000000 -#define HSR0_VC47 0x80000000 - -/* - * The following defines are for the flags in the host interrupt control - * register. - */ -#define HICR_IEV 0x00000001 -#define HICR_CHGM 0x00000002 - -/* - * The following defines are for the flags in the DMA status register. - */ -#define DMSR_HP 0x00000001 -#define DMSR_HR 0x00000002 -#define DMSR_SP 0x00000004 -#define DMSR_SR 0x00000008 - -/* - * The following defines are for the flags in the host DMA source address - * register. - */ -#define HSAR_HOST_ADDR_MASK 0xFFFFFFFF -#define HSAR_DSP_ADDR_MASK 0x0000FFFF -#define HSAR_MEMID_MASK 0x000F0000 -#define HSAR_MEMID_SP_DMEM0 0x00000000 -#define HSAR_MEMID_SP_DMEM1 0x00010000 -#define HSAR_MEMID_SP_PMEM 0x00020000 -#define HSAR_MEMID_SP_DEBUG 0x00030000 -#define HSAR_MEMID_OMNI_MEM 0x000E0000 -#define HSAR_END 0x40000000 -#define HSAR_ERR 0x80000000 - -/* - * The following defines are for the flags in the host DMA destination address - * register. - */ -#define HDAR_HOST_ADDR_MASK 0xFFFFFFFF -#define HDAR_DSP_ADDR_MASK 0x0000FFFF -#define HDAR_MEMID_MASK 0x000F0000 -#define HDAR_MEMID_SP_DMEM0 0x00000000 -#define HDAR_MEMID_SP_DMEM1 0x00010000 -#define HDAR_MEMID_SP_PMEM 0x00020000 -#define HDAR_MEMID_SP_DEBUG 0x00030000 -#define HDAR_MEMID_OMNI_MEM 0x000E0000 -#define HDAR_END 0x40000000 -#define HDAR_ERR 0x80000000 - -/* - * The following defines are for the flags in the host DMA control register. - */ -#define HDMR_AC_MASK 0x0000F000 -#define HDMR_AC_8_16 0x00001000 -#define HDMR_AC_M_S 0x00002000 -#define HDMR_AC_B_L 0x00004000 -#define HDMR_AC_S_U 0x00008000 - -/* - * The following defines are for the flags in the host DMA control register. - */ -#define HDCR_COUNT_MASK 0x000003FF -#define HDCR_DONE 0x00004000 -#define HDCR_OPT 0x00008000 -#define HDCR_WBD 0x00400000 -#define HDCR_WBS 0x00800000 -#define HDCR_DMS_MASK 0x07000000 -#define HDCR_DMS_LINEAR 0x00000000 -#define HDCR_DMS_16_DWORDS 0x01000000 -#define HDCR_DMS_32_DWORDS 0x02000000 -#define HDCR_DMS_64_DWORDS 0x03000000 -#define HDCR_DMS_128_DWORDS 0x04000000 -#define HDCR_DMS_256_DWORDS 0x05000000 -#define HDCR_DMS_512_DWORDS 0x06000000 -#define HDCR_DMS_1024_DWORDS 0x07000000 -#define HDCR_DH 0x08000000 -#define HDCR_SMS_MASK 0x70000000 -#define HDCR_SMS_LINEAR 0x00000000 -#define HDCR_SMS_16_DWORDS 0x10000000 -#define HDCR_SMS_32_DWORDS 0x20000000 -#define HDCR_SMS_64_DWORDS 0x30000000 -#define HDCR_SMS_128_DWORDS 0x40000000 -#define HDCR_SMS_256_DWORDS 0x50000000 -#define HDCR_SMS_512_DWORDS 0x60000000 -#define HDCR_SMS_1024_DWORDS 0x70000000 -#define HDCR_SH 0x80000000 -#define HDCR_COUNT_SHIFT 0 - -/* - * The following defines are for the flags in the performance monitor control - * register. - */ -#define PFMC_C1SS_MASK 0x0000001F -#define PFMC_C1EV 0x00000020 -#define PFMC_C1RS 0x00008000 -#define PFMC_C2SS_MASK 0x001F0000 -#define PFMC_C2EV 0x00200000 -#define PFMC_C2RS 0x80000000 -#define PFMC_C1SS_SHIFT 0 -#define PFMC_C2SS_SHIFT 16 -#define PFMC_BUS_GRANT 0 -#define PFMC_GRANT_AFTER_REQ 1 -#define PFMC_TRANSACTION 2 -#define PFMC_DWORD_TRANSFER 3 -#define PFMC_SLAVE_READ 4 -#define PFMC_SLAVE_WRITE 5 -#define PFMC_PREEMPTION 6 -#define PFMC_DISCONNECT_RETRY 7 -#define PFMC_INTERRUPT 8 -#define PFMC_BUS_OWNERSHIP 9 -#define PFMC_TRANSACTION_LAG 10 -#define PFMC_PCI_CLOCK 11 -#define PFMC_SERIAL_CLOCK 12 -#define PFMC_SP_CLOCK 13 - -/* - * The following defines are for the flags in the performance counter value 1 - * register. - */ -#define PFCV1_PC1V_MASK 0xFFFFFFFF -#define PFCV1_PC1V_SHIFT 0 - -/* - * The following defines are for the flags in the performance counter value 2 - * register. - */ -#define PFCV2_PC2V_MASK 0xFFFFFFFF -#define PFCV2_PC2V_SHIFT 0 - -/* - * The following defines are for the flags in the clock control register 1. - */ -#define CLKCR1_OSCS 0x00000001 -#define CLKCR1_OSCP 0x00000002 -#define CLKCR1_PLLSS_MASK 0x0000000C -#define CLKCR1_PLLSS_SERIAL 0x00000000 -#define CLKCR1_PLLSS_CRYSTAL 0x00000004 -#define CLKCR1_PLLSS_PCI 0x00000008 -#define CLKCR1_PLLSS_RESERVED 0x0000000C -#define CLKCR1_PLLP 0x00000010 -#define CLKCR1_SWCE 0x00000020 -#define CLKCR1_PLLOS 0x00000040 - -/* - * The following defines are for the flags in the clock control register 2. - */ -#define CLKCR2_PDIVS_MASK 0x0000000F -#define CLKCR2_PDIVS_1 0x00000001 -#define CLKCR2_PDIVS_2 0x00000002 -#define CLKCR2_PDIVS_4 0x00000004 -#define CLKCR2_PDIVS_7 0x00000007 -#define CLKCR2_PDIVS_8 0x00000008 -#define CLKCR2_PDIVS_16 0x00000000 - -/* - * The following defines are for the flags in the PLL multiplier register. - */ -#define PLLM_MASK 0x000000FF -#define PLLM_SHIFT 0 - -/* - * The following defines are for the flags in the PLL capacitor coefficient - * register. - */ -#define PLLCC_CDR_MASK 0x00000007 -#ifndef NO_CS4610 -#define PLLCC_CDR_240_350_MHZ 0x00000000 -#define PLLCC_CDR_184_265_MHZ 0x00000001 -#define PLLCC_CDR_144_205_MHZ 0x00000002 -#define PLLCC_CDR_111_160_MHZ 0x00000003 -#define PLLCC_CDR_87_123_MHZ 0x00000004 -#define PLLCC_CDR_67_96_MHZ 0x00000005 -#define PLLCC_CDR_52_74_MHZ 0x00000006 -#define PLLCC_CDR_45_58_MHZ 0x00000007 -#endif -#ifndef NO_CS4612 -#define PLLCC_CDR_271_398_MHZ 0x00000000 -#define PLLCC_CDR_227_330_MHZ 0x00000001 -#define PLLCC_CDR_167_239_MHZ 0x00000002 -#define PLLCC_CDR_150_215_MHZ 0x00000003 -#define PLLCC_CDR_107_154_MHZ 0x00000004 -#define PLLCC_CDR_98_140_MHZ 0x00000005 -#define PLLCC_CDR_73_104_MHZ 0x00000006 -#define PLLCC_CDR_63_90_MHZ 0x00000007 -#endif -#define PLLCC_LPF_MASK 0x000000F8 -#ifndef NO_CS4610 -#define PLLCC_LPF_23850_60000_KHZ 0x00000000 -#define PLLCC_LPF_7960_26290_KHZ 0x00000008 -#define PLLCC_LPF_4160_10980_KHZ 0x00000018 -#define PLLCC_LPF_1740_4580_KHZ 0x00000038 -#define PLLCC_LPF_724_1910_KHZ 0x00000078 -#define PLLCC_LPF_317_798_KHZ 0x000000F8 -#endif -#ifndef NO_CS4612 -#define PLLCC_LPF_25580_64530_KHZ 0x00000000 -#define PLLCC_LPF_14360_37270_KHZ 0x00000008 -#define PLLCC_LPF_6100_16020_KHZ 0x00000018 -#define PLLCC_LPF_2540_6690_KHZ 0x00000038 -#define PLLCC_LPF_1050_2780_KHZ 0x00000078 -#define PLLCC_LPF_450_1160_KHZ 0x000000F8 -#endif - -/* - * The following defines are for the flags in the feature reporting register. - */ -#define FRR_FAB_MASK 0x00000003 -#define FRR_MASK_MASK 0x0000001C -#ifdef NO_CS4612 -#define FRR_CFOP_MASK 0x000000E0 -#else -#define FRR_CFOP_MASK 0x00000FE0 -#endif -#define FRR_CFOP_NOT_DVD 0x00000020 -#define FRR_CFOP_A3D 0x00000040 -#define FRR_CFOP_128_PIN 0x00000080 -#ifndef NO_CS4612 -#define FRR_CFOP_CS4280 0x00000800 -#endif -#define FRR_FAB_SHIFT 0 -#define FRR_MASK_SHIFT 2 -#define FRR_CFOP_SHIFT 5 - -/* - * The following defines are for the flags in the configuration load 1 - * register. - */ -#define CFL1_CLOCK_SOURCE_MASK 0x00000003 -#define CFL1_CLOCK_SOURCE_CS423X 0x00000000 -#define CFL1_CLOCK_SOURCE_AC97 0x00000001 -#define CFL1_CLOCK_SOURCE_CRYSTAL 0x00000002 -#define CFL1_CLOCK_SOURCE_DUAL_AC97 0x00000003 -#define CFL1_VALID_DATA_MASK 0x000000FF - -/* - * The following defines are for the flags in the configuration load 2 - * register. - */ -#define CFL2_VALID_DATA_MASK 0x000000FF - -/* - * The following defines are for the flags in the serial port master control - * register 1. - */ -#define SERMC1_MSPE 0x00000001 -#define SERMC1_PTC_MASK 0x0000000E -#define SERMC1_PTC_CS423X 0x00000000 -#define SERMC1_PTC_AC97 0x00000002 -#define SERMC1_PTC_DAC 0x00000004 -#define SERMC1_PLB 0x00000010 -#define SERMC1_XLB 0x00000020 - -/* - * The following defines are for the flags in the serial port master control - * register 2. - */ -#define SERMC2_LROE 0x00000001 -#define SERMC2_MCOE 0x00000002 -#define SERMC2_MCDIV 0x00000004 - -/* - * The following defines are for the flags in the serial port 1 configuration - * register. - */ -#define SERC1_SO1EN 0x00000001 -#define SERC1_SO1F_MASK 0x0000000E -#define SERC1_SO1F_CS423X 0x00000000 -#define SERC1_SO1F_AC97 0x00000002 -#define SERC1_SO1F_DAC 0x00000004 -#define SERC1_SO1F_SPDIF 0x00000006 - -/* - * The following defines are for the flags in the serial port 2 configuration - * register. - */ -#define SERC2_SI1EN 0x00000001 -#define SERC2_SI1F_MASK 0x0000000E -#define SERC2_SI1F_CS423X 0x00000000 -#define SERC2_SI1F_AC97 0x00000002 -#define SERC2_SI1F_ADC 0x00000004 -#define SERC2_SI1F_SPDIF 0x00000006 - -/* - * The following defines are for the flags in the serial port 3 configuration - * register. - */ -#define SERC3_SO2EN 0x00000001 -#define SERC3_SO2F_MASK 0x00000006 -#define SERC3_SO2F_DAC 0x00000000 -#define SERC3_SO2F_SPDIF 0x00000002 - -/* - * The following defines are for the flags in the serial port 4 configuration - * register. - */ -#define SERC4_SO3EN 0x00000001 -#define SERC4_SO3F_MASK 0x00000006 -#define SERC4_SO3F_DAC 0x00000000 -#define SERC4_SO3F_SPDIF 0x00000002 - -/* - * The following defines are for the flags in the serial port 5 configuration - * register. - */ -#define SERC5_SI2EN 0x00000001 -#define SERC5_SI2F_MASK 0x00000006 -#define SERC5_SI2F_ADC 0x00000000 -#define SERC5_SI2F_SPDIF 0x00000002 - -/* - * The following defines are for the flags in the serial port backdoor sample - * pointer register. - */ -#define SERBSP_FSP_MASK 0x0000000F -#define SERBSP_FSP_SHIFT 0 - -/* - * The following defines are for the flags in the serial port backdoor status - * register. - */ -#define SERBST_RRDY 0x00000001 -#define SERBST_WBSY 0x00000002 - -/* - * The following defines are for the flags in the serial port backdoor command - * register. - */ -#define SERBCM_RDC 0x00000001 -#define SERBCM_WRC 0x00000002 - -/* - * The following defines are for the flags in the serial port backdoor address - * register. - */ -#ifdef NO_CS4612 -#define SERBAD_FAD_MASK 0x000000FF -#else -#define SERBAD_FAD_MASK 0x000001FF -#endif -#define SERBAD_FAD_SHIFT 0 - -/* - * The following defines are for the flags in the serial port backdoor - * configuration register. - */ -#define SERBCF_HBP 0x00000001 - -/* - * The following defines are for the flags in the serial port backdoor write - * port register. - */ -#define SERBWP_FWD_MASK 0x000FFFFF -#define SERBWP_FWD_SHIFT 0 - -/* - * The following defines are for the flags in the serial port backdoor read - * port register. - */ -#define SERBRP_FRD_MASK 0x000FFFFF -#define SERBRP_FRD_SHIFT 0 - -/* - * The following defines are for the flags in the async FIFO address register. - */ -#ifndef NO_CS4612 -#define ASER_FADDR_A1_MASK 0x000001FF -#define ASER_FADDR_EN1 0x00008000 -#define ASER_FADDR_A2_MASK 0x01FF0000 -#define ASER_FADDR_EN2 0x80000000 -#define ASER_FADDR_A1_SHIFT 0 -#define ASER_FADDR_A2_SHIFT 16 -#endif - -/* - * The following defines are for the flags in the AC97 control register. - */ -#define ACCTL_RSTN 0x00000001 -#define ACCTL_ESYN 0x00000002 -#define ACCTL_VFRM 0x00000004 -#define ACCTL_DCV 0x00000008 -#define ACCTL_CRW 0x00000010 -#define ACCTL_ASYN 0x00000020 -#ifndef NO_CS4612 -#define ACCTL_TC 0x00000040 -#endif - -/* - * The following defines are for the flags in the AC97 status register. - */ -#define ACSTS_CRDY 0x00000001 -#define ACSTS_VSTS 0x00000002 -#ifndef NO_CS4612 -#define ACSTS_WKUP 0x00000004 -#endif - -/* - * The following defines are for the flags in the AC97 output slot valid - * register. - */ -#define ACOSV_SLV3 0x00000001 -#define ACOSV_SLV4 0x00000002 -#define ACOSV_SLV5 0x00000004 -#define ACOSV_SLV6 0x00000008 -#define ACOSV_SLV7 0x00000010 -#define ACOSV_SLV8 0x00000020 -#define ACOSV_SLV9 0x00000040 -#define ACOSV_SLV10 0x00000080 -#define ACOSV_SLV11 0x00000100 -#define ACOSV_SLV12 0x00000200 - -/* - * The following defines are for the flags in the AC97 command address - * register. - */ -#define ACCAD_CI_MASK 0x0000007F -#define ACCAD_CI_SHIFT 0 - -/* - * The following defines are for the flags in the AC97 command data register. - */ -#define ACCDA_CD_MASK 0x0000FFFF -#define ACCDA_CD_SHIFT 0 - -/* - * The following defines are for the flags in the AC97 input slot valid - * register. - */ -#define ACISV_ISV3 0x00000001 -#define ACISV_ISV4 0x00000002 -#define ACISV_ISV5 0x00000004 -#define ACISV_ISV6 0x00000008 -#define ACISV_ISV7 0x00000010 -#define ACISV_ISV8 0x00000020 -#define ACISV_ISV9 0x00000040 -#define ACISV_ISV10 0x00000080 -#define ACISV_ISV11 0x00000100 -#define ACISV_ISV12 0x00000200 - -/* - * The following defines are for the flags in the AC97 status address - * register. - */ -#define ACSAD_SI_MASK 0x0000007F -#define ACSAD_SI_SHIFT 0 - -/* - * The following defines are for the flags in the AC97 status data register. - */ -#define ACSDA_SD_MASK 0x0000FFFF -#define ACSDA_SD_SHIFT 0 - -/* - * The following defines are for the flags in the joystick poll/trigger - * register. - */ -#define JSPT_CAX 0x00000001 -#define JSPT_CAY 0x00000002 -#define JSPT_CBX 0x00000004 -#define JSPT_CBY 0x00000008 -#define JSPT_BA1 0x00000010 -#define JSPT_BA2 0x00000020 -#define JSPT_BB1 0x00000040 -#define JSPT_BB2 0x00000080 - -/* - * The following defines are for the flags in the joystick control register. - */ -#define JSCTL_SP_MASK 0x00000003 -#define JSCTL_SP_SLOW 0x00000000 -#define JSCTL_SP_MEDIUM_SLOW 0x00000001 -#define JSCTL_SP_MEDIUM_FAST 0x00000002 -#define JSCTL_SP_FAST 0x00000003 -#define JSCTL_ARE 0x00000004 - -/* - * The following defines are for the flags in the joystick coordinate pair 1 - * readback register. - */ -#define JSC1_Y1V_MASK 0x0000FFFF -#define JSC1_X1V_MASK 0xFFFF0000 -#define JSC1_Y1V_SHIFT 0 -#define JSC1_X1V_SHIFT 16 - -/* - * The following defines are for the flags in the joystick coordinate pair 2 - * readback register. - */ -#define JSC2_Y2V_MASK 0x0000FFFF -#define JSC2_X2V_MASK 0xFFFF0000 -#define JSC2_Y2V_SHIFT 0 -#define JSC2_X2V_SHIFT 16 - -/* - * The following defines are for the flags in the MIDI control register. - */ -#define MIDCR_TXE 0x00000001 /* Enable transmitting. */ -#define MIDCR_RXE 0x00000002 /* Enable receiving. */ -#define MIDCR_RIE 0x00000004 /* Interrupt upon tx ready. */ -#define MIDCR_TIE 0x00000008 /* Interrupt upon rx ready. */ -#define MIDCR_MLB 0x00000010 /* Enable midi loopback. */ -#define MIDCR_MRST 0x00000020 /* Reset interface. */ - -/* - * The following defines are for the flags in the MIDI status register. - */ -#define MIDSR_TBF 0x00000001 /* Tx FIFO is full. */ -#define MIDSR_RBE 0x00000002 /* Rx FIFO is empty. */ - -/* - * The following defines are for the flags in the MIDI write port register. - */ -#define MIDWP_MWD_MASK 0x000000FF -#define MIDWP_MWD_SHIFT 0 - -/* - * The following defines are for the flags in the MIDI read port register. - */ -#define MIDRP_MRD_MASK 0x000000FF -#define MIDRP_MRD_SHIFT 0 - -/* - * The following defines are for the flags in the joystick GPIO register. - */ -#define JSIO_DAX 0x00000001 -#define JSIO_DAY 0x00000002 -#define JSIO_DBX 0x00000004 -#define JSIO_DBY 0x00000008 -#define JSIO_AXOE 0x00000010 -#define JSIO_AYOE 0x00000020 -#define JSIO_BXOE 0x00000040 -#define JSIO_BYOE 0x00000080 - -/* - * The following defines are for the flags in the master async/sync serial - * port enable register. - */ -#ifndef NO_CS4612 -#define ASER_MASTER_ME 0x00000001 -#endif - -/* - * The following defines are for the flags in the configuration interface - * register. - */ -#define CFGI_CLK 0x00000001 -#define CFGI_DOUT 0x00000002 -#define CFGI_DIN_EEN 0x00000004 -#define CFGI_EELD 0x00000008 - -/* - * The following defines are for the flags in the subsystem ID and vendor ID - * register. - */ -#define SSVID_VID_MASK 0x0000FFFF -#define SSVID_SID_MASK 0xFFFF0000 -#define SSVID_VID_SHIFT 0 -#define SSVID_SID_SHIFT 16 - -/* - * The following defines are for the flags in the GPIO pin interface register. - */ -#define GPIOR_VOLDN 0x00000001 -#define GPIOR_VOLUP 0x00000002 -#define GPIOR_SI2D 0x00000004 -#define GPIOR_SI2OE 0x00000008 - -/* - * The following defines are for the flags in the extended GPIO pin direction - * register. - */ -#ifndef NO_CS4612 -#define EGPIODR_GPOE0 0x00000001 -#define EGPIODR_GPOE1 0x00000002 -#define EGPIODR_GPOE2 0x00000004 -#define EGPIODR_GPOE3 0x00000008 -#define EGPIODR_GPOE4 0x00000010 -#define EGPIODR_GPOE5 0x00000020 -#define EGPIODR_GPOE6 0x00000040 -#define EGPIODR_GPOE7 0x00000080 -#define EGPIODR_GPOE8 0x00000100 -#endif - -/* - * The following defines are for the flags in the extended GPIO pin polarity/ - * type register. - */ -#ifndef NO_CS4612 -#define EGPIOPTR_GPPT0 0x00000001 -#define EGPIOPTR_GPPT1 0x00000002 -#define EGPIOPTR_GPPT2 0x00000004 -#define EGPIOPTR_GPPT3 0x00000008 -#define EGPIOPTR_GPPT4 0x00000010 -#define EGPIOPTR_GPPT5 0x00000020 -#define EGPIOPTR_GPPT6 0x00000040 -#define EGPIOPTR_GPPT7 0x00000080 -#define EGPIOPTR_GPPT8 0x00000100 -#endif - -/* - * The following defines are for the flags in the extended GPIO pin sticky - * register. - */ -#ifndef NO_CS4612 -#define EGPIOTR_GPS0 0x00000001 -#define EGPIOTR_GPS1 0x00000002 -#define EGPIOTR_GPS2 0x00000004 -#define EGPIOTR_GPS3 0x00000008 -#define EGPIOTR_GPS4 0x00000010 -#define EGPIOTR_GPS5 0x00000020 -#define EGPIOTR_GPS6 0x00000040 -#define EGPIOTR_GPS7 0x00000080 -#define EGPIOTR_GPS8 0x00000100 -#endif - -/* - * The following defines are for the flags in the extended GPIO ping wakeup - * register. - */ -#ifndef NO_CS4612 -#define EGPIOWR_GPW0 0x00000001 -#define EGPIOWR_GPW1 0x00000002 -#define EGPIOWR_GPW2 0x00000004 -#define EGPIOWR_GPW3 0x00000008 -#define EGPIOWR_GPW4 0x00000010 -#define EGPIOWR_GPW5 0x00000020 -#define EGPIOWR_GPW6 0x00000040 -#define EGPIOWR_GPW7 0x00000080 -#define EGPIOWR_GPW8 0x00000100 -#endif - -/* - * The following defines are for the flags in the extended GPIO pin status - * register. - */ -#ifndef NO_CS4612 -#define EGPIOSR_GPS0 0x00000001 -#define EGPIOSR_GPS1 0x00000002 -#define EGPIOSR_GPS2 0x00000004 -#define EGPIOSR_GPS3 0x00000008 -#define EGPIOSR_GPS4 0x00000010 -#define EGPIOSR_GPS5 0x00000020 -#define EGPIOSR_GPS6 0x00000040 -#define EGPIOSR_GPS7 0x00000080 -#define EGPIOSR_GPS8 0x00000100 -#endif - -/* - * The following defines are for the flags in the serial port 6 configuration - * register. - */ -#ifndef NO_CS4612 -#define SERC6_ASDO2EN 0x00000001 -#endif - -/* - * The following defines are for the flags in the serial port 7 configuration - * register. - */ -#ifndef NO_CS4612 -#define SERC7_ASDI2EN 0x00000001 -#define SERC7_POSILB 0x00000002 -#define SERC7_SIPOLB 0x00000004 -#define SERC7_SOSILB 0x00000008 -#define SERC7_SISOLB 0x00000010 -#endif - -/* - * The following defines are for the flags in the serial port AC link - * configuration register. - */ -#ifndef NO_CS4612 -#define SERACC_CHIP_TYPE_MASK 0x00000001 -#define SERACC_CHIP_TYPE_1_03 0x00000000 -#define SERACC_CHIP_TYPE_2_0 0x00000001 -#define SERACC_TWO_CODECS 0x00000002 -#define SERACC_MDM 0x00000004 -#define SERACC_HSP 0x00000008 -#define SERACC_ODT 0x00000010 /* only CS4630 */ -#endif - -/* - * The following defines are for the flags in the AC97 control register 2. - */ -#ifndef NO_CS4612 -#define ACCTL2_RSTN 0x00000001 -#define ACCTL2_ESYN 0x00000002 -#define ACCTL2_VFRM 0x00000004 -#define ACCTL2_DCV 0x00000008 -#define ACCTL2_CRW 0x00000010 -#define ACCTL2_ASYN 0x00000020 -#endif - -/* - * The following defines are for the flags in the AC97 status register 2. - */ -#ifndef NO_CS4612 -#define ACSTS2_CRDY 0x00000001 -#define ACSTS2_VSTS 0x00000002 -#endif - -/* - * The following defines are for the flags in the AC97 output slot valid - * register 2. - */ -#ifndef NO_CS4612 -#define ACOSV2_SLV3 0x00000001 -#define ACOSV2_SLV4 0x00000002 -#define ACOSV2_SLV5 0x00000004 -#define ACOSV2_SLV6 0x00000008 -#define ACOSV2_SLV7 0x00000010 -#define ACOSV2_SLV8 0x00000020 -#define ACOSV2_SLV9 0x00000040 -#define ACOSV2_SLV10 0x00000080 -#define ACOSV2_SLV11 0x00000100 -#define ACOSV2_SLV12 0x00000200 -#endif - -/* - * The following defines are for the flags in the AC97 command address - * register 2. - */ -#ifndef NO_CS4612 -#define ACCAD2_CI_MASK 0x0000007F -#define ACCAD2_CI_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the AC97 command data register - * 2. - */ -#ifndef NO_CS4612 -#define ACCDA2_CD_MASK 0x0000FFFF -#define ACCDA2_CD_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the AC97 input slot valid - * register 2. - */ -#ifndef NO_CS4612 -#define ACISV2_ISV3 0x00000001 -#define ACISV2_ISV4 0x00000002 -#define ACISV2_ISV5 0x00000004 -#define ACISV2_ISV6 0x00000008 -#define ACISV2_ISV7 0x00000010 -#define ACISV2_ISV8 0x00000020 -#define ACISV2_ISV9 0x00000040 -#define ACISV2_ISV10 0x00000080 -#define ACISV2_ISV11 0x00000100 -#define ACISV2_ISV12 0x00000200 -#endif - -/* - * The following defines are for the flags in the AC97 status address - * register 2. - */ -#ifndef NO_CS4612 -#define ACSAD2_SI_MASK 0x0000007F -#define ACSAD2_SI_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the AC97 status data register 2. - */ -#ifndef NO_CS4612 -#define ACSDA2_SD_MASK 0x0000FFFF -#define ACSDA2_SD_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the I/O trap address and control - * registers (all 12). - */ -#ifndef NO_CS4612 -#define IOTAC_SA_MASK 0x0000FFFF -#define IOTAC_MSK_MASK 0x000F0000 -#define IOTAC_IODC_MASK 0x06000000 -#define IOTAC_IODC_16_BIT 0x00000000 -#define IOTAC_IODC_10_BIT 0x02000000 -#define IOTAC_IODC_12_BIT 0x04000000 -#define IOTAC_WSPI 0x08000000 -#define IOTAC_RSPI 0x10000000 -#define IOTAC_WSE 0x20000000 -#define IOTAC_WE 0x40000000 -#define IOTAC_RE 0x80000000 -#define IOTAC_SA_SHIFT 0 -#define IOTAC_MSK_SHIFT 16 -#endif - -/* - * The following defines are for the flags in the I/O trap fast read registers - * (all 8). - */ -#ifndef NO_CS4612 -#define IOTFR_D_MASK 0x0000FFFF -#define IOTFR_A_MASK 0x000F0000 -#define IOTFR_R_MASK 0x0F000000 -#define IOTFR_ALL 0x40000000 -#define IOTFR_VL 0x80000000 -#define IOTFR_D_SHIFT 0 -#define IOTFR_A_SHIFT 16 -#define IOTFR_R_SHIFT 24 -#endif - -/* - * The following defines are for the flags in the I/O trap FIFO register. - */ -#ifndef NO_CS4612 -#define IOTFIFO_BA_MASK 0x00003FFF -#define IOTFIFO_S_MASK 0x00FF0000 -#define IOTFIFO_OF 0x40000000 -#define IOTFIFO_SPIOF 0x80000000 -#define IOTFIFO_BA_SHIFT 0 -#define IOTFIFO_S_SHIFT 16 -#endif - -/* - * The following defines are for the flags in the I/O trap retry read data - * register. - */ -#ifndef NO_CS4612 -#define IOTRRD_D_MASK 0x0000FFFF -#define IOTRRD_RDV 0x80000000 -#define IOTRRD_D_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the I/O trap FIFO pointer - * register. - */ -#ifndef NO_CS4612 -#define IOTFP_CA_MASK 0x00003FFF -#define IOTFP_PA_MASK 0x3FFF0000 -#define IOTFP_CA_SHIFT 0 -#define IOTFP_PA_SHIFT 16 -#endif - -/* - * The following defines are for the flags in the I/O trap control register. - */ -#ifndef NO_CS4612 -#define IOTCR_ITD 0x00000001 -#define IOTCR_HRV 0x00000002 -#define IOTCR_SRV 0x00000004 -#define IOTCR_DTI 0x00000008 -#define IOTCR_DFI 0x00000010 -#define IOTCR_DDP 0x00000020 -#define IOTCR_JTE 0x00000040 -#define IOTCR_PPE 0x00000080 -#endif - -/* - * The following defines are for the flags in the direct PCI data register. - */ -#ifndef NO_CS4612 -#define DPCID_D_MASK 0xFFFFFFFF -#define DPCID_D_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the direct PCI address register. - */ -#ifndef NO_CS4612 -#define DPCIA_A_MASK 0xFFFFFFFF -#define DPCIA_A_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the direct PCI command register. - */ -#ifndef NO_CS4612 -#define DPCIC_C_MASK 0x0000000F -#define DPCIC_C_IOREAD 0x00000002 -#define DPCIC_C_IOWRITE 0x00000003 -#define DPCIC_BE_MASK 0x000000F0 -#endif - -/* - * The following defines are for the flags in the PC/PCI request register. - */ -#ifndef NO_CS4612 -#define PCPCIR_RDC_MASK 0x00000007 -#define PCPCIR_C_MASK 0x00007000 -#define PCPCIR_REQ 0x00008000 -#define PCPCIR_RDC_SHIFT 0 -#define PCPCIR_C_SHIFT 12 -#endif - -/* - * The following defines are for the flags in the PC/PCI grant register. - */ -#ifndef NO_CS4612 -#define PCPCIG_GDC_MASK 0x00000007 -#define PCPCIG_VL 0x00008000 -#define PCPCIG_GDC_SHIFT 0 -#endif - -/* - * The following defines are for the flags in the PC/PCI master enable - * register. - */ -#ifndef NO_CS4612 -#define PCPCIEN_EN 0x00000001 -#endif - -/* - * The following defines are for the flags in the extended PCI power - * management control register. - */ -#ifndef NO_CS4612 -#define EPCIPMC_GWU 0x00000001 -#define EPCIPMC_FSPC 0x00000002 -#endif - -/* - * The following defines are for the flags in the SP control register. - */ -#define SPCR_RUN 0x00000001 -#define SPCR_STPFR 0x00000002 -#define SPCR_RUNFR 0x00000004 -#define SPCR_TICK 0x00000008 -#define SPCR_DRQEN 0x00000020 -#define SPCR_RSTSP 0x00000040 -#define SPCR_OREN 0x00000080 -#ifndef NO_CS4612 -#define SPCR_PCIINT 0x00000100 -#define SPCR_OINTD 0x00000200 -#define SPCR_CRE 0x00008000 -#endif - -/* - * The following defines are for the flags in the debug index register. - */ -#define DREG_REGID_MASK 0x0000007F -#define DREG_DEBUG 0x00000080 -#define DREG_RGBK_MASK 0x00000700 -#define DREG_TRAP 0x00000800 -#if !defined(NO_CS4612) -#if !defined(NO_CS4615) -#define DREG_TRAPX 0x00001000 -#endif -#endif -#define DREG_REGID_SHIFT 0 -#define DREG_RGBK_SHIFT 8 -#define DREG_RGBK_REGID_MASK 0x0000077F -#define DREG_REGID_R0 0x00000010 -#define DREG_REGID_R1 0x00000011 -#define DREG_REGID_R2 0x00000012 -#define DREG_REGID_R3 0x00000013 -#define DREG_REGID_R4 0x00000014 -#define DREG_REGID_R5 0x00000015 -#define DREG_REGID_R6 0x00000016 -#define DREG_REGID_R7 0x00000017 -#define DREG_REGID_R8 0x00000018 -#define DREG_REGID_R9 0x00000019 -#define DREG_REGID_RA 0x0000001A -#define DREG_REGID_RB 0x0000001B -#define DREG_REGID_RC 0x0000001C -#define DREG_REGID_RD 0x0000001D -#define DREG_REGID_RE 0x0000001E -#define DREG_REGID_RF 0x0000001F -#define DREG_REGID_RA_BUS_LOW 0x00000020 -#define DREG_REGID_RA_BUS_HIGH 0x00000038 -#define DREG_REGID_YBUS_LOW 0x00000050 -#define DREG_REGID_YBUS_HIGH 0x00000058 -#define DREG_REGID_TRAP_0 0x00000100 -#define DREG_REGID_TRAP_1 0x00000101 -#define DREG_REGID_TRAP_2 0x00000102 -#define DREG_REGID_TRAP_3 0x00000103 -#define DREG_REGID_TRAP_4 0x00000104 -#define DREG_REGID_TRAP_5 0x00000105 -#define DREG_REGID_TRAP_6 0x00000106 -#define DREG_REGID_TRAP_7 0x00000107 -#define DREG_REGID_INDIRECT_ADDRESS 0x0000010E -#define DREG_REGID_TOP_OF_STACK 0x0000010F -#if !defined(NO_CS4612) -#if !defined(NO_CS4615) -#define DREG_REGID_TRAP_8 0x00000110 -#define DREG_REGID_TRAP_9 0x00000111 -#define DREG_REGID_TRAP_10 0x00000112 -#define DREG_REGID_TRAP_11 0x00000113 -#define DREG_REGID_TRAP_12 0x00000114 -#define DREG_REGID_TRAP_13 0x00000115 -#define DREG_REGID_TRAP_14 0x00000116 -#define DREG_REGID_TRAP_15 0x00000117 -#define DREG_REGID_TRAP_16 0x00000118 -#define DREG_REGID_TRAP_17 0x00000119 -#define DREG_REGID_TRAP_18 0x0000011A -#define DREG_REGID_TRAP_19 0x0000011B -#define DREG_REGID_TRAP_20 0x0000011C -#define DREG_REGID_TRAP_21 0x0000011D -#define DREG_REGID_TRAP_22 0x0000011E -#define DREG_REGID_TRAP_23 0x0000011F -#endif -#endif -#define DREG_REGID_RSA0_LOW 0x00000200 -#define DREG_REGID_RSA0_HIGH 0x00000201 -#define DREG_REGID_RSA1_LOW 0x00000202 -#define DREG_REGID_RSA1_HIGH 0x00000203 -#define DREG_REGID_RSA2 0x00000204 -#define DREG_REGID_RSA3 0x00000205 -#define DREG_REGID_RSI0_LOW 0x00000206 -#define DREG_REGID_RSI0_HIGH 0x00000207 -#define DREG_REGID_RSI1 0x00000208 -#define DREG_REGID_RSI2 0x00000209 -#define DREG_REGID_SAGUSTATUS 0x0000020A -#define DREG_REGID_RSCONFIG01_LOW 0x0000020B -#define DREG_REGID_RSCONFIG01_HIGH 0x0000020C -#define DREG_REGID_RSCONFIG23_LOW 0x0000020D -#define DREG_REGID_RSCONFIG23_HIGH 0x0000020E -#define DREG_REGID_RSDMA01E 0x0000020F -#define DREG_REGID_RSDMA23E 0x00000210 -#define DREG_REGID_RSD0_LOW 0x00000211 -#define DREG_REGID_RSD0_HIGH 0x00000212 -#define DREG_REGID_RSD1_LOW 0x00000213 -#define DREG_REGID_RSD1_HIGH 0x00000214 -#define DREG_REGID_RSD2_LOW 0x00000215 -#define DREG_REGID_RSD2_HIGH 0x00000216 -#define DREG_REGID_RSD3_LOW 0x00000217 -#define DREG_REGID_RSD3_HIGH 0x00000218 -#define DREG_REGID_SRAR_HIGH 0x0000021A -#define DREG_REGID_SRAR_LOW 0x0000021B -#define DREG_REGID_DMA_STATE 0x0000021C -#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021D -#define DREG_REGID_NEXT_DMA_STREAM 0x0000021E -#define DREG_REGID_CPU_STATUS 0x00000300 -#define DREG_REGID_MAC_MODE 0x00000301 -#define DREG_REGID_STACK_AND_REPEAT 0x00000302 -#define DREG_REGID_INDEX0 0x00000304 -#define DREG_REGID_INDEX1 0x00000305 -#define DREG_REGID_DMA_STATE_0_3 0x00000400 -#define DREG_REGID_DMA_STATE_4_7 0x00000404 -#define DREG_REGID_DMA_STATE_8_11 0x00000408 -#define DREG_REGID_DMA_STATE_12_15 0x0000040C -#define DREG_REGID_DMA_STATE_16_19 0x00000410 -#define DREG_REGID_DMA_STATE_20_23 0x00000414 -#define DREG_REGID_DMA_STATE_24_27 0x00000418 -#define DREG_REGID_DMA_STATE_28_31 0x0000041C -#define DREG_REGID_DMA_STATE_32_35 0x00000420 -#define DREG_REGID_DMA_STATE_36_39 0x00000424 -#define DREG_REGID_DMA_STATE_40_43 0x00000428 -#define DREG_REGID_DMA_STATE_44_47 0x0000042C -#define DREG_REGID_DMA_STATE_48_51 0x00000430 -#define DREG_REGID_DMA_STATE_52_55 0x00000434 -#define DREG_REGID_DMA_STATE_56_59 0x00000438 -#define DREG_REGID_DMA_STATE_60_63 0x0000043C -#define DREG_REGID_DMA_STATE_64_67 0x00000440 -#define DREG_REGID_DMA_STATE_68_71 0x00000444 -#define DREG_REGID_DMA_STATE_72_75 0x00000448 -#define DREG_REGID_DMA_STATE_76_79 0x0000044C -#define DREG_REGID_DMA_STATE_80_83 0x00000450 -#define DREG_REGID_DMA_STATE_84_87 0x00000454 -#define DREG_REGID_DMA_STATE_88_91 0x00000458 -#define DREG_REGID_DMA_STATE_92_95 0x0000045C -#define DREG_REGID_TRAP_SELECT 0x00000500 -#define DREG_REGID_TRAP_WRITE_0 0x00000500 -#define DREG_REGID_TRAP_WRITE_1 0x00000501 -#define DREG_REGID_TRAP_WRITE_2 0x00000502 -#define DREG_REGID_TRAP_WRITE_3 0x00000503 -#define DREG_REGID_TRAP_WRITE_4 0x00000504 -#define DREG_REGID_TRAP_WRITE_5 0x00000505 -#define DREG_REGID_TRAP_WRITE_6 0x00000506 -#define DREG_REGID_TRAP_WRITE_7 0x00000507 -#if !defined(NO_CS4612) -#if !defined(NO_CS4615) -#define DREG_REGID_TRAP_WRITE_8 0x00000510 -#define DREG_REGID_TRAP_WRITE_9 0x00000511 -#define DREG_REGID_TRAP_WRITE_10 0x00000512 -#define DREG_REGID_TRAP_WRITE_11 0x00000513 -#define DREG_REGID_TRAP_WRITE_12 0x00000514 -#define DREG_REGID_TRAP_WRITE_13 0x00000515 -#define DREG_REGID_TRAP_WRITE_14 0x00000516 -#define DREG_REGID_TRAP_WRITE_15 0x00000517 -#define DREG_REGID_TRAP_WRITE_16 0x00000518 -#define DREG_REGID_TRAP_WRITE_17 0x00000519 -#define DREG_REGID_TRAP_WRITE_18 0x0000051A -#define DREG_REGID_TRAP_WRITE_19 0x0000051B -#define DREG_REGID_TRAP_WRITE_20 0x0000051C -#define DREG_REGID_TRAP_WRITE_21 0x0000051D -#define DREG_REGID_TRAP_WRITE_22 0x0000051E -#define DREG_REGID_TRAP_WRITE_23 0x0000051F -#endif -#endif -#define DREG_REGID_MAC0_ACC0_LOW 0x00000600 -#define DREG_REGID_MAC0_ACC1_LOW 0x00000601 -#define DREG_REGID_MAC0_ACC2_LOW 0x00000602 -#define DREG_REGID_MAC0_ACC3_LOW 0x00000603 -#define DREG_REGID_MAC1_ACC0_LOW 0x00000604 -#define DREG_REGID_MAC1_ACC1_LOW 0x00000605 -#define DREG_REGID_MAC1_ACC2_LOW 0x00000606 -#define DREG_REGID_MAC1_ACC3_LOW 0x00000607 -#define DREG_REGID_MAC0_ACC0_MID 0x00000608 -#define DREG_REGID_MAC0_ACC1_MID 0x00000609 -#define DREG_REGID_MAC0_ACC2_MID 0x0000060A -#define DREG_REGID_MAC0_ACC3_MID 0x0000060B -#define DREG_REGID_MAC1_ACC0_MID 0x0000060C -#define DREG_REGID_MAC1_ACC1_MID 0x0000060D -#define DREG_REGID_MAC1_ACC2_MID 0x0000060E -#define DREG_REGID_MAC1_ACC3_MID 0x0000060F -#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610 -#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611 -#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612 -#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613 -#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614 -#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615 -#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616 -#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617 -#define DREG_REGID_RSHOUT_LOW 0x00000620 -#define DREG_REGID_RSHOUT_MID 0x00000628 -#define DREG_REGID_RSHOUT_HIGH 0x00000630 - -/* - * The following defines are for the flags in the DMA stream requestor write - */ -#define DSRWP_DSR_MASK 0x0000000F -#define DSRWP_DSR_BG_RQ 0x00000001 -#define DSRWP_DSR_PRIORITY_MASK 0x00000006 -#define DSRWP_DSR_PRIORITY_0 0x00000000 -#define DSRWP_DSR_PRIORITY_1 0x00000002 -#define DSRWP_DSR_PRIORITY_2 0x00000004 -#define DSRWP_DSR_PRIORITY_3 0x00000006 -#define DSRWP_DSR_RQ_PENDING 0x00000008 - -/* - * The following defines are for the flags in the trap write port register. - */ -#define TWPR_TW_MASK 0x0000FFFF -#define TWPR_TW_SHIFT 0 - -/* - * The following defines are for the flags in the stack pointer write - * register. - */ -#define SPWR_STKP_MASK 0x0000000F -#define SPWR_STKP_SHIFT 0 - -/* - * The following defines are for the flags in the SP interrupt register. - */ -#define SPIR_FRI 0x00000001 -#define SPIR_DOI 0x00000002 -#define SPIR_GPI2 0x00000004 -#define SPIR_GPI3 0x00000008 -#define SPIR_IP0 0x00000010 -#define SPIR_IP1 0x00000020 -#define SPIR_IP2 0x00000040 -#define SPIR_IP3 0x00000080 - -/* - * The following defines are for the flags in the functional group 1 register. - */ -#define FGR1_F1S_MASK 0x0000FFFF -#define FGR1_F1S_SHIFT 0 - -/* - * The following defines are for the flags in the SP clock status register. - */ -#define SPCS_FRI 0x00000001 -#define SPCS_DOI 0x00000002 -#define SPCS_GPI2 0x00000004 -#define SPCS_GPI3 0x00000008 -#define SPCS_IP0 0x00000010 -#define SPCS_IP1 0x00000020 -#define SPCS_IP2 0x00000040 -#define SPCS_IP3 0x00000080 -#define SPCS_SPRUN 0x00000100 -#define SPCS_SLEEP 0x00000200 -#define SPCS_FG 0x00000400 -#define SPCS_ORUN 0x00000800 -#define SPCS_IRQ 0x00001000 -#define SPCS_FGN_MASK 0x0000E000 -#define SPCS_FGN_SHIFT 13 - -/* - * The following defines are for the flags in the SP DMA requestor status - * register. - */ -#define SDSR_DCS_MASK 0x000000FF -#define SDSR_DCS_SHIFT 0 -#define SDSR_DCS_NONE 0x00000007 - -/* - * The following defines are for the flags in the frame timer register. - */ -#define FRMT_FTV_MASK 0x0000FFFF -#define FRMT_FTV_SHIFT 0 - -/* - * The following defines are for the flags in the frame timer current count - * register. - */ -#define FRCC_FCC_MASK 0x0000FFFF -#define FRCC_FCC_SHIFT 0 - -/* - * The following defines are for the flags in the frame timer save count - * register. - */ -#define FRSC_FCS_MASK 0x0000FFFF -#define FRSC_FCS_SHIFT 0 - -/* - * The following define the various flags stored in the scatter/gather - * descriptors. - */ -#define DMA_SG_NEXT_ENTRY_MASK 0x00000FF8 -#define DMA_SG_SAMPLE_END_MASK 0x0FFF0000 -#define DMA_SG_SAMPLE_END_FLAG 0x10000000 -#define DMA_SG_LOOP_END_FLAG 0x20000000 -#define DMA_SG_SIGNAL_END_FLAG 0x40000000 -#define DMA_SG_SIGNAL_PAGE_FLAG 0x80000000 -#define DMA_SG_NEXT_ENTRY_SHIFT 3 -#define DMA_SG_SAMPLE_END_SHIFT 16 - -/* - * The following define the offsets of the fields within the on-chip generic - * DMA requestor. - */ -#define DMA_RQ_CONTROL1 0x00000000 -#define DMA_RQ_CONTROL2 0x00000004 -#define DMA_RQ_SOURCE_ADDR 0x00000008 -#define DMA_RQ_DESTINATION_ADDR 0x0000000C -#define DMA_RQ_NEXT_PAGE_ADDR 0x00000010 -#define DMA_RQ_NEXT_PAGE_SGDESC 0x00000014 -#define DMA_RQ_LOOP_START_ADDR 0x00000018 -#define DMA_RQ_POST_LOOP_ADDR 0x0000001C -#define DMA_RQ_PAGE_MAP_ADDR 0x00000020 - -/* - * The following defines are for the flags in the first control word of the - * on-chip generic DMA requestor. - */ -#define DMA_RQ_C1_COUNT_MASK 0x000003FF -#define DMA_RQ_C1_DESTINATION_SCATTER 0x00001000 -#define DMA_RQ_C1_SOURCE_GATHER 0x00002000 -#define DMA_RQ_C1_DONE_FLAG 0x00004000 -#define DMA_RQ_C1_OPTIMIZE_STATE 0x00008000 -#define DMA_RQ_C1_SAMPLE_END_STATE_MASK 0x00030000 -#define DMA_RQ_C1_FULL_PAGE 0x00000000 -#define DMA_RQ_C1_BEFORE_SAMPLE_END 0x00010000 -#define DMA_RQ_C1_PAGE_MAP_ERROR 0x00020000 -#define DMA_RQ_C1_AT_SAMPLE_END 0x00030000 -#define DMA_RQ_C1_LOOP_END_STATE_MASK 0x000C0000 -#define DMA_RQ_C1_NOT_LOOP_END 0x00000000 -#define DMA_RQ_C1_BEFORE_LOOP_END 0x00040000 -#define DMA_RQ_C1_2PAGE_LOOP_BEGIN 0x00080000 -#define DMA_RQ_C1_LOOP_BEGIN 0x000C0000 -#define DMA_RQ_C1_PAGE_MAP_MASK 0x00300000 -#define DMA_RQ_C1_PM_NONE_PENDING 0x00000000 -#define DMA_RQ_C1_PM_NEXT_PENDING 0x00100000 -#define DMA_RQ_C1_PM_RESERVED 0x00200000 -#define DMA_RQ_C1_PM_LOOP_NEXT_PENDING 0x00300000 -#define DMA_RQ_C1_WRITEBACK_DEST_FLAG 0x00400000 -#define DMA_RQ_C1_WRITEBACK_SRC_FLAG 0x00800000 -#define DMA_RQ_C1_DEST_SIZE_MASK 0x07000000 -#define DMA_RQ_C1_DEST_LINEAR 0x00000000 -#define DMA_RQ_C1_DEST_MOD16 0x01000000 -#define DMA_RQ_C1_DEST_MOD32 0x02000000 -#define DMA_RQ_C1_DEST_MOD64 0x03000000 -#define DMA_RQ_C1_DEST_MOD128 0x04000000 -#define DMA_RQ_C1_DEST_MOD256 0x05000000 -#define DMA_RQ_C1_DEST_MOD512 0x06000000 -#define DMA_RQ_C1_DEST_MOD1024 0x07000000 -#define DMA_RQ_C1_DEST_ON_HOST 0x08000000 -#define DMA_RQ_C1_SOURCE_SIZE_MASK 0x70000000 -#define DMA_RQ_C1_SOURCE_LINEAR 0x00000000 -#define DMA_RQ_C1_SOURCE_MOD16 0x10000000 -#define DMA_RQ_C1_SOURCE_MOD32 0x20000000 -#define DMA_RQ_C1_SOURCE_MOD64 0x30000000 -#define DMA_RQ_C1_SOURCE_MOD128 0x40000000 -#define DMA_RQ_C1_SOURCE_MOD256 0x50000000 -#define DMA_RQ_C1_SOURCE_MOD512 0x60000000 -#define DMA_RQ_C1_SOURCE_MOD1024 0x70000000 -#define DMA_RQ_C1_SOURCE_ON_HOST 0x80000000 -#define DMA_RQ_C1_COUNT_SHIFT 0 - -/* - * The following defines are for the flags in the second control word of the - * on-chip generic DMA requestor. - */ -#define DMA_RQ_C2_VIRTUAL_CHANNEL_MASK 0x0000003F -#define DMA_RQ_C2_VIRTUAL_SIGNAL_MASK 0x00000300 -#define DMA_RQ_C2_NO_VIRTUAL_SIGNAL 0x00000000 -#define DMA_RQ_C2_SIGNAL_EVERY_DMA 0x00000100 -#define DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG 0x00000200 -#define DMA_RQ_C2_SIGNAL_DEST_PINGPONG 0x00000300 -#define DMA_RQ_C2_AUDIO_CONVERT_MASK 0x0000F000 -#define DMA_RQ_C2_AC_NONE 0x00000000 -#define DMA_RQ_C2_AC_8_TO_16_BIT 0x00001000 -#define DMA_RQ_C2_AC_MONO_TO_STEREO 0x00002000 -#define DMA_RQ_C2_AC_ENDIAN_CONVERT 0x00004000 -#define DMA_RQ_C2_AC_SIGNED_CONVERT 0x00008000 -#define DMA_RQ_C2_LOOP_END_MASK 0x0FFF0000 -#define DMA_RQ_C2_LOOP_MASK 0x30000000 -#define DMA_RQ_C2_NO_LOOP 0x00000000 -#define DMA_RQ_C2_ONE_PAGE_LOOP 0x10000000 -#define DMA_RQ_C2_TWO_PAGE_LOOP 0x20000000 -#define DMA_RQ_C2_MULTI_PAGE_LOOP 0x30000000 -#define DMA_RQ_C2_SIGNAL_LOOP_BACK 0x40000000 -#define DMA_RQ_C2_SIGNAL_POST_BEGIN_PAGE 0x80000000 -#define DMA_RQ_C2_VIRTUAL_CHANNEL_SHIFT 0 -#define DMA_RQ_C2_LOOP_END_SHIFT 16 - -/* - * The following defines are for the flags in the source and destination words - * of the on-chip generic DMA requestor. - */ -#define DMA_RQ_SD_ADDRESS_MASK 0x0000FFFF -#define DMA_RQ_SD_MEMORY_ID_MASK 0x000F0000 -#define DMA_RQ_SD_SP_PARAM_ADDR 0x00000000 -#define DMA_RQ_SD_SP_SAMPLE_ADDR 0x00010000 -#define DMA_RQ_SD_SP_PROGRAM_ADDR 0x00020000 -#define DMA_RQ_SD_SP_DEBUG_ADDR 0x00030000 -#define DMA_RQ_SD_OMNIMEM_ADDR 0x000E0000 -#define DMA_RQ_SD_END_FLAG 0x40000000 -#define DMA_RQ_SD_ERROR_FLAG 0x80000000 -#define DMA_RQ_SD_ADDRESS_SHIFT 0 - -/* - * The following defines are for the flags in the page map address word of the - * on-chip generic DMA requestor. - */ -#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_MASK 0x00000FF8 -#define DMA_RQ_PMA_PAGE_TABLE_MASK 0xFFFFF000 -#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_SHIFT 3 -#define DMA_RQ_PMA_PAGE_TABLE_SHIFT 12 - -#define BA1_VARIDEC_BUF_1 0x000 - -#define BA1_PDTC 0x0c0 /* BA1_PLAY_DMA_TRANSACTION_COUNT_REG */ -#define BA1_PFIE 0x0c4 /* BA1_PLAY_FORMAT_&_INTERRUPT_ENABLE_REG */ -#define BA1_PBA 0x0c8 /* BA1_PLAY_BUFFER_ADDRESS */ -#define BA1_PVOL 0x0f8 /* BA1_PLAY_VOLUME_REG */ -#define BA1_PSRC 0x288 /* BA1_PLAY_SAMPLE_RATE_CORRECTION_REG */ -#define BA1_PCTL 0x2a4 /* BA1_PLAY_CONTROL_REG */ -#define BA1_PPI 0x2b4 /* BA1_PLAY_PHASE_INCREMENT_REG */ - -#define BA1_CCTL 0x064 /* BA1_CAPTURE_CONTROL_REG */ -#define BA1_CIE 0x104 /* BA1_CAPTURE_INTERRUPT_ENABLE_REG */ -#define BA1_CBA 0x10c /* BA1_CAPTURE_BUFFER_ADDRESS */ -#define BA1_CSRC 0x2c8 /* BA1_CAPTURE_SAMPLE_RATE_CORRECTION_REG */ -#define BA1_CCI 0x2d8 /* BA1_CAPTURE_COEFFICIENT_INCREMENT_REG */ -#define BA1_CD 0x2e0 /* BA1_CAPTURE_DELAY_REG */ -#define BA1_CPI 0x2f4 /* BA1_CAPTURE_PHASE_INCREMENT_REG */ -#define BA1_CVOL 0x2f8 /* BA1_CAPTURE_VOLUME_REG */ - -#define BA1_CFG1 0x134 /* BA1_CAPTURE_FRAME_GROUP_1_REG */ -#define BA1_CFG2 0x138 /* BA1_CAPTURE_FRAME_GROUP_2_REG */ -#define BA1_CCST 0x13c /* BA1_CAPTURE_CONSTANT_REG */ -#define BA1_CSPB 0x340 /* BA1_CAPTURE_SPB_ADDRESS */ - -/* - * - */ - -#define CS46XX_MODE_OUTPUT (1<<0) /* MIDI UART - output */ -#define CS46XX_MODE_INPUT (1<<1) /* MIDI UART - input */ - -/* - * - */ - -#define SAVE_REG_MAX 0x10 -#define POWER_DOWN_ALL 0x7f0f - -/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ -#define MAX_NR_AC97 4 -#define CS46XX_PRIMARY_CODEC_INDEX 0 -#define CS46XX_SECONDARY_CODEC_INDEX 1 -#define CS46XX_SECONDARY_CODEC_OFFSET 0x80 -#define CS46XX_DSP_CAPTURE_CHANNEL 1 - -/* capture */ -#define CS46XX_DSP_CAPTURE_CHANNEL 1 - -/* mixer */ -#define CS46XX_MIXER_SPDIF_INPUT_ELEMENT 1 -#define CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT 2 - - -struct snd_cs46xx_pcm { - struct snd_dma_buffer hw_buf; - - unsigned int ctl; - unsigned int shift; /* Shift count to trasform frames in bytes */ - struct snd_pcm_indirect pcm_rec; - struct snd_pcm_substream *substream; - - struct dsp_pcm_channel_descriptor * pcm_channel; - - int pcm_channel_id; /* Fron Rear, Center Lfe ... */ -}; - -struct snd_cs46xx_region { - char name[24]; - unsigned long base; - void __iomem *remap_addr; - unsigned long size; - struct resource *resource; -}; - -struct snd_cs46xx { - int irq; - unsigned long ba0_addr; - unsigned long ba1_addr; - union { - struct { - struct snd_cs46xx_region ba0; - struct snd_cs46xx_region data0; - struct snd_cs46xx_region data1; - struct snd_cs46xx_region pmem; - struct snd_cs46xx_region reg; - } name; - struct snd_cs46xx_region idx[5]; - } region; - - unsigned int mode; - - struct { - struct snd_dma_buffer hw_buf; - - unsigned int ctl; - unsigned int shift; /* Shift count to trasform frames in bytes */ - struct snd_pcm_indirect pcm_rec; - struct snd_pcm_substream *substream; - } capt; - - - int nr_ac97_codecs; - struct snd_ac97_bus *ac97_bus; - struct snd_ac97 *ac97[MAX_NR_AC97]; - - struct pci_dev *pci; - struct snd_card *card; - struct snd_pcm *pcm; - - struct snd_rawmidi *rmidi; - struct snd_rawmidi_substream *midi_input; - struct snd_rawmidi_substream *midi_output; - - spinlock_t reg_lock; - unsigned int midcr; - unsigned int uartm; - - int amplifier; - void (*amplifier_ctrl)(struct snd_cs46xx *, int); - void (*active_ctrl)(struct snd_cs46xx *, int); - void (*mixer_init)(struct snd_cs46xx *); - - int acpi_port; - struct snd_kcontrol *eapd_switch; /* for amplifier hack */ - int accept_valid; /* accept mmap valid (for OSS) */ - int in_suspend; - - struct gameport *gameport; - -#ifdef CONFIG_SND_CS46XX_NEW_DSP - struct mutex spos_mutex; - - struct dsp_spos_instance * dsp_spos_instance; - - struct snd_pcm *pcm_rear; - struct snd_pcm *pcm_center_lfe; - struct snd_pcm *pcm_iec958; -#else /* for compatibility */ - struct snd_cs46xx_pcm *playback_pcm; - unsigned int play_ctl; -#endif - -#ifdef CONFIG_PM - u32 *saved_regs; -#endif -}; - -int snd_cs46xx_create(struct snd_card *card, - struct pci_dev *pci, - int external_amp, int thinkpad, - struct snd_cs46xx **rcodec); -extern const struct dev_pm_ops snd_cs46xx_pm; - -int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); -int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); -int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); -int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); -int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device); -int snd_cs46xx_midi(struct snd_cs46xx *chip, int device, struct snd_rawmidi **rmidi); -int snd_cs46xx_start_dsp(struct snd_cs46xx *chip); -int snd_cs46xx_gameport(struct snd_cs46xx *chip); - -#endif /* __SOUND_CS46XX_H */ diff --git a/include/sound/cs46xx_dsp_scb_types.h b/include/sound/cs46xx_dsp_scb_types.h deleted file mode 100644 index 080857ad0ca2..000000000000 --- a/include/sound/cs46xx_dsp_scb_types.h +++ /dev/null @@ -1,1213 +0,0 @@ -/* - * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards - * Copyright (c) by Jaroslav Kysela - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * - * NOTE: comments are copy/paste from cwcemb80.lst - * provided by Tom Woller at Cirrus (my only - * documentation about the SP OS running inside - * the DSP) - */ - -#ifndef __CS46XX_DSP_SCB_TYPES_H__ -#define __CS46XX_DSP_SCB_TYPES_H__ - -#include - -#ifndef ___DSP_DUAL_16BIT_ALLOC -#if defined(__LITTLE_ENDIAN) -#define ___DSP_DUAL_16BIT_ALLOC(a,b) u16 a; u16 b; -#elif defined(__BIG_ENDIAN) -#define ___DSP_DUAL_16BIT_ALLOC(a,b) u16 b; u16 a; -#else -#error Not __LITTLE_ENDIAN and not __BIG_ENDIAN, then what ??? -#endif -#endif - -/* This structs are used internally by the SP */ - -struct dsp_basic_dma_req { - /* DMA Requestor Word 0 (DCW) fields: - - 31 [30-28]27 [26:24] 23 22 21 20 [19:18] [17:16] 15 14 13 12 11 10 9 8 7 6 [5:0] - _______________________________________________________________________________________ - |S| SBT |D| DBT |wb|wb| | | LS | SS |Opt|Do|SSG|DSG| | | | | | | Dword | - |H|_____ |H|_________|S_|D |__|__|______|_______|___|ne|__ |__ |__|__|_|_|_|_|_Count -1| - */ - u32 dcw; /* DMA Control Word */ - u32 dmw; /* DMA Mode Word */ - u32 saw; /* Source Address Word */ - u32 daw; /* Destination Address Word */ -}; - -struct dsp_scatter_gather_ext { - u32 npaw; /* Next-Page Address Word */ - - /* DMA Requestor Word 5 (NPCW) fields: - - 31-30 29 28 [27:16] [15:12] [11:3] [2:0] - _________________________________________________________________________________________ - |SV |LE|SE| Sample-end byte offset | | Page-map entry offset for next | | - |page|__|__| ___________________________|_________|__page, if !sample-end___________|____| - */ - u32 npcw; /* Next-Page Control Word */ - u32 lbaw; /* Loop-Begin Address Word */ - u32 nplbaw; /* Next-Page after Loop-Begin Address Word */ - u32 sgaw; /* Scatter/Gather Address Word */ -}; - -struct dsp_volume_control { - ___DSP_DUAL_16BIT_ALLOC( - rightTarg, /* Target volume for left & right channels */ - leftTarg - ) - ___DSP_DUAL_16BIT_ALLOC( - rightVol, /* Current left & right channel volumes */ - leftVol - ) -}; - -/* Generic stream control block (SCB) structure definition */ -struct dsp_generic_scb { - /* For streaming I/O, the DSP should never alter any words in the DMA - requestor or the scatter/gather extension. Only ad hoc DMA request - streams are free to alter the requestor (currently only occur in the - DOS-based MIDI controller and in debugger-inserted code). - - If an SCB does not have any associated DMA requestor, these 9 ints - may be freed for use by other tasks, but the pointer to the SCB must - still be such that the insOrd:nextSCB appear at offset 9 from the - SCB pointer. - - Basic (non scatter/gather) DMA requestor (4 ints) - */ - - /* Initialized by the host, only modified by DMA - R/O for the DSP task */ - struct dsp_basic_dma_req basic_req; /* Optional */ - - /* Scatter/gather DMA requestor extension (5 ints) - Initialized by the host, only modified by DMA - DSP task never needs to even read these. - */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - - /* Sublist pointer & next stream control block (SCB) link. - Initialized & modified by the host R/O for the DSP task - */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - /* Pointer to this tasks parameter block & stream function pointer - Initialized by the host R/O for the DSP task */ - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - /* rsConfig register for stream buffer (rsDMA reg. - is loaded from basicReq.daw for incoming streams, or - basicReq.saw, for outgoing streams) - - 31 30 29 [28:24] [23:16] 15 14 13 12 11 10 9 8 7 6 5 4 [3:0] - ______________________________________________________________________________ - |DMA |D|maxDMAsize| streamNum|dir|p| | | | | | |ds |shr 1|rev Cy | mod | - |prio |_|__________|__________|___|_|__|__|__|__|_|_|___|_____|_______|_______| - 31 30 29 [28:24] [23:16] 15 14 13 12 11 10 9 8 7 6 5 4 [3:0] - - - Initialized by the host R/O for the DSP task - */ - u32 strm_rs_config; /* REQUIRED */ - // - /* On mixer input streams: indicates mixer input stream configuration - On Tees, this is copied from the stream being snooped - - Stream sample pointer & MAC-unit mode for this stream - - Initialized by the host Updated by the DSP task - */ - u32 strm_buf_ptr; /* REQUIRED */ - - /* On mixer input streams: points to next mixer input and is updated by the - mixer subroutine in the "parent" DSP task - (least-significant 16 bits are preserved, unused) - - On Tees, the pointer is copied from the stream being snooped on - initialization, and, subsequently, it is copied into the - stream being snooped. - - On wavetable/3D voices: the strmBufPtr will use all 32 bits to allow for - fractional phase accumulation - - Fractional increment per output sample in the input sample buffer - - (Not used on mixer input streams & redefined on Tees) - On wavetable/3D voices: this 32-bit word specifies the integer.fractional - increment per output sample. - */ - u32 strmPhiIncr; - - - /* Standard stereo volume control - Initialized by the host (host updates target volumes) - - Current volumes update by the DSP task - On mixer input streams: required & updated by the mixer subroutine in the - "parent" DSP task - - On Tees, both current & target volumes are copied up on initialization, - and, subsequently, the target volume is copied up while the current - volume is copied down. - - These two 32-bit words are redefined for wavetable & 3-D voices. - */ - struct dsp_volume_control vol_ctrl_t; /* Optional */ -}; - - -struct dsp_spos_control_block { - /* WARNING: Certain items in this structure are modified by the host - Any dword that can be modified by the host, must not be - modified by the SP as the host can only do atomic dword - writes, and to do otherwise, even a read modify write, - may lead to corrupted data on the SP. - - This rule does not apply to one off boot time initialisation prior to starting the SP - */ - - - ___DSP_DUAL_16BIT_ALLOC( - /* First element on the Hyper forground task tree */ - hfg_tree_root_ptr, /* HOST */ - /* First 3 dwords are written by the host and read-only on the DSP */ - hfg_stack_base /* HOST */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - /* Point to this data structure to enable easy access */ - spos_cb_ptr, /* SP */ - prev_task_tree_ptr /* SP && HOST */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - /* Currently Unused */ - xxinterval_timer_period, - /* Enable extension of SPOS data structure */ - HFGSPB_ptr - ) - - - ___DSP_DUAL_16BIT_ALLOC( - xxnum_HFG_ticks_thisInterval, - /* Modified by the DSP */ - xxnum_tntervals - ) - - - /* Set by DSP upon encountering a trap (breakpoint) or a spurious - interrupt. The host must clear this dword after reading it - upon receiving spInt1. */ - ___DSP_DUAL_16BIT_ALLOC( - spurious_int_flag, /* (Host & SP) Nature of the spurious interrupt */ - trap_flag /* (Host & SP) Nature of detected Trap */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - unused2, - invalid_IP_flag /* (Host & SP ) Indicate detection of invalid instruction pointer */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - /* pointer to forground task tree header for use in next task search */ - fg_task_tree_hdr_ptr, /* HOST */ - /* Data structure for controlling synchronous link update */ - hfg_sync_update_ptr /* HOST */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - begin_foreground_FCNT, /* SP */ - /* Place holder for holding sleep timing */ - last_FCNT_before_sleep /* SP */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - unused7, /* SP */ - next_task_treePtr /* SP */ - ) - - u32 unused5; - - ___DSP_DUAL_16BIT_ALLOC( - active_flags, /* SP */ - /* State flags, used to assist control of execution of Hyper Forground */ - HFG_flags /* SP */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - unused9, - unused8 - ) - - /* Space for saving enough context so that we can set up enough - to save some more context. - */ - u32 rFE_save_for_invalid_IP; - u32 r32_save_for_spurious_int; - u32 r32_save_for_trap; - u32 r32_save_for_HFG; -}; - -/* SPB for MIX_TO_OSTREAM algorithm family */ -struct dsp_mix2_ostream_spb -{ - /* 16b.16b integer.frac approximation to the - number of 3 sample triplets to output each - frame. (approximation must be floor, to - insure that the fractional error is always - positive) - */ - u32 outTripletsPerFrame; - - /* 16b.16b integer.frac accumulated number of - output triplets since the start of group - */ - u32 accumOutTriplets; -}; - -/* SCB for Timing master algorithm */ -struct dsp_timing_master_scb { - /* First 12 dwords from generic_scb_t */ - struct dsp_basic_dma_req basic_req; /* Optional */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - /* Initial values are 0000:xxxx */ - reserved, - extra_sample_accum - ) - - - /* Initial values are xxxx:0000 - hi: Current CODEC output FIFO pointer - (0 to 0x0f) - lo: Flag indicating that the CODEC - FIFO is sync'd (host clears to - resynchronize the FIFO pointer - upon start/restart) - */ - ___DSP_DUAL_16BIT_ALLOC( - codec_FIFO_syncd, - codec_FIFO_ptr - ) - - /* Init. 8000:0005 for 44.1k - 8000:0001 for 48k - hi: Fractional sample accumulator 0.16b - lo: Number of frames remaining to be - processed in the current group of - frames - */ - ___DSP_DUAL_16BIT_ALLOC( - frac_samp_accum_qm1, - TM_frms_left_in_group - ) - - /* Init. 0001:0005 for 44.1k - 0000:0001 for 48k - hi: Fractional sample correction factor 0.16b - to be added every frameGroupLength frames - to correct for truncation error in - nsamp_per_frm_q15 - lo: Number of frames in the group - */ - ___DSP_DUAL_16BIT_ALLOC( - frac_samp_correction_qm1, - TM_frm_group_length - ) - - /* Init. 44.1k*65536/8k = 0x00058333 for 44.1k - 48k*65536/8k = 0x00060000 for 48k - 16b.16b integer.frac approximation to the - number of samples to output each frame. - (approximation must be floor, to insure */ - u32 nsamp_per_frm_q15; -}; - -/* SCB for CODEC output algorithm */ -struct dsp_codec_output_scb { - /* First 13 dwords from generic_scb_t */ - struct dsp_basic_dma_req basic_req; /* Optional */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - u32 strm_rs_config; /* REQUIRED */ - - u32 strm_buf_ptr; /* REQUIRED */ - - /* NOTE: The CODEC output task reads samples from the first task on its - sublist at the stream buffer pointer (init. to lag DMA destination - address word). After the required number of samples is transferred, - the CODEC output task advances sub_list_ptr->strm_buf_ptr past the samples - consumed. - */ - - /* Init. 0000:0010 for SDout - 0060:0010 for SDout2 - 0080:0010 for SDout3 - hi: Base IO address of FIFO to which - the left-channel samples are to - be written. - lo: Displacement for the base IO - address for left-channel to obtain - the base IO address for the FIFO - to which the right-channel samples - are to be written. - */ - ___DSP_DUAL_16BIT_ALLOC( - left_chan_base_IO_addr, - right_chan_IO_disp - ) - - - /* Init: 0x0080:0004 for non-AC-97 - Init: 0x0080:0000 for AC-97 - hi: Exponential volume change rate - for input stream - lo: Positive shift count to shift the - 16-bit input sample to obtain the - 32-bit output word - */ - ___DSP_DUAL_16BIT_ALLOC( - CO_scale_shift_count, - CO_exp_vol_change_rate - ) - - /* Pointer to SCB at end of input chain */ - ___DSP_DUAL_16BIT_ALLOC( - reserved, - last_sub_ptr - ) -}; - -/* SCB for CODEC input algorithm */ -struct dsp_codec_input_scb { - /* First 13 dwords from generic_scb_t */ - struct dsp_basic_dma_req basic_req; /* Optional */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - u32 strm_rs_config; /* REQUIRED */ - u32 strm_buf_ptr; /* REQUIRED */ - - /* NOTE: The CODEC input task reads samples from the hardware FIFO - sublist at the DMA source address word (sub_list_ptr->basic_req.saw). - After the required number of samples is transferred, the CODEC - output task advances sub_list_ptr->basic_req.saw past the samples - consumed. SPuD must initialize the sub_list_ptr->basic_req.saw - to point half-way around from the initial sub_list_ptr->strm_nuf_ptr - to allow for lag/lead. - */ - - /* Init. 0000:0010 for SDout - 0060:0010 for SDout2 - 0080:0010 for SDout3 - hi: Base IO address of FIFO to which - the left-channel samples are to - be written. - lo: Displacement for the base IO - address for left-channel to obtain - the base IO address for the FIFO - to which the right-channel samples - are to be written. - */ - ___DSP_DUAL_16BIT_ALLOC( - rightChanINdisp, - left_chan_base_IN_addr - ) - /* Init. ?:fffc - lo: Negative shift count to shift the - 32-bit input dword to obtain the - 16-bit sample msb-aligned (count - is negative to shift left) - */ - ___DSP_DUAL_16BIT_ALLOC( - scaleShiftCount, - reserver1 - ) - - u32 reserved2; -}; - - -struct dsp_pcm_serial_input_scb { - /* First 13 dwords from generic_scb_t */ - struct dsp_basic_dma_req basic_req; /* Optional */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - u32 strm_buf_ptr; /* REQUIRED */ - u32 strm_rs_config; /* REQUIRED */ - - /* Init. Ptr to CODEC input SCB - hi: Pointer to the SCB containing the - input buffer to which CODEC input - samples are written - lo: Flag indicating the link to the CODEC - input task is to be initialized - */ - ___DSP_DUAL_16BIT_ALLOC( - init_codec_input_link, - codec_input_buf_scb - ) - - /* Initialized by the host (host updates target volumes) */ - struct dsp_volume_control psi_vol_ctrl; - -}; - -struct dsp_src_task_scb { - ___DSP_DUAL_16BIT_ALLOC( - frames_left_in_gof, - gofs_left_in_sec - ) - - ___DSP_DUAL_16BIT_ALLOC( - const2_thirds, - num_extra_tnput_samples - ) - - ___DSP_DUAL_16BIT_ALLOC( - cor_per_gof, - correction_per_sec - ) - - ___DSP_DUAL_16BIT_ALLOC( - output_buf_producer_ptr, - junk_DMA_MID - ) - - ___DSP_DUAL_16BIT_ALLOC( - gof_length, - gofs_per_sec - ) - - u32 input_buf_strm_config; - - ___DSP_DUAL_16BIT_ALLOC( - reserved_for_SRC_use, - input_buf_consumer_ptr - ) - - u32 accum_phi; - - ___DSP_DUAL_16BIT_ALLOC( - exp_src_vol_change_rate, - input_buf_producer_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - src_next_scb, - src_sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - src_entry_point, - src_this_sbp - ) - - u32 src_strm_rs_config; - u32 src_strm_buf_ptr; - - u32 phiIncr6int_26frac; - - struct dsp_volume_control src_vol_ctrl; -}; - -struct dsp_decimate_by_pow2_scb { - /* decimationFactor = 2, 4, or 8 (larger factors waste too much memory - when compared to cascading decimators) - */ - ___DSP_DUAL_16BIT_ALLOC( - dec2_coef_base_ptr, - dec2_coef_increment - ) - - /* coefIncrement = 128 / decimationFactor (for our ROM filter) - coefBasePtr = 0x8000 (for our ROM filter) - */ - ___DSP_DUAL_16BIT_ALLOC( - dec2_in_samples_per_out_triplet, - dec2_extra_in_samples - ) - /* extraInSamples: # of accumulated, unused input samples (init. to 0) - inSamplesPerOutTriplet = 3 * decimationFactor - */ - - ___DSP_DUAL_16BIT_ALLOC( - dec2_const2_thirds, - dec2_half_num_taps_mp5 - ) - /* halfNumTapsM5: (1/2 number of taps in decimation filter) minus 5 - const2thirds: constant 2/3 in 16Q0 format (sign.15) - */ - - ___DSP_DUAL_16BIT_ALLOC( - dec2_output_buf_producer_ptr, - dec2_junkdma_mid - ) - - u32 dec2_reserved2; - - u32 dec2_input_nuf_strm_config; - /* inputBufStrmConfig: rsConfig for the input buffer to the decimator - (buffer size = decimationFactor * 32 dwords) - */ - - ___DSP_DUAL_16BIT_ALLOC( - dec2_phi_incr, - dec2_input_buf_consumer_ptr - ) - /* inputBufConsumerPtr: Input buffer read pointer (into SRC filter) - phiIncr = decimationFactor * 4 - */ - - u32 dec2_reserved3; - - ___DSP_DUAL_16BIT_ALLOC( - dec2_exp_vol_change_rate, - dec2_input_buf_producer_ptr - ) - /* inputBufProducerPtr: Input buffer write pointer - expVolChangeRate: Exponential volume change rate for possible - future mixer on input streams - */ - - ___DSP_DUAL_16BIT_ALLOC( - dec2_next_scb, - dec2_sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - dec2_entry_point, - dec2_this_spb - ) - - u32 dec2_strm_rs_config; - u32 dec2_strm_buf_ptr; - - u32 dec2_reserved4; - - struct dsp_volume_control dec2_vol_ctrl; /* Not used! */ -}; - -struct dsp_vari_decimate_scb { - ___DSP_DUAL_16BIT_ALLOC( - vdec_frames_left_in_gof, - vdec_gofs_left_in_sec - ) - - ___DSP_DUAL_16BIT_ALLOC( - vdec_const2_thirds, - vdec_extra_in_samples - ) - /* extraInSamples: # of accumulated, unused input samples (init. to 0) - const2thirds: constant 2/3 in 16Q0 format (sign.15) */ - - ___DSP_DUAL_16BIT_ALLOC( - vdec_cor_per_gof, - vdec_correction_per_sec - ) - - ___DSP_DUAL_16BIT_ALLOC( - vdec_output_buf_producer_ptr, - vdec_input_buf_consumer_ptr - ) - /* inputBufConsumerPtr: Input buffer read pointer (into SRC filter) */ - ___DSP_DUAL_16BIT_ALLOC( - vdec_gof_length, - vdec_gofs_per_sec - ) - - u32 vdec_input_buf_strm_config; - /* inputBufStrmConfig: rsConfig for the input buffer to the decimator - (buffer size = 64 dwords) */ - u32 vdec_coef_increment; - /* coefIncrement = - 128.0 / decimationFactor (as a 32Q15 number) */ - - u32 vdec_accumphi; - /* accumPhi: accumulated fractional phase increment (6.26) */ - - ___DSP_DUAL_16BIT_ALLOC( - vdec_exp_vol_change_rate, - vdec_input_buf_producer_ptr - ) - /* inputBufProducerPtr: Input buffer write pointer - expVolChangeRate: Exponential volume change rate for possible - future mixer on input streams */ - - ___DSP_DUAL_16BIT_ALLOC( - vdec_next_scb, - vdec_sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - vdec_entry_point, - vdec_this_spb - ) - - u32 vdec_strm_rs_config; - u32 vdec_strm_buf_ptr; - - u32 vdec_phi_incr_6int_26frac; - - struct dsp_volume_control vdec_vol_ctrl; -}; - - -/* SCB for MIX_TO_OSTREAM algorithm family */ -struct dsp_mix2_ostream_scb { - /* First 13 dwords from generic_scb_t */ - struct dsp_basic_dma_req basic_req; /* Optional */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - u32 strm_rs_config; /* REQUIRED */ - u32 strm_buf_ptr; /* REQUIRED */ - - - /* hi: Number of mixed-down input triplets - computed since start of group - lo: Number of frames remaining to be - processed in the current group of - frames - */ - ___DSP_DUAL_16BIT_ALLOC( - frames_left_in_group, - accum_input_triplets - ) - - /* hi: Exponential volume change rate - for mixer on input streams - lo: Number of frames in the group - */ - ___DSP_DUAL_16BIT_ALLOC( - frame_group_length, - exp_vol_change_rate - ) - - ___DSP_DUAL_16BIT_ALLOC( - const_FFFF, - const_zero - ) -}; - - -/* SCB for S16_MIX algorithm */ -struct dsp_mix_only_scb { - /* First 13 dwords from generic_scb_t */ - struct dsp_basic_dma_req basic_req; /* Optional */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - u32 strm_rs_config; /* REQUIRED */ - u32 strm_buf_ptr; /* REQUIRED */ - - u32 reserved; - struct dsp_volume_control vol_ctrl; -}; - -/* SCB for the async. CODEC input algorithm */ -struct dsp_async_codec_input_scb { - u32 io_free2; - - u32 io_current_total; - u32 io_previous_total; - - u16 io_count; - u16 io_count_limit; - - u16 o_fifo_base_addr; - u16 ost_mo_format; - /* 1 = stereo; 0 = mono - xxx for ASER 1 (not allowed); 118 for ASER2 */ - - u32 ostrm_rs_config; - u32 ostrm_buf_ptr; - - ___DSP_DUAL_16BIT_ALLOC( - io_sclks_per_lr_clk, - io_io_enable - ) - - u32 io_free4; - - ___DSP_DUAL_16BIT_ALLOC( - io_next_scb, - io_sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - io_entry_point, - io_this_spb - ) - - u32 istrm_rs_config; - u32 istrm_buf_ptr; - - /* Init. 0000:8042: for ASER1 - 0000:8044: for ASER2 */ - ___DSP_DUAL_16BIT_ALLOC( - io_stat_reg_addr, - iofifo_pointer - ) - - /* Init 1 stero:100 ASER1 - Init 0 mono:110 ASER2 - */ - ___DSP_DUAL_16BIT_ALLOC( - ififo_base_addr, - ist_mo_format - ) - - u32 i_free; -}; - - -/* SCB for the SP/DIF CODEC input and output */ -struct dsp_spdifiscb { - ___DSP_DUAL_16BIT_ALLOC( - status_ptr, - status_start_ptr - ) - - u32 current_total; - u32 previous_total; - - ___DSP_DUAL_16BIT_ALLOC( - count, - count_limit - ) - - u32 status_data; - - ___DSP_DUAL_16BIT_ALLOC( - status, - free4 - ) - - u32 free3; - - ___DSP_DUAL_16BIT_ALLOC( - free2, - bit_count - ) - - u32 temp_status; - - ___DSP_DUAL_16BIT_ALLOC( - next_SCB, - sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, - this_spb - ) - - u32 strm_rs_config; - u32 strm_buf_ptr; - - ___DSP_DUAL_16BIT_ALLOC( - stat_reg_addr, - fifo_pointer - ) - - ___DSP_DUAL_16BIT_ALLOC( - fifo_base_addr, - st_mo_format - ) - - u32 free1; -}; - - -/* SCB for the SP/DIF CODEC input and output */ -struct dsp_spdifoscb { - - u32 free2; - - u32 free3[4]; - - /* Need to be here for compatibility with AsynchFGTxCode */ - u32 strm_rs_config; - - u32 strm_buf_ptr; - - ___DSP_DUAL_16BIT_ALLOC( - status, - free5 - ) - - u32 free4; - - ___DSP_DUAL_16BIT_ALLOC( - next_scb, - sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, - this_spb - ) - - u32 free6[2]; - - ___DSP_DUAL_16BIT_ALLOC( - stat_reg_addr, - fifo_pointer - ) - - ___DSP_DUAL_16BIT_ALLOC( - fifo_base_addr, - st_mo_format - ) - - u32 free1; -}; - - -struct dsp_asynch_fg_rx_scb { - ___DSP_DUAL_16BIT_ALLOC( - bot_buf_mask, - buf_Mask - ) - - ___DSP_DUAL_16BIT_ALLOC( - max, - min - ) - - ___DSP_DUAL_16BIT_ALLOC( - old_producer_pointer, - hfg_scb_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - delta, - adjust_count - ) - - u32 unused2[5]; - - ___DSP_DUAL_16BIT_ALLOC( - sibling_ptr, - child_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - code_ptr, - this_ptr - ) - - u32 strm_rs_config; - - u32 strm_buf_ptr; - - u32 unused_phi_incr; - - ___DSP_DUAL_16BIT_ALLOC( - right_targ, - left_targ - ) - - ___DSP_DUAL_16BIT_ALLOC( - right_vol, - left_vol - ) -}; - - -struct dsp_asynch_fg_tx_scb { - ___DSP_DUAL_16BIT_ALLOC( - not_buf_mask, - buf_mask - ) - - ___DSP_DUAL_16BIT_ALLOC( - max, - min - ) - - ___DSP_DUAL_16BIT_ALLOC( - unused1, - hfg_scb_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - delta, - adjust_count - ) - - u32 accum_phi; - - ___DSP_DUAL_16BIT_ALLOC( - unused2, - const_one_third - ) - - u32 unused3[3]; - - ___DSP_DUAL_16BIT_ALLOC( - sibling_ptr, - child_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - codePtr, - this_ptr - ) - - u32 strm_rs_config; - - u32 strm_buf_ptr; - - u32 phi_incr; - - ___DSP_DUAL_16BIT_ALLOC( - unused_right_targ, - unused_left_targ - ) - - ___DSP_DUAL_16BIT_ALLOC( - unused_right_vol, - unused_left_vol - ) -}; - - -struct dsp_output_snoop_scb { - /* First 13 dwords from generic_scb_t */ - struct dsp_basic_dma_req basic_req; /* Optional */ - struct dsp_scatter_gather_ext sg_ext; /* Optional */ - ___DSP_DUAL_16BIT_ALLOC( - next_scb, /* REQUIRED */ - sub_list_ptr /* REQUIRED */ - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* REQUIRED */ - this_spb /* REQUIRED */ - ) - - u32 strm_rs_config; /* REQUIRED */ - u32 strm_buf_ptr; /* REQUIRED */ - - ___DSP_DUAL_16BIT_ALLOC( - init_snoop_input_link, - snoop_child_input_scb - ) - - u32 snoop_input_buf_ptr; - - ___DSP_DUAL_16BIT_ALLOC( - reserved, - input_scb - ) -}; - -struct dsp_spio_write_scb { - ___DSP_DUAL_16BIT_ALLOC( - address1, - address2 - ) - - u32 data1; - - u32 data2; - - ___DSP_DUAL_16BIT_ALLOC( - address3, - address4 - ) - - u32 data3; - - u32 data4; - - ___DSP_DUAL_16BIT_ALLOC( - unused1, - data_ptr - ) - - u32 unused2[2]; - - ___DSP_DUAL_16BIT_ALLOC( - sibling_ptr, - child_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, - this_ptr - ) - - u32 unused3[5]; -}; - -struct dsp_magic_snoop_task { - u32 i0; - u32 i1; - - u32 strm_buf_ptr1; - - u16 i2; - u16 snoop_scb; - - u32 i3; - u32 i4; - u32 i5; - u32 i6; - - u32 i7; - - ___DSP_DUAL_16BIT_ALLOC( - next_scb, - sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, - this_ptr - ) - - u32 strm_buf_config; - u32 strm_buf_ptr2; - - u32 i8; - - struct dsp_volume_control vdec_vol_ctrl; -}; - - -struct dsp_filter_scb { - ___DSP_DUAL_16BIT_ALLOC( - a0_right, /* 0x00 */ - a0_left - ) - ___DSP_DUAL_16BIT_ALLOC( - a1_right, /* 0x01 */ - a1_left - ) - ___DSP_DUAL_16BIT_ALLOC( - a2_right, /* 0x02 */ - a2_left - ) - ___DSP_DUAL_16BIT_ALLOC( - output_buf_ptr, /* 0x03 */ - init - ) - - ___DSP_DUAL_16BIT_ALLOC( - filter_unused3, /* 0x04 */ - filter_unused2 - ) - - u32 prev_sample_output1; /* 0x05 */ - u32 prev_sample_output2; /* 0x06 */ - u32 prev_sample_input1; /* 0x07 */ - u32 prev_sample_input2; /* 0x08 */ - - ___DSP_DUAL_16BIT_ALLOC( - next_scb_ptr, /* 0x09 */ - sub_list_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - entry_point, /* 0x0A */ - spb_ptr - ) - - u32 strm_rs_config; /* 0x0B */ - u32 strm_buf_ptr; /* 0x0C */ - - ___DSP_DUAL_16BIT_ALLOC( - b0_right, /* 0x0D */ - b0_left - ) - ___DSP_DUAL_16BIT_ALLOC( - b1_right, /* 0x0E */ - b1_left - ) - ___DSP_DUAL_16BIT_ALLOC( - b2_right, /* 0x0F */ - b2_left - ) -}; -#endif /* __DSP_SCB_TYPES_H__ */ diff --git a/include/sound/cs46xx_dsp_spos.h b/include/sound/cs46xx_dsp_spos.h deleted file mode 100644 index 8008c59288a6..000000000000 --- a/include/sound/cs46xx_dsp_spos.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards - * Copyright (c) by Jaroslav Kysela - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __CS46XX_DSP_SPOS_H__ -#define __CS46XX_DSP_SPOS_H__ - -#include "cs46xx_dsp_scb_types.h" -#include "cs46xx_dsp_task_types.h" - -#define SYMBOL_CONSTANT 0x0 -#define SYMBOL_SAMPLE 0x1 -#define SYMBOL_PARAMETER 0x2 -#define SYMBOL_CODE 0x3 - -#define SEGTYPE_SP_PROGRAM 0x00000001 -#define SEGTYPE_SP_PARAMETER 0x00000002 -#define SEGTYPE_SP_SAMPLE 0x00000003 -#define SEGTYPE_SP_COEFFICIENT 0x00000004 - -#define DSP_SPOS_UU 0x0deadul /* unused */ -#define DSP_SPOS_DC 0x0badul /* don't care */ -#define DSP_SPOS_DC_DC 0x0bad0badul /* don't care */ -#define DSP_SPOS_UUUU 0xdeadc0edul /* unused */ -#define DSP_SPOS_UUHI 0xdeadul -#define DSP_SPOS_UULO 0xc0edul -#define DSP_SPOS_DCDC 0x0badf1d0ul /* don't care */ -#define DSP_SPOS_DCDCHI 0x0badul -#define DSP_SPOS_DCDCLO 0xf1d0ul - -#define DSP_MAX_TASK_NAME 60 -#define DSP_MAX_SYMBOL_NAME 100 -#define DSP_MAX_SCB_NAME 60 -#define DSP_MAX_SCB_DESC 200 -#define DSP_MAX_TASK_DESC 50 - -#define DSP_MAX_PCM_CHANNELS 32 -#define DSP_MAX_SRC_NR 14 - -#define DSP_PCM_MAIN_CHANNEL 1 -#define DSP_PCM_REAR_CHANNEL 2 -#define DSP_PCM_CENTER_LFE_CHANNEL 3 -#define DSP_PCM_S71_CHANNEL 4 /* surround 7.1 */ -#define DSP_IEC958_CHANNEL 5 - -#define DSP_SPDIF_STATUS_OUTPUT_ENABLED 1 -#define DSP_SPDIF_STATUS_PLAYBACK_OPEN 2 -#define DSP_SPDIF_STATUS_HW_ENABLED 4 -#define DSP_SPDIF_STATUS_INPUT_CTRL_ENABLED 8 - -struct dsp_symbol_entry { - u32 address; - char symbol_name[DSP_MAX_SYMBOL_NAME]; - int symbol_type; - - /* initialized by driver */ - struct dsp_module_desc * module; - int deleted; -}; - -struct dsp_symbol_desc { - int nsymbols; - - struct dsp_symbol_entry *symbols; - - /* initialized by driver */ - int highest_frag_index; -}; - -struct dsp_segment_desc { - int segment_type; - u32 offset; - u32 size; - u32 * data; -}; - -struct dsp_module_desc { - char * module_name; - struct dsp_symbol_desc symbol_table; - int nsegments; - struct dsp_segment_desc * segments; - - /* initialized by driver */ - u32 overlay_begin_address; - u32 load_address; - int nfixups; -}; - -struct dsp_scb_descriptor { - char scb_name[DSP_MAX_SCB_NAME]; - u32 address; - int index; - u32 *data; - - struct dsp_scb_descriptor * sub_list_ptr; - struct dsp_scb_descriptor * next_scb_ptr; - struct dsp_scb_descriptor * parent_scb_ptr; - - struct dsp_symbol_entry * task_entry; - struct dsp_symbol_entry * scb_symbol; - - struct snd_info_entry *proc_info; - int ref_count; - - u16 volume[2]; - unsigned int deleted :1; - unsigned int updated :1; - unsigned int volume_set :1; -}; - -struct dsp_task_descriptor { - char task_name[DSP_MAX_TASK_NAME]; - int size; - u32 address; - int index; - u32 *data; -}; - -struct dsp_pcm_channel_descriptor { - int active; - int src_slot; - int pcm_slot; - u32 sample_rate; - u32 unlinked; - struct dsp_scb_descriptor * pcm_reader_scb; - struct dsp_scb_descriptor * src_scb; - struct dsp_scb_descriptor * mixer_scb; - - void * private_data; -}; - -struct dsp_spos_instance { - struct dsp_symbol_desc symbol_table; /* currently available loaded symbols in SP */ - - int nmodules; - struct dsp_module_desc * modules; /* modules loaded into SP */ - - struct dsp_segment_desc code; - - /* Main PCM playback mixer */ - struct dsp_scb_descriptor * master_mix_scb; - u16 dac_volume_right; - u16 dac_volume_left; - - /* Rear/surround PCM playback mixer */ - struct dsp_scb_descriptor * rear_mix_scb; - - /* Center/LFE mixer */ - struct dsp_scb_descriptor * center_lfe_mix_scb; - - int npcm_channels; - int nsrc_scb; - struct dsp_pcm_channel_descriptor pcm_channels[DSP_MAX_PCM_CHANNELS]; - int src_scb_slots[DSP_MAX_SRC_NR]; - - /* cache this symbols */ - struct dsp_symbol_entry * null_algorithm; /* used by PCMreaderSCB's */ - struct dsp_symbol_entry * s16_up; /* used by SRCtaskSCB's */ - - /* proc fs */ - struct snd_card *snd_card; - struct snd_info_entry * proc_dsp_dir; - struct snd_info_entry * proc_sym_info_entry; - struct snd_info_entry * proc_modules_info_entry; - struct snd_info_entry * proc_parameter_dump_info_entry; - struct snd_info_entry * proc_sample_dump_info_entry; - - /* SCB's descriptors */ - int nscb; - int scb_highest_frag_index; - struct dsp_scb_descriptor scbs[DSP_MAX_SCB_DESC]; - struct snd_info_entry * proc_scb_info_entry; - struct dsp_scb_descriptor * the_null_scb; - - /* Task's descriptors */ - int ntask; - struct dsp_task_descriptor tasks[DSP_MAX_TASK_DESC]; - struct snd_info_entry * proc_task_info_entry; - - /* SPDIF status */ - int spdif_status_out; - int spdif_status_in; - u16 spdif_input_volume_right; - u16 spdif_input_volume_left; - /* spdif channel status, - left right and user validity bits */ - unsigned int spdif_csuv_default; - unsigned int spdif_csuv_stream; - - /* SPDIF input sample rate converter */ - struct dsp_scb_descriptor * spdif_in_src; - /* SPDIF input asynch. receiver */ - struct dsp_scb_descriptor * asynch_rx_scb; - - /* Capture record mixer SCB */ - struct dsp_scb_descriptor * record_mixer_scb; - - /* CODEC input SCB */ - struct dsp_scb_descriptor * codec_in_scb; - - /* reference snooper */ - struct dsp_scb_descriptor * ref_snoop_scb; - - /* SPDIF output PCM reference */ - struct dsp_scb_descriptor * spdif_pcm_input_scb; - - /* asynch TX task */ - struct dsp_scb_descriptor * asynch_tx_scb; - - /* record sources */ - struct dsp_scb_descriptor * pcm_input; - struct dsp_scb_descriptor * adc_input; - - int spdif_in_sample_rate; -}; - -#endif /* __DSP_SPOS_H__ */ diff --git a/include/sound/cs46xx_dsp_task_types.h b/include/sound/cs46xx_dsp_task_types.h deleted file mode 100644 index 5cf920bfda27..000000000000 --- a/include/sound/cs46xx_dsp_task_types.h +++ /dev/null @@ -1,252 +0,0 @@ -/* - * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards - * Copyright (c) by Jaroslav Kysela - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * - * NOTE: comments are copy/paste from cwcemb80.lst - * provided by Tom Woller at Cirrus (my only - * documentation about the SP OS running inside - * the DSP) - */ - -#ifndef __CS46XX_DSP_TASK_TYPES_H__ -#define __CS46XX_DSP_TASK_TYPES_H__ - -#include "cs46xx_dsp_scb_types.h" - -/********************************************************************************************* -Example hierarchy of stream control blocks in the SP - -hfgTree -Ptr____Call (c) - \ - -------+------ ------------- ------------- ------------- ----- -| SBlaster IF |______\| Foreground |___\| Middlegr'nd |___\| Background |___\| Nul | -| |Goto /| tree header |g /| tree header |g /| tree header |g /| SCB |r - -------------- (g) ------------- ------------- ------------- ----- - |c |c |c |c - | | | | - \/ ------------- ------------- ------------- - | Foreground |_\ | Middlegr'nd |_\ | Background |_\ - | tree |g/ | tree |g/ | tree |g/ - ------------- ------------- ------------- - |c |c |c - | | | - \/ \/ \/ - -*********************************************************************************************/ - -#define HFG_FIRST_EXECUTE_MODE 0x0001 -#define HFG_FIRST_EXECUTE_MODE_BIT 0 -#define HFG_CONTEXT_SWITCH_MODE 0x0002 -#define HFG_CONTEXT_SWITCH_MODE_BIT 1 - -#define MAX_FG_STACK_SIZE 32 /* THESE NEED TO BE COMPUTED PROPERLY */ -#define MAX_MG_STACK_SIZE 16 -#define MAX_BG_STACK_SIZE 9 -#define MAX_HFG_STACK_SIZE 4 - -#define SLEEP_ACTIVE_INCREMENT 0 /* Enable task tree thread to go to sleep - This should only ever be used on the Background thread */ -#define STANDARD_ACTIVE_INCREMENT 1 /* Task tree thread normal operation */ -#define SUSPEND_ACTIVE_INCREMENT 2 /* Cause execution to suspend in the task tree thread - This should only ever be used on the Background thread */ - -#define HOSTFLAGS_DISABLE_BG_SLEEP 0 /* Host-controlled flag that determines whether we go to sleep - at the end of BG */ - -/* Minimal context save area for Hyper Forground */ -struct dsp_hf_save_area { - u32 r10_save; - u32 r54_save; - u32 r98_save; - - ___DSP_DUAL_16BIT_ALLOC( - status_save, - ind_save - ) - - ___DSP_DUAL_16BIT_ALLOC( - rci1_save, - rci0_save - ) - - u32 r32_save; - u32 r76_save; - u32 rsd2_save; - - ___DSP_DUAL_16BIT_ALLOC( - rsi2_save, /* See TaskTreeParameterBlock for - remainder of registers */ - rsa2Save - ) - /* saved as part of HFG context */ -}; - - -/* Task link data structure */ -struct dsp_tree_link { - ___DSP_DUAL_16BIT_ALLOC( - /* Pointer to sibling task control block */ - next_scb, - /* Pointer to child task control block */ - sub_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - /* Pointer to code entry point */ - entry_point, - /* Pointer to local data */ - this_spb - ) -}; - - -struct dsp_task_tree_data { - ___DSP_DUAL_16BIT_ALLOC( - /* Initial tock count; controls task tree execution rate */ - tock_count_limit, - /* Tock down counter */ - tock_count - ) - - /* Add to ActiveCount when TockCountLimit reached: - Subtract on task tree termination */ - ___DSP_DUAL_16BIT_ALLOC( - active_tncrement, - /* Number of pending activations for task tree */ - active_count - ) - - ___DSP_DUAL_16BIT_ALLOC( - /* BitNumber to enable modification of correct bit in ActiveTaskFlags */ - active_bit, - /* Pointer to OS location for indicating current activity on task level */ - active_task_flags_ptr - ) - - /* Data structure for controlling movement of memory blocks:- - currently unused */ - ___DSP_DUAL_16BIT_ALLOC( - mem_upd_ptr, - /* Data structure for controlling synchronous link update */ - link_upd_ptr - ) - - ___DSP_DUAL_16BIT_ALLOC( - /* Save area for remainder of full context. */ - save_area, - /* Address of start of local stack for data storage */ - data_stack_base_ptr - ) - -}; - - -struct dsp_interval_timer_data -{ - /* These data items have the same relative locations to those */ - ___DSP_DUAL_16BIT_ALLOC( - interval_timer_period, - itd_unused - ) - - /* used for this data in the SPOS control block for SPOS 1.0 */ - ___DSP_DUAL_16BIT_ALLOC( - num_FG_ticks_this_interval, - num_intervals - ) -}; - - -/* This structure contains extra storage for the task tree - Currently, this additional data is related only to a full context save */ -struct dsp_task_tree_context_block { - /* Up to 10 values are saved onto the stack. 8 for the task tree, 1 for - The access to the context switch (call or interrupt), and 1 spare that - users should never use. This last may be required by the system */ - ___DSP_DUAL_16BIT_ALLOC( - stack1, - stack0 - ) - ___DSP_DUAL_16BIT_ALLOC( - stack3, - stack2 - ) - ___DSP_DUAL_16BIT_ALLOC( - stack5, - stack4 - ) - ___DSP_DUAL_16BIT_ALLOC( - stack7, - stack6 - ) - ___DSP_DUAL_16BIT_ALLOC( - stack9, - stack8 - ) - - u32 saverfe; - - /* Value may be overwriten by stack save algorithm. - Retain the size of the stack data saved here if used */ - ___DSP_DUAL_16BIT_ALLOC( - reserved1, - stack_size - ) - u32 saverba; /* (HFG) */ - u32 saverdc; - u32 savers_config_23; /* (HFG) */ - u32 savers_DMA23; /* (HFG) */ - u32 saversa0; - u32 saversi0; - u32 saversa1; - u32 saversi1; - u32 saversa3; - u32 saversd0; - u32 saversd1; - u32 saversd3; - u32 savers_config01; - u32 savers_DMA01; - u32 saveacc0hl; - u32 saveacc1hl; - u32 saveacc0xacc1x; - u32 saveacc2hl; - u32 saveacc3hl; - u32 saveacc2xacc3x; - u32 saveaux0hl; - u32 saveaux1hl; - u32 saveaux0xaux1x; - u32 saveaux2hl; - u32 saveaux3hl; - u32 saveaux2xaux3x; - u32 savershouthl; - u32 savershoutxmacmode; -}; - - -struct dsp_task_tree_control_block { - struct dsp_hf_save_area context; - struct dsp_tree_link links; - struct dsp_task_tree_data data; - struct dsp_task_tree_context_block context_blk; - struct dsp_interval_timer_data int_timer; -}; - - -#endif /* __DSP_TASK_TYPES_H__ */ diff --git a/include/sound/trident.h b/include/sound/trident.h deleted file mode 100644 index 06f0478103db..000000000000 --- a/include/sound/trident.h +++ /dev/null @@ -1,444 +0,0 @@ -#ifndef __SOUND_TRIDENT_H -#define __SOUND_TRIDENT_H - -/* - * audio@tridentmicro.com - * Fri Feb 19 15:55:28 MST 1999 - * Definitions for Trident 4DWave DX/NX chips - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "pcm.h" -#include "mpu401.h" -#include "ac97_codec.h" -#include "util_mem.h" - -#define TRIDENT_DEVICE_ID_DX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_DX) -#define TRIDENT_DEVICE_ID_NX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_NX) -#define TRIDENT_DEVICE_ID_SI7018 ((PCI_VENDOR_ID_SI<<16)|PCI_DEVICE_ID_SI_7018) - -#define SNDRV_TRIDENT_VOICE_TYPE_PCM 0 -#define SNDRV_TRIDENT_VOICE_TYPE_SYNTH 1 -#define SNDRV_TRIDENT_VOICE_TYPE_MIDI 2 - -#define SNDRV_TRIDENT_VFLG_RUNNING (1<<0) - -/* TLB code constants */ -#define SNDRV_TRIDENT_PAGE_SIZE 4096 -#define SNDRV_TRIDENT_PAGE_SHIFT 12 -#define SNDRV_TRIDENT_PAGE_MASK ((1<port + (x)) - -#define ID_4DWAVE_DX 0x2000 -#define ID_4DWAVE_NX 0x2001 - -/* Bank definitions */ - -#define T4D_BANK_A 0 -#define T4D_BANK_B 1 -#define T4D_NUM_BANKS 2 - -/* Register definitions */ - -/* Global registers */ - -enum global_control_bits { - CHANNEL_IDX = 0x0000003f, - OVERRUN_IE = 0x00000400, /* interrupt enable: capture overrun */ - UNDERRUN_IE = 0x00000800, /* interrupt enable: playback underrun */ - ENDLP_IE = 0x00001000, /* interrupt enable: end of buffer */ - MIDLP_IE = 0x00002000, /* interrupt enable: middle buffer */ - ETOG_IE = 0x00004000, /* interrupt enable: envelope toggling */ - EDROP_IE = 0x00008000, /* interrupt enable: envelope drop */ - BANK_B_EN = 0x00010000, /* SiS: enable bank B (64 channels) */ - PCMIN_B_MIX = 0x00020000, /* SiS: PCM IN B mixing enable */ - I2S_OUT_ASSIGN = 0x00040000, /* SiS: I2S Out contains surround PCM */ - SPDIF_OUT_ASSIGN= 0x00080000, /* SiS: 0=S/PDIF L/R | 1=PCM Out FIFO */ - MAIN_OUT_ASSIGN = 0x00100000, /* SiS: 0=PCM Out FIFO | 1=MMC Out buffer */ -}; - -enum miscint_bits { - PB_UNDERRUN_IRQ = 0x00000001, REC_OVERRUN_IRQ = 0x00000002, - SB_IRQ = 0x00000004, MPU401_IRQ = 0x00000008, - OPL3_IRQ = 0x00000010, ADDRESS_IRQ = 0x00000020, - ENVELOPE_IRQ = 0x00000040, PB_UNDERRUN = 0x00000100, - REC_OVERRUN = 0x00000200, MIXER_UNDERFLOW = 0x00000400, - MIXER_OVERFLOW = 0x00000800, NX_SB_IRQ_DISABLE = 0x00001000, - ST_TARGET_REACHED = 0x00008000, - PB_24K_MODE = 0x00010000, ST_IRQ_EN = 0x00800000, - ACGPIO_IRQ = 0x01000000 -}; - -/* T2 legacy dma control registers. */ -#define LEGACY_DMAR0 0x00 // ADR0 -#define LEGACY_DMAR4 0x04 // CNT0 -#define LEGACY_DMAR6 0x06 // CNT0 - High bits -#define LEGACY_DMAR11 0x0b // MOD -#define LEGACY_DMAR15 0x0f // MMR - -#define T4D_START_A 0x80 -#define T4D_STOP_A 0x84 -#define T4D_DLY_A 0x88 -#define T4D_SIGN_CSO_A 0x8c -#define T4D_CSPF_A 0x90 -#define T4D_CSPF_B 0xbc -#define T4D_CEBC_A 0x94 -#define T4D_AINT_A 0x98 -#define T4D_AINTEN_A 0x9c -#define T4D_LFO_GC_CIR 0xa0 -#define T4D_MUSICVOL_WAVEVOL 0xa8 -#define T4D_SBDELTA_DELTA_R 0xac -#define T4D_MISCINT 0xb0 -#define T4D_START_B 0xb4 -#define T4D_STOP_B 0xb8 -#define T4D_SBBL_SBCL 0xc0 -#define T4D_SBCTRL_SBE2R_SBDD 0xc4 -#define T4D_STIMER 0xc8 -#define T4D_AINT_B 0xd8 -#define T4D_AINTEN_B 0xdc -#define T4D_RCI 0x70 - -/* MPU-401 UART */ -#define T4D_MPU401_BASE 0x20 -#define T4D_MPUR0 0x20 -#define T4D_MPUR1 0x21 -#define T4D_MPUR2 0x22 -#define T4D_MPUR3 0x23 - -/* S/PDIF Registers */ -#define NX_SPCTRL_SPCSO 0x24 -#define NX_SPLBA 0x28 -#define NX_SPESO 0x2c -#define NX_SPCSTATUS 0x64 - -/* Joystick */ -#define GAMEPORT_GCR 0x30 -#define GAMEPORT_MODE_ADC 0x80 -#define GAMEPORT_LEGACY 0x31 -#define GAMEPORT_AXES 0x34 - -/* NX Specific Registers */ -#define NX_TLBC 0x6c - -/* Channel Registers */ - -#define CH_START 0xe0 - -#define CH_DX_CSO_ALPHA_FMS 0xe0 -#define CH_DX_ESO_DELTA 0xe8 -#define CH_DX_FMC_RVOL_CVOL 0xec - -#define CH_NX_DELTA_CSO 0xe0 -#define CH_NX_DELTA_ESO 0xe8 -#define CH_NX_ALPHA_FMS_FMC_RVOL_CVOL 0xec - -#define CH_LBA 0xe4 -#define CH_GVSEL_PAN_VOL_CTRL_EC 0xf0 -#define CH_EBUF1 0xf4 -#define CH_EBUF2 0xf8 - -/* AC-97 Registers */ - -#define DX_ACR0_AC97_W 0x40 -#define DX_ACR1_AC97_R 0x44 -#define DX_ACR2_AC97_COM_STAT 0x48 - -#define NX_ACR0_AC97_COM_STAT 0x40 -#define NX_ACR1_AC97_W 0x44 -#define NX_ACR2_AC97_R_PRIMARY 0x48 -#define NX_ACR3_AC97_R_SECONDARY 0x4c - -#define SI_AC97_WRITE 0x40 -#define SI_AC97_READ 0x44 -#define SI_SERIAL_INTF_CTRL 0x48 -#define SI_AC97_GPIO 0x4c -#define SI_ASR0 0x50 -#define SI_SPDIF_CS 0x70 -#define SI_GPIO 0x7c - -enum trident_nx_ac97_bits { - /* ACR1-3 */ - NX_AC97_BUSY_WRITE = 0x0800, - NX_AC97_BUSY_READ = 0x0800, - NX_AC97_BUSY_DATA = 0x0400, - NX_AC97_WRITE_SECONDARY = 0x0100, - /* ACR0 */ - NX_AC97_SECONDARY_READY = 0x0040, - NX_AC97_SECONDARY_RECORD = 0x0020, - NX_AC97_SURROUND_OUTPUT = 0x0010, - NX_AC97_PRIMARY_READY = 0x0008, - NX_AC97_PRIMARY_RECORD = 0x0004, - NX_AC97_PCM_OUTPUT = 0x0002, - NX_AC97_WARM_RESET = 0x0001 -}; - -enum trident_dx_ac97_bits { - DX_AC97_BUSY_WRITE = 0x8000, - DX_AC97_BUSY_READ = 0x8000, - DX_AC97_READY = 0x0010, - DX_AC97_RECORD = 0x0008, - DX_AC97_PLAYBACK = 0x0002 -}; - -enum sis7018_ac97_bits { - SI_AC97_BUSY_WRITE = 0x00008000, - SI_AC97_AUDIO_BUSY = 0x00004000, - SI_AC97_MODEM_BUSY = 0x00002000, - SI_AC97_BUSY_READ = 0x00008000, - SI_AC97_SECONDARY = 0x00000080, -}; - -enum serial_intf_ctrl_bits { - WARM_RESET = 0x00000001, - COLD_RESET = 0x00000002, - I2S_CLOCK = 0x00000004, - PCM_SEC_AC97 = 0x00000008, - AC97_DBL_RATE = 0x00000010, - SPDIF_EN = 0x00000020, - I2S_OUTPUT_EN = 0x00000040, - I2S_INPUT_EN = 0x00000080, - PCMIN = 0x00000100, - LINE1IN = 0x00000200, - MICIN = 0x00000400, - LINE2IN = 0x00000800, - HEAD_SET_IN = 0x00001000, - GPIOIN = 0x00002000, - /* 7018 spec says id = 01 but the demo board routed to 10 - SECONDARY_ID= 0x00004000, */ - SECONDARY_ID = 0x00004000, - PCMOUT = 0x00010000, - SURROUT = 0x00020000, - CENTEROUT = 0x00040000, - LFEOUT = 0x00080000, - LINE1OUT = 0x00100000, - LINE2OUT = 0x00200000, - GPIOOUT = 0x00400000, - SI_AC97_PRIMARY_READY = 0x01000000, - SI_AC97_SECONDARY_READY = 0x02000000, - SI_AC97_POWERDOWN = 0x04000000, -}; - -/* PCM defaults */ - -#define T4D_DEFAULT_PCM_VOL 10 /* 0 - 255 */ -#define T4D_DEFAULT_PCM_PAN 0 /* 0 - 127 */ -#define T4D_DEFAULT_PCM_RVOL 127 /* 0 - 127 */ -#define T4D_DEFAULT_PCM_CVOL 127 /* 0 - 127 */ - -struct snd_trident; -struct snd_trident_voice; -struct snd_trident_pcm_mixer; - -struct snd_trident_port { - struct snd_midi_channel_set * chset; - struct snd_trident * trident; - int mode; /* operation mode */ - int client; /* sequencer client number */ - int port; /* sequencer port number */ - unsigned int midi_has_voices: 1; -}; - -struct snd_trident_memblk_arg { - short first_page, last_page; -}; - -struct snd_trident_tlb { - unsigned int * entries; /* 16k-aligned TLB table */ - dma_addr_t entries_dmaaddr; /* 16k-aligned PCI address to TLB table */ - unsigned long * shadow_entries; /* shadow entries with virtual addresses */ - struct snd_dma_buffer buffer; - struct snd_util_memhdr * memhdr; /* page allocation list */ - struct snd_dma_buffer silent_page; -}; - -struct snd_trident_voice { - unsigned int number; - unsigned int use: 1, - pcm: 1, - synth:1, - midi: 1; - unsigned int flags; - unsigned char client; - unsigned char port; - unsigned char index; - - struct snd_trident_sample_ops *sample_ops; - - /* channel parameters */ - unsigned int CSO; /* 24 bits (16 on DX) */ - unsigned int ESO; /* 24 bits (16 on DX) */ - unsigned int LBA; /* 30 bits */ - unsigned short EC; /* 12 bits */ - unsigned short Alpha; /* 12 bits */ - unsigned short Delta; /* 16 bits */ - unsigned short Attribute; /* 16 bits - SiS 7018 */ - unsigned short Vol; /* 12 bits (6.6) */ - unsigned char Pan; /* 7 bits (1.4.2) */ - unsigned char GVSel; /* 1 bit */ - unsigned char RVol; /* 7 bits (5.2) */ - unsigned char CVol; /* 7 bits (5.2) */ - unsigned char FMC; /* 2 bits */ - unsigned char CTRL; /* 4 bits */ - unsigned char FMS; /* 4 bits */ - unsigned char LFO; /* 8 bits */ - - unsigned int negCSO; /* nonzero - use negative CSO */ - - struct snd_util_memblk *memblk; /* memory block if TLB enabled */ - - /* PCM data */ - - struct snd_trident *trident; - struct snd_pcm_substream *substream; - struct snd_trident_voice *extra; /* extra PCM voice (acts as interrupt generator) */ - unsigned int running: 1, - capture: 1, - spdif: 1, - foldback: 1, - isync: 1, - isync2: 1, - isync3: 1; - int foldback_chan; /* foldback subdevice number */ - unsigned int stimer; /* global sample timer (to detect spurious interrupts) */ - unsigned int spurious_threshold; /* spurious threshold */ - unsigned int isync_mark; - unsigned int isync_max; - unsigned int isync_ESO; - - /* --- */ - - void *private_data; - void (*private_free)(struct snd_trident_voice *voice); -}; - -struct snd_4dwave { - int seq_client; - - struct snd_trident_port seq_ports[4]; - struct snd_trident_voice voices[64]; - - int ChanSynthCount; /* number of allocated synth channels */ - int max_size; /* maximum synth memory size in bytes */ - int current_size; /* current allocated synth mem in bytes */ -}; - -struct snd_trident_pcm_mixer { - struct snd_trident_voice *voice; /* active voice */ - unsigned short vol; /* front volume */ - unsigned char pan; /* pan control */ - unsigned char rvol; /* rear volume */ - unsigned char cvol; /* center volume */ - unsigned char pad; -}; - -struct snd_trident { - int irq; - - unsigned int device; /* device ID */ - - unsigned char bDMAStart; - - unsigned long port; - unsigned long midi_port; - - unsigned int spurious_irq_count; - unsigned int spurious_irq_max_delta; - - struct snd_trident_tlb tlb; /* TLB entries for NX cards */ - - unsigned char spdif_ctrl; - unsigned char spdif_pcm_ctrl; - unsigned int spdif_bits; - unsigned int spdif_pcm_bits; - struct snd_kcontrol *spdif_pcm_ctl; /* S/PDIF settings */ - unsigned int ac97_ctrl; - - unsigned int ChanMap[2]; /* allocation map for hardware channels */ - - int ChanPCM; /* max number of PCM channels */ - int ChanPCMcnt; /* actual number of PCM channels */ - - unsigned int ac97_detect: 1; /* 1 = AC97 in detection phase */ - unsigned int in_suspend: 1; /* 1 during suspend/resume */ - - struct snd_4dwave synth; /* synth specific variables */ - - spinlock_t event_lock; - spinlock_t voice_alloc; - - struct snd_dma_device dma_dev; - - struct pci_dev *pci; - struct snd_card *card; - struct snd_pcm *pcm; /* ADC/DAC PCM */ - struct snd_pcm *foldback; /* Foldback PCM */ - struct snd_pcm *spdif; /* SPDIF PCM */ - struct snd_rawmidi *rmidi; - - struct snd_ac97_bus *ac97_bus; - struct snd_ac97 *ac97; - struct snd_ac97 *ac97_sec; - - unsigned int musicvol_wavevol; - struct snd_trident_pcm_mixer pcm_mixer[32]; - struct snd_kcontrol *ctl_vol; /* front volume */ - struct snd_kcontrol *ctl_pan; /* pan */ - struct snd_kcontrol *ctl_rvol; /* rear volume */ - struct snd_kcontrol *ctl_cvol; /* center volume */ - - spinlock_t reg_lock; - - struct gameport *gameport; -}; - -int snd_trident_create(struct snd_card *card, - struct pci_dev *pci, - int pcm_streams, - int pcm_spdif_device, - int max_wavetable_size, - struct snd_trident ** rtrident); -int snd_trident_create_gameport(struct snd_trident *trident); - -int snd_trident_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm); -int snd_trident_foldback_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm); -int snd_trident_spdif_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm); -int snd_trident_attach_synthesizer(struct snd_trident * trident); -struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, - int client, int port); -void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice); -void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice); -void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice); -void snd_trident_write_voice_regs(struct snd_trident * trident, struct snd_trident_voice *voice); -extern const struct dev_pm_ops snd_trident_pm; - -/* TLB memory allocation */ -struct snd_util_memblk *snd_trident_alloc_pages(struct snd_trident *trident, - struct snd_pcm_substream *substream); -int snd_trident_free_pages(struct snd_trident *trident, struct snd_util_memblk *blk); -struct snd_util_memblk *snd_trident_synth_alloc(struct snd_trident *trident, unsigned int size); -int snd_trident_synth_free(struct snd_trident *trident, struct snd_util_memblk *blk); -int snd_trident_synth_copy_from_user(struct snd_trident *trident, struct snd_util_memblk *blk, - int offset, const char __user *data, int size); - -#endif /* __SOUND_TRIDENT_H */ diff --git a/include/sound/ymfpci.h b/include/sound/ymfpci.h deleted file mode 100644 index 238f118de6e1..000000000000 --- a/include/sound/ymfpci.h +++ /dev/null @@ -1,389 +0,0 @@ -#ifndef __SOUND_YMFPCI_H -#define __SOUND_YMFPCI_H - -/* - * Copyright (c) by Jaroslav Kysela - * Definitions for Yahama YMF724/740/744/754 chips - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "pcm.h" -#include "rawmidi.h" -#include "ac97_codec.h" -#include "timer.h" -#include - -/* - * Direct registers - */ - -#define YMFREG(chip, reg) (chip->port + YDSXGR_##reg) - -#define YDSXGR_INTFLAG 0x0004 -#define YDSXGR_ACTIVITY 0x0006 -#define YDSXGR_GLOBALCTRL 0x0008 -#define YDSXGR_ZVCTRL 0x000A -#define YDSXGR_TIMERCTRL 0x0010 -#define YDSXGR_TIMERCOUNT 0x0012 -#define YDSXGR_SPDIFOUTCTRL 0x0018 -#define YDSXGR_SPDIFOUTSTATUS 0x001C -#define YDSXGR_EEPROMCTRL 0x0020 -#define YDSXGR_SPDIFINCTRL 0x0034 -#define YDSXGR_SPDIFINSTATUS 0x0038 -#define YDSXGR_DSPPROGRAMDL 0x0048 -#define YDSXGR_DLCNTRL 0x004C -#define YDSXGR_GPIOININTFLAG 0x0050 -#define YDSXGR_GPIOININTENABLE 0x0052 -#define YDSXGR_GPIOINSTATUS 0x0054 -#define YDSXGR_GPIOOUTCTRL 0x0056 -#define YDSXGR_GPIOFUNCENABLE 0x0058 -#define YDSXGR_GPIOTYPECONFIG 0x005A -#define YDSXGR_AC97CMDDATA 0x0060 -#define YDSXGR_AC97CMDADR 0x0062 -#define YDSXGR_PRISTATUSDATA 0x0064 -#define YDSXGR_PRISTATUSADR 0x0066 -#define YDSXGR_SECSTATUSDATA 0x0068 -#define YDSXGR_SECSTATUSADR 0x006A -#define YDSXGR_SECCONFIG 0x0070 -#define YDSXGR_LEGACYOUTVOL 0x0080 -#define YDSXGR_LEGACYOUTVOLL 0x0080 -#define YDSXGR_LEGACYOUTVOLR 0x0082 -#define YDSXGR_NATIVEDACOUTVOL 0x0084 -#define YDSXGR_NATIVEDACOUTVOLL 0x0084 -#define YDSXGR_NATIVEDACOUTVOLR 0x0086 -#define YDSXGR_ZVOUTVOL 0x0088 -#define YDSXGR_ZVOUTVOLL 0x0088 -#define YDSXGR_ZVOUTVOLR 0x008A -#define YDSXGR_SECADCOUTVOL 0x008C -#define YDSXGR_SECADCOUTVOLL 0x008C -#define YDSXGR_SECADCOUTVOLR 0x008E -#define YDSXGR_PRIADCOUTVOL 0x0090 -#define YDSXGR_PRIADCOUTVOLL 0x0090 -#define YDSXGR_PRIADCOUTVOLR 0x0092 -#define YDSXGR_LEGACYLOOPVOL 0x0094 -#define YDSXGR_LEGACYLOOPVOLL 0x0094 -#define YDSXGR_LEGACYLOOPVOLR 0x0096 -#define YDSXGR_NATIVEDACLOOPVOL 0x0098 -#define YDSXGR_NATIVEDACLOOPVOLL 0x0098 -#define YDSXGR_NATIVEDACLOOPVOLR 0x009A -#define YDSXGR_ZVLOOPVOL 0x009C -#define YDSXGR_ZVLOOPVOLL 0x009E -#define YDSXGR_ZVLOOPVOLR 0x009E -#define YDSXGR_SECADCLOOPVOL 0x00A0 -#define YDSXGR_SECADCLOOPVOLL 0x00A0 -#define YDSXGR_SECADCLOOPVOLR 0x00A2 -#define YDSXGR_PRIADCLOOPVOL 0x00A4 -#define YDSXGR_PRIADCLOOPVOLL 0x00A4 -#define YDSXGR_PRIADCLOOPVOLR 0x00A6 -#define YDSXGR_NATIVEADCINVOL 0x00A8 -#define YDSXGR_NATIVEADCINVOLL 0x00A8 -#define YDSXGR_NATIVEADCINVOLR 0x00AA -#define YDSXGR_NATIVEDACINVOL 0x00AC -#define YDSXGR_NATIVEDACINVOLL 0x00AC -#define YDSXGR_NATIVEDACINVOLR 0x00AE -#define YDSXGR_BUF441OUTVOL 0x00B0 -#define YDSXGR_BUF441OUTVOLL 0x00B0 -#define YDSXGR_BUF441OUTVOLR 0x00B2 -#define YDSXGR_BUF441LOOPVOL 0x00B4 -#define YDSXGR_BUF441LOOPVOLL 0x00B4 -#define YDSXGR_BUF441LOOPVOLR 0x00B6 -#define YDSXGR_SPDIFOUTVOL 0x00B8 -#define YDSXGR_SPDIFOUTVOLL 0x00B8 -#define YDSXGR_SPDIFOUTVOLR 0x00BA -#define YDSXGR_SPDIFLOOPVOL 0x00BC -#define YDSXGR_SPDIFLOOPVOLL 0x00BC -#define YDSXGR_SPDIFLOOPVOLR 0x00BE -#define YDSXGR_ADCSLOTSR 0x00C0 -#define YDSXGR_RECSLOTSR 0x00C4 -#define YDSXGR_ADCFORMAT 0x00C8 -#define YDSXGR_RECFORMAT 0x00CC -#define YDSXGR_P44SLOTSR 0x00D0 -#define YDSXGR_STATUS 0x0100 -#define YDSXGR_CTRLSELECT 0x0104 -#define YDSXGR_MODE 0x0108 -#define YDSXGR_SAMPLECOUNT 0x010C -#define YDSXGR_NUMOFSAMPLES 0x0110 -#define YDSXGR_CONFIG 0x0114 -#define YDSXGR_PLAYCTRLSIZE 0x0140 -#define YDSXGR_RECCTRLSIZE 0x0144 -#define YDSXGR_EFFCTRLSIZE 0x0148 -#define YDSXGR_WORKSIZE 0x014C -#define YDSXGR_MAPOFREC 0x0150 -#define YDSXGR_MAPOFEFFECT 0x0154 -#define YDSXGR_PLAYCTRLBASE 0x0158 -#define YDSXGR_RECCTRLBASE 0x015C -#define YDSXGR_EFFCTRLBASE 0x0160 -#define YDSXGR_WORKBASE 0x0164 -#define YDSXGR_DSPINSTRAM 0x1000 -#define YDSXGR_CTRLINSTRAM 0x4000 - -#define YDSXG_AC97READCMD 0x8000 -#define YDSXG_AC97WRITECMD 0x0000 - -#define PCIR_DSXG_LEGACY 0x40 -#define PCIR_DSXG_ELEGACY 0x42 -#define PCIR_DSXG_CTRL 0x48 -#define PCIR_DSXG_PWRCTRL1 0x4a -#define PCIR_DSXG_PWRCTRL2 0x4e -#define PCIR_DSXG_FMBASE 0x60 -#define PCIR_DSXG_SBBASE 0x62 -#define PCIR_DSXG_MPU401BASE 0x64 -#define PCIR_DSXG_JOYBASE 0x66 - -#define YDSXG_DSPLENGTH 0x0080 -#define YDSXG_CTRLLENGTH 0x3000 - -#define YDSXG_DEFAULT_WORK_SIZE 0x0400 - -#define YDSXG_PLAYBACK_VOICES 64 -#define YDSXG_CAPTURE_VOICES 2 -#define YDSXG_EFFECT_VOICES 5 - -#define YMFPCI_LEGACY_SBEN (1 << 0) /* soundblaster enable */ -#define YMFPCI_LEGACY_FMEN (1 << 1) /* OPL3 enable */ -#define YMFPCI_LEGACY_JPEN (1 << 2) /* joystick enable */ -#define YMFPCI_LEGACY_MEN (1 << 3) /* MPU401 enable */ -#define YMFPCI_LEGACY_MIEN (1 << 4) /* MPU RX irq enable */ -#define YMFPCI_LEGACY_IOBITS (1 << 5) /* i/o bits range, 0 = 16bit, 1 =10bit */ -#define YMFPCI_LEGACY_SDMA (3 << 6) /* SB DMA select */ -#define YMFPCI_LEGACY_SBIRQ (7 << 8) /* SB IRQ select */ -#define YMFPCI_LEGACY_MPUIRQ (7 << 11) /* MPU IRQ select */ -#define YMFPCI_LEGACY_SIEN (1 << 14) /* serialized IRQ */ -#define YMFPCI_LEGACY_LAD (1 << 15) /* legacy audio disable */ - -#define YMFPCI_LEGACY2_FMIO (3 << 0) /* OPL3 i/o address (724/740) */ -#define YMFPCI_LEGACY2_SBIO (3 << 2) /* SB i/o address (724/740) */ -#define YMFPCI_LEGACY2_MPUIO (3 << 4) /* MPU401 i/o address (724/740) */ -#define YMFPCI_LEGACY2_JSIO (3 << 6) /* joystick i/o address (724/740) */ -#define YMFPCI_LEGACY2_MAIM (1 << 8) /* MPU401 ack intr mask */ -#define YMFPCI_LEGACY2_SMOD (3 << 11) /* SB DMA mode */ -#define YMFPCI_LEGACY2_SBVER (3 << 13) /* SB version select */ -#define YMFPCI_LEGACY2_IMOD (1 << 15) /* legacy IRQ mode */ -/* SIEN:IMOD 0:0 = legacy irq, 0:1 = INTA, 1:0 = serialized IRQ */ - -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) -#define SUPPORT_JOYSTICK -#endif - -/* - * - */ - -struct snd_ymfpci_playback_bank { - u32 format; - u32 loop_default; - u32 base; /* 32-bit address */ - u32 loop_start; /* 32-bit offset */ - u32 loop_end; /* 32-bit offset */ - u32 loop_frac; /* 8-bit fraction - loop_start */ - u32 delta_end; /* pitch delta end */ - u32 lpfK_end; - u32 eg_gain_end; - u32 left_gain_end; - u32 right_gain_end; - u32 eff1_gain_end; - u32 eff2_gain_end; - u32 eff3_gain_end; - u32 lpfQ; - u32 status; - u32 num_of_frames; - u32 loop_count; - u32 start; - u32 start_frac; - u32 delta; - u32 lpfK; - u32 eg_gain; - u32 left_gain; - u32 right_gain; - u32 eff1_gain; - u32 eff2_gain; - u32 eff3_gain; - u32 lpfD1; - u32 lpfD2; - }; - -struct snd_ymfpci_capture_bank { - u32 base; /* 32-bit address */ - u32 loop_end; /* 32-bit offset */ - u32 start; /* 32-bit offset */ - u32 num_of_loops; /* counter */ -}; - -struct snd_ymfpci_effect_bank { - u32 base; /* 32-bit address */ - u32 loop_end; /* 32-bit offset */ - u32 start; /* 32-bit offset */ - u32 temp; -}; - -struct snd_ymfpci_pcm; -struct snd_ymfpci; - -enum snd_ymfpci_voice_type { - YMFPCI_PCM, - YMFPCI_SYNTH, - YMFPCI_MIDI -}; - -struct snd_ymfpci_voice { - struct snd_ymfpci *chip; - int number; - unsigned int use: 1, - pcm: 1, - synth: 1, - midi: 1; - struct snd_ymfpci_playback_bank *bank; - dma_addr_t bank_addr; - void (*interrupt)(struct snd_ymfpci *chip, struct snd_ymfpci_voice *voice); - struct snd_ymfpci_pcm *ypcm; -}; - -enum snd_ymfpci_pcm_type { - PLAYBACK_VOICE, - CAPTURE_REC, - CAPTURE_AC97, - EFFECT_DRY_LEFT, - EFFECT_DRY_RIGHT, - EFFECT_EFF1, - EFFECT_EFF2, - EFFECT_EFF3 -}; - -struct snd_ymfpci_pcm { - struct snd_ymfpci *chip; - enum snd_ymfpci_pcm_type type; - struct snd_pcm_substream *substream; - struct snd_ymfpci_voice *voices[2]; /* playback only */ - unsigned int running: 1, - use_441_slot: 1, - output_front: 1, - output_rear: 1, - swap_rear: 1; - unsigned int update_pcm_vol; - u32 period_size; /* cached from runtime->period_size */ - u32 buffer_size; /* cached from runtime->buffer_size */ - u32 period_pos; - u32 last_pos; - u32 capture_bank_number; - u32 shift; -}; - -struct snd_ymfpci { - int irq; - - unsigned int device_id; /* PCI device ID */ - unsigned char rev; /* PCI revision */ - unsigned long reg_area_phys; - void __iomem *reg_area_virt; - struct resource *res_reg_area; - struct resource *fm_res; - struct resource *mpu_res; - - unsigned short old_legacy_ctrl; -#ifdef SUPPORT_JOYSTICK - struct gameport *gameport; -#endif - - struct snd_dma_buffer work_ptr; - - unsigned int bank_size_playback; - unsigned int bank_size_capture; - unsigned int bank_size_effect; - unsigned int work_size; - - void *bank_base_playback; - void *bank_base_capture; - void *bank_base_effect; - void *work_base; - dma_addr_t bank_base_playback_addr; - dma_addr_t bank_base_capture_addr; - dma_addr_t bank_base_effect_addr; - dma_addr_t work_base_addr; - struct snd_dma_buffer ac3_tmp_base; - - u32 *ctrl_playback; - struct snd_ymfpci_playback_bank *bank_playback[YDSXG_PLAYBACK_VOICES][2]; - struct snd_ymfpci_capture_bank *bank_capture[YDSXG_CAPTURE_VOICES][2]; - struct snd_ymfpci_effect_bank *bank_effect[YDSXG_EFFECT_VOICES][2]; - - int start_count; - - u32 active_bank; - struct snd_ymfpci_voice voices[64]; - int src441_used; - - struct snd_ac97_bus *ac97_bus; - struct snd_ac97 *ac97; - struct snd_rawmidi *rawmidi; - struct snd_timer *timer; - unsigned int timer_ticks; - - struct pci_dev *pci; - struct snd_card *card; - struct snd_pcm *pcm; - struct snd_pcm *pcm2; - struct snd_pcm *pcm_spdif; - struct snd_pcm *pcm_4ch; - struct snd_pcm_substream *capture_substream[YDSXG_CAPTURE_VOICES]; - struct snd_pcm_substream *effect_substream[YDSXG_EFFECT_VOICES]; - struct snd_kcontrol *ctl_vol_recsrc; - struct snd_kcontrol *ctl_vol_adcrec; - struct snd_kcontrol *ctl_vol_spdifrec; - unsigned short spdif_bits, spdif_pcm_bits; - struct snd_kcontrol *spdif_pcm_ctl; - int mode_dup4ch; - int rear_opened; - int spdif_opened; - struct snd_ymfpci_pcm_mixer { - u16 left; - u16 right; - struct snd_kcontrol *ctl; - } pcm_mixer[32]; - - spinlock_t reg_lock; - spinlock_t voice_lock; - wait_queue_head_t interrupt_sleep; - atomic_t interrupt_sleep_count; - struct snd_info_entry *proc_entry; - const struct firmware *dsp_microcode; - const struct firmware *controller_microcode; - -#ifdef CONFIG_PM - u32 *saved_regs; - u32 saved_ydsxgr_mode; - u16 saved_dsxg_legacy; - u16 saved_dsxg_elegacy; -#endif -}; - -int snd_ymfpci_create(struct snd_card *card, - struct pci_dev *pci, - unsigned short old_legacy_ctrl, - struct snd_ymfpci ** rcodec); -void snd_ymfpci_free_gameport(struct snd_ymfpci *chip); - -extern const struct dev_pm_ops snd_ymfpci_pm; - -int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); -int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); -int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); -int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); -int snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch); -int snd_ymfpci_timer(struct snd_ymfpci *chip, int device); - -#endif /* __SOUND_YMFPCI_H */ diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 00e03bc9a762..1e007c736a8b 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include "cs46xx.h" #include MODULE_AUTHOR("Jaroslav Kysela "); diff --git a/sound/pci/cs46xx/cs46xx.h b/sound/pci/cs46xx/cs46xx.h new file mode 100644 index 000000000000..29d8a8da1ba7 --- /dev/null +++ b/sound/pci/cs46xx/cs46xx.h @@ -0,0 +1,1744 @@ +#ifndef __SOUND_CS46XX_H +#define __SOUND_CS46XX_H + +/* + * Copyright (c) by Jaroslav Kysela , + * Cirrus Logic, Inc. + * Definitions for Cirrus Logic CS46xx chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include "cs46xx_dsp_spos.h" + +/* + * Direct registers + */ + +/* + * The following define the offsets of the registers accessed via base address + * register zero on the CS46xx part. + */ +#define BA0_HISR 0x00000000 +#define BA0_HSR0 0x00000004 +#define BA0_HICR 0x00000008 +#define BA0_DMSR 0x00000100 +#define BA0_HSAR 0x00000110 +#define BA0_HDAR 0x00000114 +#define BA0_HDMR 0x00000118 +#define BA0_HDCR 0x0000011C +#define BA0_PFMC 0x00000200 +#define BA0_PFCV1 0x00000204 +#define BA0_PFCV2 0x00000208 +#define BA0_PCICFG00 0x00000300 +#define BA0_PCICFG04 0x00000304 +#define BA0_PCICFG08 0x00000308 +#define BA0_PCICFG0C 0x0000030C +#define BA0_PCICFG10 0x00000310 +#define BA0_PCICFG14 0x00000314 +#define BA0_PCICFG18 0x00000318 +#define BA0_PCICFG1C 0x0000031C +#define BA0_PCICFG20 0x00000320 +#define BA0_PCICFG24 0x00000324 +#define BA0_PCICFG28 0x00000328 +#define BA0_PCICFG2C 0x0000032C +#define BA0_PCICFG30 0x00000330 +#define BA0_PCICFG34 0x00000334 +#define BA0_PCICFG38 0x00000338 +#define BA0_PCICFG3C 0x0000033C +#define BA0_CLKCR1 0x00000400 +#define BA0_CLKCR2 0x00000404 +#define BA0_PLLM 0x00000408 +#define BA0_PLLCC 0x0000040C +#define BA0_FRR 0x00000410 +#define BA0_CFL1 0x00000414 +#define BA0_CFL2 0x00000418 +#define BA0_SERMC1 0x00000420 +#define BA0_SERMC2 0x00000424 +#define BA0_SERC1 0x00000428 +#define BA0_SERC2 0x0000042C +#define BA0_SERC3 0x00000430 +#define BA0_SERC4 0x00000434 +#define BA0_SERC5 0x00000438 +#define BA0_SERBSP 0x0000043C +#define BA0_SERBST 0x00000440 +#define BA0_SERBCM 0x00000444 +#define BA0_SERBAD 0x00000448 +#define BA0_SERBCF 0x0000044C +#define BA0_SERBWP 0x00000450 +#define BA0_SERBRP 0x00000454 +#ifndef NO_CS4612 +#define BA0_ASER_FADDR 0x00000458 +#endif +#define BA0_ACCTL 0x00000460 +#define BA0_ACSTS 0x00000464 +#define BA0_ACOSV 0x00000468 +#define BA0_ACCAD 0x0000046C +#define BA0_ACCDA 0x00000470 +#define BA0_ACISV 0x00000474 +#define BA0_ACSAD 0x00000478 +#define BA0_ACSDA 0x0000047C +#define BA0_JSPT 0x00000480 +#define BA0_JSCTL 0x00000484 +#define BA0_JSC1 0x00000488 +#define BA0_JSC2 0x0000048C +#define BA0_MIDCR 0x00000490 +#define BA0_MIDSR 0x00000494 +#define BA0_MIDWP 0x00000498 +#define BA0_MIDRP 0x0000049C +#define BA0_JSIO 0x000004A0 +#ifndef NO_CS4612 +#define BA0_ASER_MASTER 0x000004A4 +#endif +#define BA0_CFGI 0x000004B0 +#define BA0_SSVID 0x000004B4 +#define BA0_GPIOR 0x000004B8 +#ifndef NO_CS4612 +#define BA0_EGPIODR 0x000004BC +#define BA0_EGPIOPTR 0x000004C0 +#define BA0_EGPIOTR 0x000004C4 +#define BA0_EGPIOWR 0x000004C8 +#define BA0_EGPIOSR 0x000004CC +#define BA0_SERC6 0x000004D0 +#define BA0_SERC7 0x000004D4 +#define BA0_SERACC 0x000004D8 +#define BA0_ACCTL2 0x000004E0 +#define BA0_ACSTS2 0x000004E4 +#define BA0_ACOSV2 0x000004E8 +#define BA0_ACCAD2 0x000004EC +#define BA0_ACCDA2 0x000004F0 +#define BA0_ACISV2 0x000004F4 +#define BA0_ACSAD2 0x000004F8 +#define BA0_ACSDA2 0x000004FC +#define BA0_IOTAC0 0x00000500 +#define BA0_IOTAC1 0x00000504 +#define BA0_IOTAC2 0x00000508 +#define BA0_IOTAC3 0x0000050C +#define BA0_IOTAC4 0x00000510 +#define BA0_IOTAC5 0x00000514 +#define BA0_IOTAC6 0x00000518 +#define BA0_IOTAC7 0x0000051C +#define BA0_IOTAC8 0x00000520 +#define BA0_IOTAC9 0x00000524 +#define BA0_IOTAC10 0x00000528 +#define BA0_IOTAC11 0x0000052C +#define BA0_IOTFR0 0x00000540 +#define BA0_IOTFR1 0x00000544 +#define BA0_IOTFR2 0x00000548 +#define BA0_IOTFR3 0x0000054C +#define BA0_IOTFR4 0x00000550 +#define BA0_IOTFR5 0x00000554 +#define BA0_IOTFR6 0x00000558 +#define BA0_IOTFR7 0x0000055C +#define BA0_IOTFIFO 0x00000580 +#define BA0_IOTRRD 0x00000584 +#define BA0_IOTFP 0x00000588 +#define BA0_IOTCR 0x0000058C +#define BA0_DPCID 0x00000590 +#define BA0_DPCIA 0x00000594 +#define BA0_DPCIC 0x00000598 +#define BA0_PCPCIR 0x00000600 +#define BA0_PCPCIG 0x00000604 +#define BA0_PCPCIEN 0x00000608 +#define BA0_EPCIPMC 0x00000610 +#endif + +/* + * The following define the offsets of the registers and memories accessed via + * base address register one on the CS46xx part. + */ +#define BA1_SP_DMEM0 0x00000000 +#define BA1_SP_DMEM1 0x00010000 +#define BA1_SP_PMEM 0x00020000 +#define BA1_SP_REG 0x00030000 +#define BA1_SPCR 0x00030000 +#define BA1_DREG 0x00030004 +#define BA1_DSRWP 0x00030008 +#define BA1_TWPR 0x0003000C +#define BA1_SPWR 0x00030010 +#define BA1_SPIR 0x00030014 +#define BA1_FGR1 0x00030020 +#define BA1_SPCS 0x00030028 +#define BA1_SDSR 0x0003002C +#define BA1_FRMT 0x00030030 +#define BA1_FRCC 0x00030034 +#define BA1_FRSC 0x00030038 +#define BA1_OMNI_MEM 0x000E0000 + + +/* + * The following defines are for the flags in the host interrupt status + * register. + */ +#define HISR_VC_MASK 0x0000FFFF +#define HISR_VC0 0x00000001 +#define HISR_VC1 0x00000002 +#define HISR_VC2 0x00000004 +#define HISR_VC3 0x00000008 +#define HISR_VC4 0x00000010 +#define HISR_VC5 0x00000020 +#define HISR_VC6 0x00000040 +#define HISR_VC7 0x00000080 +#define HISR_VC8 0x00000100 +#define HISR_VC9 0x00000200 +#define HISR_VC10 0x00000400 +#define HISR_VC11 0x00000800 +#define HISR_VC12 0x00001000 +#define HISR_VC13 0x00002000 +#define HISR_VC14 0x00004000 +#define HISR_VC15 0x00008000 +#define HISR_INT0 0x00010000 +#define HISR_INT1 0x00020000 +#define HISR_DMAI 0x00040000 +#define HISR_FROVR 0x00080000 +#define HISR_MIDI 0x00100000 +#ifdef NO_CS4612 +#define HISR_RESERVED 0x0FE00000 +#else +#define HISR_SBINT 0x00200000 +#define HISR_RESERVED 0x0FC00000 +#endif +#define HISR_H0P 0x40000000 +#define HISR_INTENA 0x80000000 + +/* + * The following defines are for the flags in the host signal register 0. + */ +#define HSR0_VC_MASK 0xFFFFFFFF +#define HSR0_VC16 0x00000001 +#define HSR0_VC17 0x00000002 +#define HSR0_VC18 0x00000004 +#define HSR0_VC19 0x00000008 +#define HSR0_VC20 0x00000010 +#define HSR0_VC21 0x00000020 +#define HSR0_VC22 0x00000040 +#define HSR0_VC23 0x00000080 +#define HSR0_VC24 0x00000100 +#define HSR0_VC25 0x00000200 +#define HSR0_VC26 0x00000400 +#define HSR0_VC27 0x00000800 +#define HSR0_VC28 0x00001000 +#define HSR0_VC29 0x00002000 +#define HSR0_VC30 0x00004000 +#define HSR0_VC31 0x00008000 +#define HSR0_VC32 0x00010000 +#define HSR0_VC33 0x00020000 +#define HSR0_VC34 0x00040000 +#define HSR0_VC35 0x00080000 +#define HSR0_VC36 0x00100000 +#define HSR0_VC37 0x00200000 +#define HSR0_VC38 0x00400000 +#define HSR0_VC39 0x00800000 +#define HSR0_VC40 0x01000000 +#define HSR0_VC41 0x02000000 +#define HSR0_VC42 0x04000000 +#define HSR0_VC43 0x08000000 +#define HSR0_VC44 0x10000000 +#define HSR0_VC45 0x20000000 +#define HSR0_VC46 0x40000000 +#define HSR0_VC47 0x80000000 + +/* + * The following defines are for the flags in the host interrupt control + * register. + */ +#define HICR_IEV 0x00000001 +#define HICR_CHGM 0x00000002 + +/* + * The following defines are for the flags in the DMA status register. + */ +#define DMSR_HP 0x00000001 +#define DMSR_HR 0x00000002 +#define DMSR_SP 0x00000004 +#define DMSR_SR 0x00000008 + +/* + * The following defines are for the flags in the host DMA source address + * register. + */ +#define HSAR_HOST_ADDR_MASK 0xFFFFFFFF +#define HSAR_DSP_ADDR_MASK 0x0000FFFF +#define HSAR_MEMID_MASK 0x000F0000 +#define HSAR_MEMID_SP_DMEM0 0x00000000 +#define HSAR_MEMID_SP_DMEM1 0x00010000 +#define HSAR_MEMID_SP_PMEM 0x00020000 +#define HSAR_MEMID_SP_DEBUG 0x00030000 +#define HSAR_MEMID_OMNI_MEM 0x000E0000 +#define HSAR_END 0x40000000 +#define HSAR_ERR 0x80000000 + +/* + * The following defines are for the flags in the host DMA destination address + * register. + */ +#define HDAR_HOST_ADDR_MASK 0xFFFFFFFF +#define HDAR_DSP_ADDR_MASK 0x0000FFFF +#define HDAR_MEMID_MASK 0x000F0000 +#define HDAR_MEMID_SP_DMEM0 0x00000000 +#define HDAR_MEMID_SP_DMEM1 0x00010000 +#define HDAR_MEMID_SP_PMEM 0x00020000 +#define HDAR_MEMID_SP_DEBUG 0x00030000 +#define HDAR_MEMID_OMNI_MEM 0x000E0000 +#define HDAR_END 0x40000000 +#define HDAR_ERR 0x80000000 + +/* + * The following defines are for the flags in the host DMA control register. + */ +#define HDMR_AC_MASK 0x0000F000 +#define HDMR_AC_8_16 0x00001000 +#define HDMR_AC_M_S 0x00002000 +#define HDMR_AC_B_L 0x00004000 +#define HDMR_AC_S_U 0x00008000 + +/* + * The following defines are for the flags in the host DMA control register. + */ +#define HDCR_COUNT_MASK 0x000003FF +#define HDCR_DONE 0x00004000 +#define HDCR_OPT 0x00008000 +#define HDCR_WBD 0x00400000 +#define HDCR_WBS 0x00800000 +#define HDCR_DMS_MASK 0x07000000 +#define HDCR_DMS_LINEAR 0x00000000 +#define HDCR_DMS_16_DWORDS 0x01000000 +#define HDCR_DMS_32_DWORDS 0x02000000 +#define HDCR_DMS_64_DWORDS 0x03000000 +#define HDCR_DMS_128_DWORDS 0x04000000 +#define HDCR_DMS_256_DWORDS 0x05000000 +#define HDCR_DMS_512_DWORDS 0x06000000 +#define HDCR_DMS_1024_DWORDS 0x07000000 +#define HDCR_DH 0x08000000 +#define HDCR_SMS_MASK 0x70000000 +#define HDCR_SMS_LINEAR 0x00000000 +#define HDCR_SMS_16_DWORDS 0x10000000 +#define HDCR_SMS_32_DWORDS 0x20000000 +#define HDCR_SMS_64_DWORDS 0x30000000 +#define HDCR_SMS_128_DWORDS 0x40000000 +#define HDCR_SMS_256_DWORDS 0x50000000 +#define HDCR_SMS_512_DWORDS 0x60000000 +#define HDCR_SMS_1024_DWORDS 0x70000000 +#define HDCR_SH 0x80000000 +#define HDCR_COUNT_SHIFT 0 + +/* + * The following defines are for the flags in the performance monitor control + * register. + */ +#define PFMC_C1SS_MASK 0x0000001F +#define PFMC_C1EV 0x00000020 +#define PFMC_C1RS 0x00008000 +#define PFMC_C2SS_MASK 0x001F0000 +#define PFMC_C2EV 0x00200000 +#define PFMC_C2RS 0x80000000 +#define PFMC_C1SS_SHIFT 0 +#define PFMC_C2SS_SHIFT 16 +#define PFMC_BUS_GRANT 0 +#define PFMC_GRANT_AFTER_REQ 1 +#define PFMC_TRANSACTION 2 +#define PFMC_DWORD_TRANSFER 3 +#define PFMC_SLAVE_READ 4 +#define PFMC_SLAVE_WRITE 5 +#define PFMC_PREEMPTION 6 +#define PFMC_DISCONNECT_RETRY 7 +#define PFMC_INTERRUPT 8 +#define PFMC_BUS_OWNERSHIP 9 +#define PFMC_TRANSACTION_LAG 10 +#define PFMC_PCI_CLOCK 11 +#define PFMC_SERIAL_CLOCK 12 +#define PFMC_SP_CLOCK 13 + +/* + * The following defines are for the flags in the performance counter value 1 + * register. + */ +#define PFCV1_PC1V_MASK 0xFFFFFFFF +#define PFCV1_PC1V_SHIFT 0 + +/* + * The following defines are for the flags in the performance counter value 2 + * register. + */ +#define PFCV2_PC2V_MASK 0xFFFFFFFF +#define PFCV2_PC2V_SHIFT 0 + +/* + * The following defines are for the flags in the clock control register 1. + */ +#define CLKCR1_OSCS 0x00000001 +#define CLKCR1_OSCP 0x00000002 +#define CLKCR1_PLLSS_MASK 0x0000000C +#define CLKCR1_PLLSS_SERIAL 0x00000000 +#define CLKCR1_PLLSS_CRYSTAL 0x00000004 +#define CLKCR1_PLLSS_PCI 0x00000008 +#define CLKCR1_PLLSS_RESERVED 0x0000000C +#define CLKCR1_PLLP 0x00000010 +#define CLKCR1_SWCE 0x00000020 +#define CLKCR1_PLLOS 0x00000040 + +/* + * The following defines are for the flags in the clock control register 2. + */ +#define CLKCR2_PDIVS_MASK 0x0000000F +#define CLKCR2_PDIVS_1 0x00000001 +#define CLKCR2_PDIVS_2 0x00000002 +#define CLKCR2_PDIVS_4 0x00000004 +#define CLKCR2_PDIVS_7 0x00000007 +#define CLKCR2_PDIVS_8 0x00000008 +#define CLKCR2_PDIVS_16 0x00000000 + +/* + * The following defines are for the flags in the PLL multiplier register. + */ +#define PLLM_MASK 0x000000FF +#define PLLM_SHIFT 0 + +/* + * The following defines are for the flags in the PLL capacitor coefficient + * register. + */ +#define PLLCC_CDR_MASK 0x00000007 +#ifndef NO_CS4610 +#define PLLCC_CDR_240_350_MHZ 0x00000000 +#define PLLCC_CDR_184_265_MHZ 0x00000001 +#define PLLCC_CDR_144_205_MHZ 0x00000002 +#define PLLCC_CDR_111_160_MHZ 0x00000003 +#define PLLCC_CDR_87_123_MHZ 0x00000004 +#define PLLCC_CDR_67_96_MHZ 0x00000005 +#define PLLCC_CDR_52_74_MHZ 0x00000006 +#define PLLCC_CDR_45_58_MHZ 0x00000007 +#endif +#ifndef NO_CS4612 +#define PLLCC_CDR_271_398_MHZ 0x00000000 +#define PLLCC_CDR_227_330_MHZ 0x00000001 +#define PLLCC_CDR_167_239_MHZ 0x00000002 +#define PLLCC_CDR_150_215_MHZ 0x00000003 +#define PLLCC_CDR_107_154_MHZ 0x00000004 +#define PLLCC_CDR_98_140_MHZ 0x00000005 +#define PLLCC_CDR_73_104_MHZ 0x00000006 +#define PLLCC_CDR_63_90_MHZ 0x00000007 +#endif +#define PLLCC_LPF_MASK 0x000000F8 +#ifndef NO_CS4610 +#define PLLCC_LPF_23850_60000_KHZ 0x00000000 +#define PLLCC_LPF_7960_26290_KHZ 0x00000008 +#define PLLCC_LPF_4160_10980_KHZ 0x00000018 +#define PLLCC_LPF_1740_4580_KHZ 0x00000038 +#define PLLCC_LPF_724_1910_KHZ 0x00000078 +#define PLLCC_LPF_317_798_KHZ 0x000000F8 +#endif +#ifndef NO_CS4612 +#define PLLCC_LPF_25580_64530_KHZ 0x00000000 +#define PLLCC_LPF_14360_37270_KHZ 0x00000008 +#define PLLCC_LPF_6100_16020_KHZ 0x00000018 +#define PLLCC_LPF_2540_6690_KHZ 0x00000038 +#define PLLCC_LPF_1050_2780_KHZ 0x00000078 +#define PLLCC_LPF_450_1160_KHZ 0x000000F8 +#endif + +/* + * The following defines are for the flags in the feature reporting register. + */ +#define FRR_FAB_MASK 0x00000003 +#define FRR_MASK_MASK 0x0000001C +#ifdef NO_CS4612 +#define FRR_CFOP_MASK 0x000000E0 +#else +#define FRR_CFOP_MASK 0x00000FE0 +#endif +#define FRR_CFOP_NOT_DVD 0x00000020 +#define FRR_CFOP_A3D 0x00000040 +#define FRR_CFOP_128_PIN 0x00000080 +#ifndef NO_CS4612 +#define FRR_CFOP_CS4280 0x00000800 +#endif +#define FRR_FAB_SHIFT 0 +#define FRR_MASK_SHIFT 2 +#define FRR_CFOP_SHIFT 5 + +/* + * The following defines are for the flags in the configuration load 1 + * register. + */ +#define CFL1_CLOCK_SOURCE_MASK 0x00000003 +#define CFL1_CLOCK_SOURCE_CS423X 0x00000000 +#define CFL1_CLOCK_SOURCE_AC97 0x00000001 +#define CFL1_CLOCK_SOURCE_CRYSTAL 0x00000002 +#define CFL1_CLOCK_SOURCE_DUAL_AC97 0x00000003 +#define CFL1_VALID_DATA_MASK 0x000000FF + +/* + * The following defines are for the flags in the configuration load 2 + * register. + */ +#define CFL2_VALID_DATA_MASK 0x000000FF + +/* + * The following defines are for the flags in the serial port master control + * register 1. + */ +#define SERMC1_MSPE 0x00000001 +#define SERMC1_PTC_MASK 0x0000000E +#define SERMC1_PTC_CS423X 0x00000000 +#define SERMC1_PTC_AC97 0x00000002 +#define SERMC1_PTC_DAC 0x00000004 +#define SERMC1_PLB 0x00000010 +#define SERMC1_XLB 0x00000020 + +/* + * The following defines are for the flags in the serial port master control + * register 2. + */ +#define SERMC2_LROE 0x00000001 +#define SERMC2_MCOE 0x00000002 +#define SERMC2_MCDIV 0x00000004 + +/* + * The following defines are for the flags in the serial port 1 configuration + * register. + */ +#define SERC1_SO1EN 0x00000001 +#define SERC1_SO1F_MASK 0x0000000E +#define SERC1_SO1F_CS423X 0x00000000 +#define SERC1_SO1F_AC97 0x00000002 +#define SERC1_SO1F_DAC 0x00000004 +#define SERC1_SO1F_SPDIF 0x00000006 + +/* + * The following defines are for the flags in the serial port 2 configuration + * register. + */ +#define SERC2_SI1EN 0x00000001 +#define SERC2_SI1F_MASK 0x0000000E +#define SERC2_SI1F_CS423X 0x00000000 +#define SERC2_SI1F_AC97 0x00000002 +#define SERC2_SI1F_ADC 0x00000004 +#define SERC2_SI1F_SPDIF 0x00000006 + +/* + * The following defines are for the flags in the serial port 3 configuration + * register. + */ +#define SERC3_SO2EN 0x00000001 +#define SERC3_SO2F_MASK 0x00000006 +#define SERC3_SO2F_DAC 0x00000000 +#define SERC3_SO2F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port 4 configuration + * register. + */ +#define SERC4_SO3EN 0x00000001 +#define SERC4_SO3F_MASK 0x00000006 +#define SERC4_SO3F_DAC 0x00000000 +#define SERC4_SO3F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port 5 configuration + * register. + */ +#define SERC5_SI2EN 0x00000001 +#define SERC5_SI2F_MASK 0x00000006 +#define SERC5_SI2F_ADC 0x00000000 +#define SERC5_SI2F_SPDIF 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor sample + * pointer register. + */ +#define SERBSP_FSP_MASK 0x0000000F +#define SERBSP_FSP_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor status + * register. + */ +#define SERBST_RRDY 0x00000001 +#define SERBST_WBSY 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor command + * register. + */ +#define SERBCM_RDC 0x00000001 +#define SERBCM_WRC 0x00000002 + +/* + * The following defines are for the flags in the serial port backdoor address + * register. + */ +#ifdef NO_CS4612 +#define SERBAD_FAD_MASK 0x000000FF +#else +#define SERBAD_FAD_MASK 0x000001FF +#endif +#define SERBAD_FAD_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor + * configuration register. + */ +#define SERBCF_HBP 0x00000001 + +/* + * The following defines are for the flags in the serial port backdoor write + * port register. + */ +#define SERBWP_FWD_MASK 0x000FFFFF +#define SERBWP_FWD_SHIFT 0 + +/* + * The following defines are for the flags in the serial port backdoor read + * port register. + */ +#define SERBRP_FRD_MASK 0x000FFFFF +#define SERBRP_FRD_SHIFT 0 + +/* + * The following defines are for the flags in the async FIFO address register. + */ +#ifndef NO_CS4612 +#define ASER_FADDR_A1_MASK 0x000001FF +#define ASER_FADDR_EN1 0x00008000 +#define ASER_FADDR_A2_MASK 0x01FF0000 +#define ASER_FADDR_EN2 0x80000000 +#define ASER_FADDR_A1_SHIFT 0 +#define ASER_FADDR_A2_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the AC97 control register. + */ +#define ACCTL_RSTN 0x00000001 +#define ACCTL_ESYN 0x00000002 +#define ACCTL_VFRM 0x00000004 +#define ACCTL_DCV 0x00000008 +#define ACCTL_CRW 0x00000010 +#define ACCTL_ASYN 0x00000020 +#ifndef NO_CS4612 +#define ACCTL_TC 0x00000040 +#endif + +/* + * The following defines are for the flags in the AC97 status register. + */ +#define ACSTS_CRDY 0x00000001 +#define ACSTS_VSTS 0x00000002 +#ifndef NO_CS4612 +#define ACSTS_WKUP 0x00000004 +#endif + +/* + * The following defines are for the flags in the AC97 output slot valid + * register. + */ +#define ACOSV_SLV3 0x00000001 +#define ACOSV_SLV4 0x00000002 +#define ACOSV_SLV5 0x00000004 +#define ACOSV_SLV6 0x00000008 +#define ACOSV_SLV7 0x00000010 +#define ACOSV_SLV8 0x00000020 +#define ACOSV_SLV9 0x00000040 +#define ACOSV_SLV10 0x00000080 +#define ACOSV_SLV11 0x00000100 +#define ACOSV_SLV12 0x00000200 + +/* + * The following defines are for the flags in the AC97 command address + * register. + */ +#define ACCAD_CI_MASK 0x0000007F +#define ACCAD_CI_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 command data register. + */ +#define ACCDA_CD_MASK 0x0000FFFF +#define ACCDA_CD_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 input slot valid + * register. + */ +#define ACISV_ISV3 0x00000001 +#define ACISV_ISV4 0x00000002 +#define ACISV_ISV5 0x00000004 +#define ACISV_ISV6 0x00000008 +#define ACISV_ISV7 0x00000010 +#define ACISV_ISV8 0x00000020 +#define ACISV_ISV9 0x00000040 +#define ACISV_ISV10 0x00000080 +#define ACISV_ISV11 0x00000100 +#define ACISV_ISV12 0x00000200 + +/* + * The following defines are for the flags in the AC97 status address + * register. + */ +#define ACSAD_SI_MASK 0x0000007F +#define ACSAD_SI_SHIFT 0 + +/* + * The following defines are for the flags in the AC97 status data register. + */ +#define ACSDA_SD_MASK 0x0000FFFF +#define ACSDA_SD_SHIFT 0 + +/* + * The following defines are for the flags in the joystick poll/trigger + * register. + */ +#define JSPT_CAX 0x00000001 +#define JSPT_CAY 0x00000002 +#define JSPT_CBX 0x00000004 +#define JSPT_CBY 0x00000008 +#define JSPT_BA1 0x00000010 +#define JSPT_BA2 0x00000020 +#define JSPT_BB1 0x00000040 +#define JSPT_BB2 0x00000080 + +/* + * The following defines are for the flags in the joystick control register. + */ +#define JSCTL_SP_MASK 0x00000003 +#define JSCTL_SP_SLOW 0x00000000 +#define JSCTL_SP_MEDIUM_SLOW 0x00000001 +#define JSCTL_SP_MEDIUM_FAST 0x00000002 +#define JSCTL_SP_FAST 0x00000003 +#define JSCTL_ARE 0x00000004 + +/* + * The following defines are for the flags in the joystick coordinate pair 1 + * readback register. + */ +#define JSC1_Y1V_MASK 0x0000FFFF +#define JSC1_X1V_MASK 0xFFFF0000 +#define JSC1_Y1V_SHIFT 0 +#define JSC1_X1V_SHIFT 16 + +/* + * The following defines are for the flags in the joystick coordinate pair 2 + * readback register. + */ +#define JSC2_Y2V_MASK 0x0000FFFF +#define JSC2_X2V_MASK 0xFFFF0000 +#define JSC2_Y2V_SHIFT 0 +#define JSC2_X2V_SHIFT 16 + +/* + * The following defines are for the flags in the MIDI control register. + */ +#define MIDCR_TXE 0x00000001 /* Enable transmitting. */ +#define MIDCR_RXE 0x00000002 /* Enable receiving. */ +#define MIDCR_RIE 0x00000004 /* Interrupt upon tx ready. */ +#define MIDCR_TIE 0x00000008 /* Interrupt upon rx ready. */ +#define MIDCR_MLB 0x00000010 /* Enable midi loopback. */ +#define MIDCR_MRST 0x00000020 /* Reset interface. */ + +/* + * The following defines are for the flags in the MIDI status register. + */ +#define MIDSR_TBF 0x00000001 /* Tx FIFO is full. */ +#define MIDSR_RBE 0x00000002 /* Rx FIFO is empty. */ + +/* + * The following defines are for the flags in the MIDI write port register. + */ +#define MIDWP_MWD_MASK 0x000000FF +#define MIDWP_MWD_SHIFT 0 + +/* + * The following defines are for the flags in the MIDI read port register. + */ +#define MIDRP_MRD_MASK 0x000000FF +#define MIDRP_MRD_SHIFT 0 + +/* + * The following defines are for the flags in the joystick GPIO register. + */ +#define JSIO_DAX 0x00000001 +#define JSIO_DAY 0x00000002 +#define JSIO_DBX 0x00000004 +#define JSIO_DBY 0x00000008 +#define JSIO_AXOE 0x00000010 +#define JSIO_AYOE 0x00000020 +#define JSIO_BXOE 0x00000040 +#define JSIO_BYOE 0x00000080 + +/* + * The following defines are for the flags in the master async/sync serial + * port enable register. + */ +#ifndef NO_CS4612 +#define ASER_MASTER_ME 0x00000001 +#endif + +/* + * The following defines are for the flags in the configuration interface + * register. + */ +#define CFGI_CLK 0x00000001 +#define CFGI_DOUT 0x00000002 +#define CFGI_DIN_EEN 0x00000004 +#define CFGI_EELD 0x00000008 + +/* + * The following defines are for the flags in the subsystem ID and vendor ID + * register. + */ +#define SSVID_VID_MASK 0x0000FFFF +#define SSVID_SID_MASK 0xFFFF0000 +#define SSVID_VID_SHIFT 0 +#define SSVID_SID_SHIFT 16 + +/* + * The following defines are for the flags in the GPIO pin interface register. + */ +#define GPIOR_VOLDN 0x00000001 +#define GPIOR_VOLUP 0x00000002 +#define GPIOR_SI2D 0x00000004 +#define GPIOR_SI2OE 0x00000008 + +/* + * The following defines are for the flags in the extended GPIO pin direction + * register. + */ +#ifndef NO_CS4612 +#define EGPIODR_GPOE0 0x00000001 +#define EGPIODR_GPOE1 0x00000002 +#define EGPIODR_GPOE2 0x00000004 +#define EGPIODR_GPOE3 0x00000008 +#define EGPIODR_GPOE4 0x00000010 +#define EGPIODR_GPOE5 0x00000020 +#define EGPIODR_GPOE6 0x00000040 +#define EGPIODR_GPOE7 0x00000080 +#define EGPIODR_GPOE8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin polarity/ + * type register. + */ +#ifndef NO_CS4612 +#define EGPIOPTR_GPPT0 0x00000001 +#define EGPIOPTR_GPPT1 0x00000002 +#define EGPIOPTR_GPPT2 0x00000004 +#define EGPIOPTR_GPPT3 0x00000008 +#define EGPIOPTR_GPPT4 0x00000010 +#define EGPIOPTR_GPPT5 0x00000020 +#define EGPIOPTR_GPPT6 0x00000040 +#define EGPIOPTR_GPPT7 0x00000080 +#define EGPIOPTR_GPPT8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin sticky + * register. + */ +#ifndef NO_CS4612 +#define EGPIOTR_GPS0 0x00000001 +#define EGPIOTR_GPS1 0x00000002 +#define EGPIOTR_GPS2 0x00000004 +#define EGPIOTR_GPS3 0x00000008 +#define EGPIOTR_GPS4 0x00000010 +#define EGPIOTR_GPS5 0x00000020 +#define EGPIOTR_GPS6 0x00000040 +#define EGPIOTR_GPS7 0x00000080 +#define EGPIOTR_GPS8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO ping wakeup + * register. + */ +#ifndef NO_CS4612 +#define EGPIOWR_GPW0 0x00000001 +#define EGPIOWR_GPW1 0x00000002 +#define EGPIOWR_GPW2 0x00000004 +#define EGPIOWR_GPW3 0x00000008 +#define EGPIOWR_GPW4 0x00000010 +#define EGPIOWR_GPW5 0x00000020 +#define EGPIOWR_GPW6 0x00000040 +#define EGPIOWR_GPW7 0x00000080 +#define EGPIOWR_GPW8 0x00000100 +#endif + +/* + * The following defines are for the flags in the extended GPIO pin status + * register. + */ +#ifndef NO_CS4612 +#define EGPIOSR_GPS0 0x00000001 +#define EGPIOSR_GPS1 0x00000002 +#define EGPIOSR_GPS2 0x00000004 +#define EGPIOSR_GPS3 0x00000008 +#define EGPIOSR_GPS4 0x00000010 +#define EGPIOSR_GPS5 0x00000020 +#define EGPIOSR_GPS6 0x00000040 +#define EGPIOSR_GPS7 0x00000080 +#define EGPIOSR_GPS8 0x00000100 +#endif + +/* + * The following defines are for the flags in the serial port 6 configuration + * register. + */ +#ifndef NO_CS4612 +#define SERC6_ASDO2EN 0x00000001 +#endif + +/* + * The following defines are for the flags in the serial port 7 configuration + * register. + */ +#ifndef NO_CS4612 +#define SERC7_ASDI2EN 0x00000001 +#define SERC7_POSILB 0x00000002 +#define SERC7_SIPOLB 0x00000004 +#define SERC7_SOSILB 0x00000008 +#define SERC7_SISOLB 0x00000010 +#endif + +/* + * The following defines are for the flags in the serial port AC link + * configuration register. + */ +#ifndef NO_CS4612 +#define SERACC_CHIP_TYPE_MASK 0x00000001 +#define SERACC_CHIP_TYPE_1_03 0x00000000 +#define SERACC_CHIP_TYPE_2_0 0x00000001 +#define SERACC_TWO_CODECS 0x00000002 +#define SERACC_MDM 0x00000004 +#define SERACC_HSP 0x00000008 +#define SERACC_ODT 0x00000010 /* only CS4630 */ +#endif + +/* + * The following defines are for the flags in the AC97 control register 2. + */ +#ifndef NO_CS4612 +#define ACCTL2_RSTN 0x00000001 +#define ACCTL2_ESYN 0x00000002 +#define ACCTL2_VFRM 0x00000004 +#define ACCTL2_DCV 0x00000008 +#define ACCTL2_CRW 0x00000010 +#define ACCTL2_ASYN 0x00000020 +#endif + +/* + * The following defines are for the flags in the AC97 status register 2. + */ +#ifndef NO_CS4612 +#define ACSTS2_CRDY 0x00000001 +#define ACSTS2_VSTS 0x00000002 +#endif + +/* + * The following defines are for the flags in the AC97 output slot valid + * register 2. + */ +#ifndef NO_CS4612 +#define ACOSV2_SLV3 0x00000001 +#define ACOSV2_SLV4 0x00000002 +#define ACOSV2_SLV5 0x00000004 +#define ACOSV2_SLV6 0x00000008 +#define ACOSV2_SLV7 0x00000010 +#define ACOSV2_SLV8 0x00000020 +#define ACOSV2_SLV9 0x00000040 +#define ACOSV2_SLV10 0x00000080 +#define ACOSV2_SLV11 0x00000100 +#define ACOSV2_SLV12 0x00000200 +#endif + +/* + * The following defines are for the flags in the AC97 command address + * register 2. + */ +#ifndef NO_CS4612 +#define ACCAD2_CI_MASK 0x0000007F +#define ACCAD2_CI_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 command data register + * 2. + */ +#ifndef NO_CS4612 +#define ACCDA2_CD_MASK 0x0000FFFF +#define ACCDA2_CD_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 input slot valid + * register 2. + */ +#ifndef NO_CS4612 +#define ACISV2_ISV3 0x00000001 +#define ACISV2_ISV4 0x00000002 +#define ACISV2_ISV5 0x00000004 +#define ACISV2_ISV6 0x00000008 +#define ACISV2_ISV7 0x00000010 +#define ACISV2_ISV8 0x00000020 +#define ACISV2_ISV9 0x00000040 +#define ACISV2_ISV10 0x00000080 +#define ACISV2_ISV11 0x00000100 +#define ACISV2_ISV12 0x00000200 +#endif + +/* + * The following defines are for the flags in the AC97 status address + * register 2. + */ +#ifndef NO_CS4612 +#define ACSAD2_SI_MASK 0x0000007F +#define ACSAD2_SI_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the AC97 status data register 2. + */ +#ifndef NO_CS4612 +#define ACSDA2_SD_MASK 0x0000FFFF +#define ACSDA2_SD_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the I/O trap address and control + * registers (all 12). + */ +#ifndef NO_CS4612 +#define IOTAC_SA_MASK 0x0000FFFF +#define IOTAC_MSK_MASK 0x000F0000 +#define IOTAC_IODC_MASK 0x06000000 +#define IOTAC_IODC_16_BIT 0x00000000 +#define IOTAC_IODC_10_BIT 0x02000000 +#define IOTAC_IODC_12_BIT 0x04000000 +#define IOTAC_WSPI 0x08000000 +#define IOTAC_RSPI 0x10000000 +#define IOTAC_WSE 0x20000000 +#define IOTAC_WE 0x40000000 +#define IOTAC_RE 0x80000000 +#define IOTAC_SA_SHIFT 0 +#define IOTAC_MSK_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap fast read registers + * (all 8). + */ +#ifndef NO_CS4612 +#define IOTFR_D_MASK 0x0000FFFF +#define IOTFR_A_MASK 0x000F0000 +#define IOTFR_R_MASK 0x0F000000 +#define IOTFR_ALL 0x40000000 +#define IOTFR_VL 0x80000000 +#define IOTFR_D_SHIFT 0 +#define IOTFR_A_SHIFT 16 +#define IOTFR_R_SHIFT 24 +#endif + +/* + * The following defines are for the flags in the I/O trap FIFO register. + */ +#ifndef NO_CS4612 +#define IOTFIFO_BA_MASK 0x00003FFF +#define IOTFIFO_S_MASK 0x00FF0000 +#define IOTFIFO_OF 0x40000000 +#define IOTFIFO_SPIOF 0x80000000 +#define IOTFIFO_BA_SHIFT 0 +#define IOTFIFO_S_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap retry read data + * register. + */ +#ifndef NO_CS4612 +#define IOTRRD_D_MASK 0x0000FFFF +#define IOTRRD_RDV 0x80000000 +#define IOTRRD_D_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the I/O trap FIFO pointer + * register. + */ +#ifndef NO_CS4612 +#define IOTFP_CA_MASK 0x00003FFF +#define IOTFP_PA_MASK 0x3FFF0000 +#define IOTFP_CA_SHIFT 0 +#define IOTFP_PA_SHIFT 16 +#endif + +/* + * The following defines are for the flags in the I/O trap control register. + */ +#ifndef NO_CS4612 +#define IOTCR_ITD 0x00000001 +#define IOTCR_HRV 0x00000002 +#define IOTCR_SRV 0x00000004 +#define IOTCR_DTI 0x00000008 +#define IOTCR_DFI 0x00000010 +#define IOTCR_DDP 0x00000020 +#define IOTCR_JTE 0x00000040 +#define IOTCR_PPE 0x00000080 +#endif + +/* + * The following defines are for the flags in the direct PCI data register. + */ +#ifndef NO_CS4612 +#define DPCID_D_MASK 0xFFFFFFFF +#define DPCID_D_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the direct PCI address register. + */ +#ifndef NO_CS4612 +#define DPCIA_A_MASK 0xFFFFFFFF +#define DPCIA_A_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the direct PCI command register. + */ +#ifndef NO_CS4612 +#define DPCIC_C_MASK 0x0000000F +#define DPCIC_C_IOREAD 0x00000002 +#define DPCIC_C_IOWRITE 0x00000003 +#define DPCIC_BE_MASK 0x000000F0 +#endif + +/* + * The following defines are for the flags in the PC/PCI request register. + */ +#ifndef NO_CS4612 +#define PCPCIR_RDC_MASK 0x00000007 +#define PCPCIR_C_MASK 0x00007000 +#define PCPCIR_REQ 0x00008000 +#define PCPCIR_RDC_SHIFT 0 +#define PCPCIR_C_SHIFT 12 +#endif + +/* + * The following defines are for the flags in the PC/PCI grant register. + */ +#ifndef NO_CS4612 +#define PCPCIG_GDC_MASK 0x00000007 +#define PCPCIG_VL 0x00008000 +#define PCPCIG_GDC_SHIFT 0 +#endif + +/* + * The following defines are for the flags in the PC/PCI master enable + * register. + */ +#ifndef NO_CS4612 +#define PCPCIEN_EN 0x00000001 +#endif + +/* + * The following defines are for the flags in the extended PCI power + * management control register. + */ +#ifndef NO_CS4612 +#define EPCIPMC_GWU 0x00000001 +#define EPCIPMC_FSPC 0x00000002 +#endif + +/* + * The following defines are for the flags in the SP control register. + */ +#define SPCR_RUN 0x00000001 +#define SPCR_STPFR 0x00000002 +#define SPCR_RUNFR 0x00000004 +#define SPCR_TICK 0x00000008 +#define SPCR_DRQEN 0x00000020 +#define SPCR_RSTSP 0x00000040 +#define SPCR_OREN 0x00000080 +#ifndef NO_CS4612 +#define SPCR_PCIINT 0x00000100 +#define SPCR_OINTD 0x00000200 +#define SPCR_CRE 0x00008000 +#endif + +/* + * The following defines are for the flags in the debug index register. + */ +#define DREG_REGID_MASK 0x0000007F +#define DREG_DEBUG 0x00000080 +#define DREG_RGBK_MASK 0x00000700 +#define DREG_TRAP 0x00000800 +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_TRAPX 0x00001000 +#endif +#endif +#define DREG_REGID_SHIFT 0 +#define DREG_RGBK_SHIFT 8 +#define DREG_RGBK_REGID_MASK 0x0000077F +#define DREG_REGID_R0 0x00000010 +#define DREG_REGID_R1 0x00000011 +#define DREG_REGID_R2 0x00000012 +#define DREG_REGID_R3 0x00000013 +#define DREG_REGID_R4 0x00000014 +#define DREG_REGID_R5 0x00000015 +#define DREG_REGID_R6 0x00000016 +#define DREG_REGID_R7 0x00000017 +#define DREG_REGID_R8 0x00000018 +#define DREG_REGID_R9 0x00000019 +#define DREG_REGID_RA 0x0000001A +#define DREG_REGID_RB 0x0000001B +#define DREG_REGID_RC 0x0000001C +#define DREG_REGID_RD 0x0000001D +#define DREG_REGID_RE 0x0000001E +#define DREG_REGID_RF 0x0000001F +#define DREG_REGID_RA_BUS_LOW 0x00000020 +#define DREG_REGID_RA_BUS_HIGH 0x00000038 +#define DREG_REGID_YBUS_LOW 0x00000050 +#define DREG_REGID_YBUS_HIGH 0x00000058 +#define DREG_REGID_TRAP_0 0x00000100 +#define DREG_REGID_TRAP_1 0x00000101 +#define DREG_REGID_TRAP_2 0x00000102 +#define DREG_REGID_TRAP_3 0x00000103 +#define DREG_REGID_TRAP_4 0x00000104 +#define DREG_REGID_TRAP_5 0x00000105 +#define DREG_REGID_TRAP_6 0x00000106 +#define DREG_REGID_TRAP_7 0x00000107 +#define DREG_REGID_INDIRECT_ADDRESS 0x0000010E +#define DREG_REGID_TOP_OF_STACK 0x0000010F +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_8 0x00000110 +#define DREG_REGID_TRAP_9 0x00000111 +#define DREG_REGID_TRAP_10 0x00000112 +#define DREG_REGID_TRAP_11 0x00000113 +#define DREG_REGID_TRAP_12 0x00000114 +#define DREG_REGID_TRAP_13 0x00000115 +#define DREG_REGID_TRAP_14 0x00000116 +#define DREG_REGID_TRAP_15 0x00000117 +#define DREG_REGID_TRAP_16 0x00000118 +#define DREG_REGID_TRAP_17 0x00000119 +#define DREG_REGID_TRAP_18 0x0000011A +#define DREG_REGID_TRAP_19 0x0000011B +#define DREG_REGID_TRAP_20 0x0000011C +#define DREG_REGID_TRAP_21 0x0000011D +#define DREG_REGID_TRAP_22 0x0000011E +#define DREG_REGID_TRAP_23 0x0000011F +#endif +#endif +#define DREG_REGID_RSA0_LOW 0x00000200 +#define DREG_REGID_RSA0_HIGH 0x00000201 +#define DREG_REGID_RSA1_LOW 0x00000202 +#define DREG_REGID_RSA1_HIGH 0x00000203 +#define DREG_REGID_RSA2 0x00000204 +#define DREG_REGID_RSA3 0x00000205 +#define DREG_REGID_RSI0_LOW 0x00000206 +#define DREG_REGID_RSI0_HIGH 0x00000207 +#define DREG_REGID_RSI1 0x00000208 +#define DREG_REGID_RSI2 0x00000209 +#define DREG_REGID_SAGUSTATUS 0x0000020A +#define DREG_REGID_RSCONFIG01_LOW 0x0000020B +#define DREG_REGID_RSCONFIG01_HIGH 0x0000020C +#define DREG_REGID_RSCONFIG23_LOW 0x0000020D +#define DREG_REGID_RSCONFIG23_HIGH 0x0000020E +#define DREG_REGID_RSDMA01E 0x0000020F +#define DREG_REGID_RSDMA23E 0x00000210 +#define DREG_REGID_RSD0_LOW 0x00000211 +#define DREG_REGID_RSD0_HIGH 0x00000212 +#define DREG_REGID_RSD1_LOW 0x00000213 +#define DREG_REGID_RSD1_HIGH 0x00000214 +#define DREG_REGID_RSD2_LOW 0x00000215 +#define DREG_REGID_RSD2_HIGH 0x00000216 +#define DREG_REGID_RSD3_LOW 0x00000217 +#define DREG_REGID_RSD3_HIGH 0x00000218 +#define DREG_REGID_SRAR_HIGH 0x0000021A +#define DREG_REGID_SRAR_LOW 0x0000021B +#define DREG_REGID_DMA_STATE 0x0000021C +#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021D +#define DREG_REGID_NEXT_DMA_STREAM 0x0000021E +#define DREG_REGID_CPU_STATUS 0x00000300 +#define DREG_REGID_MAC_MODE 0x00000301 +#define DREG_REGID_STACK_AND_REPEAT 0x00000302 +#define DREG_REGID_INDEX0 0x00000304 +#define DREG_REGID_INDEX1 0x00000305 +#define DREG_REGID_DMA_STATE_0_3 0x00000400 +#define DREG_REGID_DMA_STATE_4_7 0x00000404 +#define DREG_REGID_DMA_STATE_8_11 0x00000408 +#define DREG_REGID_DMA_STATE_12_15 0x0000040C +#define DREG_REGID_DMA_STATE_16_19 0x00000410 +#define DREG_REGID_DMA_STATE_20_23 0x00000414 +#define DREG_REGID_DMA_STATE_24_27 0x00000418 +#define DREG_REGID_DMA_STATE_28_31 0x0000041C +#define DREG_REGID_DMA_STATE_32_35 0x00000420 +#define DREG_REGID_DMA_STATE_36_39 0x00000424 +#define DREG_REGID_DMA_STATE_40_43 0x00000428 +#define DREG_REGID_DMA_STATE_44_47 0x0000042C +#define DREG_REGID_DMA_STATE_48_51 0x00000430 +#define DREG_REGID_DMA_STATE_52_55 0x00000434 +#define DREG_REGID_DMA_STATE_56_59 0x00000438 +#define DREG_REGID_DMA_STATE_60_63 0x0000043C +#define DREG_REGID_DMA_STATE_64_67 0x00000440 +#define DREG_REGID_DMA_STATE_68_71 0x00000444 +#define DREG_REGID_DMA_STATE_72_75 0x00000448 +#define DREG_REGID_DMA_STATE_76_79 0x0000044C +#define DREG_REGID_DMA_STATE_80_83 0x00000450 +#define DREG_REGID_DMA_STATE_84_87 0x00000454 +#define DREG_REGID_DMA_STATE_88_91 0x00000458 +#define DREG_REGID_DMA_STATE_92_95 0x0000045C +#define DREG_REGID_TRAP_SELECT 0x00000500 +#define DREG_REGID_TRAP_WRITE_0 0x00000500 +#define DREG_REGID_TRAP_WRITE_1 0x00000501 +#define DREG_REGID_TRAP_WRITE_2 0x00000502 +#define DREG_REGID_TRAP_WRITE_3 0x00000503 +#define DREG_REGID_TRAP_WRITE_4 0x00000504 +#define DREG_REGID_TRAP_WRITE_5 0x00000505 +#define DREG_REGID_TRAP_WRITE_6 0x00000506 +#define DREG_REGID_TRAP_WRITE_7 0x00000507 +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_WRITE_8 0x00000510 +#define DREG_REGID_TRAP_WRITE_9 0x00000511 +#define DREG_REGID_TRAP_WRITE_10 0x00000512 +#define DREG_REGID_TRAP_WRITE_11 0x00000513 +#define DREG_REGID_TRAP_WRITE_12 0x00000514 +#define DREG_REGID_TRAP_WRITE_13 0x00000515 +#define DREG_REGID_TRAP_WRITE_14 0x00000516 +#define DREG_REGID_TRAP_WRITE_15 0x00000517 +#define DREG_REGID_TRAP_WRITE_16 0x00000518 +#define DREG_REGID_TRAP_WRITE_17 0x00000519 +#define DREG_REGID_TRAP_WRITE_18 0x0000051A +#define DREG_REGID_TRAP_WRITE_19 0x0000051B +#define DREG_REGID_TRAP_WRITE_20 0x0000051C +#define DREG_REGID_TRAP_WRITE_21 0x0000051D +#define DREG_REGID_TRAP_WRITE_22 0x0000051E +#define DREG_REGID_TRAP_WRITE_23 0x0000051F +#endif +#endif +#define DREG_REGID_MAC0_ACC0_LOW 0x00000600 +#define DREG_REGID_MAC0_ACC1_LOW 0x00000601 +#define DREG_REGID_MAC0_ACC2_LOW 0x00000602 +#define DREG_REGID_MAC0_ACC3_LOW 0x00000603 +#define DREG_REGID_MAC1_ACC0_LOW 0x00000604 +#define DREG_REGID_MAC1_ACC1_LOW 0x00000605 +#define DREG_REGID_MAC1_ACC2_LOW 0x00000606 +#define DREG_REGID_MAC1_ACC3_LOW 0x00000607 +#define DREG_REGID_MAC0_ACC0_MID 0x00000608 +#define DREG_REGID_MAC0_ACC1_MID 0x00000609 +#define DREG_REGID_MAC0_ACC2_MID 0x0000060A +#define DREG_REGID_MAC0_ACC3_MID 0x0000060B +#define DREG_REGID_MAC1_ACC0_MID 0x0000060C +#define DREG_REGID_MAC1_ACC1_MID 0x0000060D +#define DREG_REGID_MAC1_ACC2_MID 0x0000060E +#define DREG_REGID_MAC1_ACC3_MID 0x0000060F +#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610 +#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611 +#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612 +#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613 +#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614 +#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615 +#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616 +#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617 +#define DREG_REGID_RSHOUT_LOW 0x00000620 +#define DREG_REGID_RSHOUT_MID 0x00000628 +#define DREG_REGID_RSHOUT_HIGH 0x00000630 + +/* + * The following defines are for the flags in the DMA stream requestor write + */ +#define DSRWP_DSR_MASK 0x0000000F +#define DSRWP_DSR_BG_RQ 0x00000001 +#define DSRWP_DSR_PRIORITY_MASK 0x00000006 +#define DSRWP_DSR_PRIORITY_0 0x00000000 +#define DSRWP_DSR_PRIORITY_1 0x00000002 +#define DSRWP_DSR_PRIORITY_2 0x00000004 +#define DSRWP_DSR_PRIORITY_3 0x00000006 +#define DSRWP_DSR_RQ_PENDING 0x00000008 + +/* + * The following defines are for the flags in the trap write port register. + */ +#define TWPR_TW_MASK 0x0000FFFF +#define TWPR_TW_SHIFT 0 + +/* + * The following defines are for the flags in the stack pointer write + * register. + */ +#define SPWR_STKP_MASK 0x0000000F +#define SPWR_STKP_SHIFT 0 + +/* + * The following defines are for the flags in the SP interrupt register. + */ +#define SPIR_FRI 0x00000001 +#define SPIR_DOI 0x00000002 +#define SPIR_GPI2 0x00000004 +#define SPIR_GPI3 0x00000008 +#define SPIR_IP0 0x00000010 +#define SPIR_IP1 0x00000020 +#define SPIR_IP2 0x00000040 +#define SPIR_IP3 0x00000080 + +/* + * The following defines are for the flags in the functional group 1 register. + */ +#define FGR1_F1S_MASK 0x0000FFFF +#define FGR1_F1S_SHIFT 0 + +/* + * The following defines are for the flags in the SP clock status register. + */ +#define SPCS_FRI 0x00000001 +#define SPCS_DOI 0x00000002 +#define SPCS_GPI2 0x00000004 +#define SPCS_GPI3 0x00000008 +#define SPCS_IP0 0x00000010 +#define SPCS_IP1 0x00000020 +#define SPCS_IP2 0x00000040 +#define SPCS_IP3 0x00000080 +#define SPCS_SPRUN 0x00000100 +#define SPCS_SLEEP 0x00000200 +#define SPCS_FG 0x00000400 +#define SPCS_ORUN 0x00000800 +#define SPCS_IRQ 0x00001000 +#define SPCS_FGN_MASK 0x0000E000 +#define SPCS_FGN_SHIFT 13 + +/* + * The following defines are for the flags in the SP DMA requestor status + * register. + */ +#define SDSR_DCS_MASK 0x000000FF +#define SDSR_DCS_SHIFT 0 +#define SDSR_DCS_NONE 0x00000007 + +/* + * The following defines are for the flags in the frame timer register. + */ +#define FRMT_FTV_MASK 0x0000FFFF +#define FRMT_FTV_SHIFT 0 + +/* + * The following defines are for the flags in the frame timer current count + * register. + */ +#define FRCC_FCC_MASK 0x0000FFFF +#define FRCC_FCC_SHIFT 0 + +/* + * The following defines are for the flags in the frame timer save count + * register. + */ +#define FRSC_FCS_MASK 0x0000FFFF +#define FRSC_FCS_SHIFT 0 + +/* + * The following define the various flags stored in the scatter/gather + * descriptors. + */ +#define DMA_SG_NEXT_ENTRY_MASK 0x00000FF8 +#define DMA_SG_SAMPLE_END_MASK 0x0FFF0000 +#define DMA_SG_SAMPLE_END_FLAG 0x10000000 +#define DMA_SG_LOOP_END_FLAG 0x20000000 +#define DMA_SG_SIGNAL_END_FLAG 0x40000000 +#define DMA_SG_SIGNAL_PAGE_FLAG 0x80000000 +#define DMA_SG_NEXT_ENTRY_SHIFT 3 +#define DMA_SG_SAMPLE_END_SHIFT 16 + +/* + * The following define the offsets of the fields within the on-chip generic + * DMA requestor. + */ +#define DMA_RQ_CONTROL1 0x00000000 +#define DMA_RQ_CONTROL2 0x00000004 +#define DMA_RQ_SOURCE_ADDR 0x00000008 +#define DMA_RQ_DESTINATION_ADDR 0x0000000C +#define DMA_RQ_NEXT_PAGE_ADDR 0x00000010 +#define DMA_RQ_NEXT_PAGE_SGDESC 0x00000014 +#define DMA_RQ_LOOP_START_ADDR 0x00000018 +#define DMA_RQ_POST_LOOP_ADDR 0x0000001C +#define DMA_RQ_PAGE_MAP_ADDR 0x00000020 + +/* + * The following defines are for the flags in the first control word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_C1_COUNT_MASK 0x000003FF +#define DMA_RQ_C1_DESTINATION_SCATTER 0x00001000 +#define DMA_RQ_C1_SOURCE_GATHER 0x00002000 +#define DMA_RQ_C1_DONE_FLAG 0x00004000 +#define DMA_RQ_C1_OPTIMIZE_STATE 0x00008000 +#define DMA_RQ_C1_SAMPLE_END_STATE_MASK 0x00030000 +#define DMA_RQ_C1_FULL_PAGE 0x00000000 +#define DMA_RQ_C1_BEFORE_SAMPLE_END 0x00010000 +#define DMA_RQ_C1_PAGE_MAP_ERROR 0x00020000 +#define DMA_RQ_C1_AT_SAMPLE_END 0x00030000 +#define DMA_RQ_C1_LOOP_END_STATE_MASK 0x000C0000 +#define DMA_RQ_C1_NOT_LOOP_END 0x00000000 +#define DMA_RQ_C1_BEFORE_LOOP_END 0x00040000 +#define DMA_RQ_C1_2PAGE_LOOP_BEGIN 0x00080000 +#define DMA_RQ_C1_LOOP_BEGIN 0x000C0000 +#define DMA_RQ_C1_PAGE_MAP_MASK 0x00300000 +#define DMA_RQ_C1_PM_NONE_PENDING 0x00000000 +#define DMA_RQ_C1_PM_NEXT_PENDING 0x00100000 +#define DMA_RQ_C1_PM_RESERVED 0x00200000 +#define DMA_RQ_C1_PM_LOOP_NEXT_PENDING 0x00300000 +#define DMA_RQ_C1_WRITEBACK_DEST_FLAG 0x00400000 +#define DMA_RQ_C1_WRITEBACK_SRC_FLAG 0x00800000 +#define DMA_RQ_C1_DEST_SIZE_MASK 0x07000000 +#define DMA_RQ_C1_DEST_LINEAR 0x00000000 +#define DMA_RQ_C1_DEST_MOD16 0x01000000 +#define DMA_RQ_C1_DEST_MOD32 0x02000000 +#define DMA_RQ_C1_DEST_MOD64 0x03000000 +#define DMA_RQ_C1_DEST_MOD128 0x04000000 +#define DMA_RQ_C1_DEST_MOD256 0x05000000 +#define DMA_RQ_C1_DEST_MOD512 0x06000000 +#define DMA_RQ_C1_DEST_MOD1024 0x07000000 +#define DMA_RQ_C1_DEST_ON_HOST 0x08000000 +#define DMA_RQ_C1_SOURCE_SIZE_MASK 0x70000000 +#define DMA_RQ_C1_SOURCE_LINEAR 0x00000000 +#define DMA_RQ_C1_SOURCE_MOD16 0x10000000 +#define DMA_RQ_C1_SOURCE_MOD32 0x20000000 +#define DMA_RQ_C1_SOURCE_MOD64 0x30000000 +#define DMA_RQ_C1_SOURCE_MOD128 0x40000000 +#define DMA_RQ_C1_SOURCE_MOD256 0x50000000 +#define DMA_RQ_C1_SOURCE_MOD512 0x60000000 +#define DMA_RQ_C1_SOURCE_MOD1024 0x70000000 +#define DMA_RQ_C1_SOURCE_ON_HOST 0x80000000 +#define DMA_RQ_C1_COUNT_SHIFT 0 + +/* + * The following defines are for the flags in the second control word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_C2_VIRTUAL_CHANNEL_MASK 0x0000003F +#define DMA_RQ_C2_VIRTUAL_SIGNAL_MASK 0x00000300 +#define DMA_RQ_C2_NO_VIRTUAL_SIGNAL 0x00000000 +#define DMA_RQ_C2_SIGNAL_EVERY_DMA 0x00000100 +#define DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG 0x00000200 +#define DMA_RQ_C2_SIGNAL_DEST_PINGPONG 0x00000300 +#define DMA_RQ_C2_AUDIO_CONVERT_MASK 0x0000F000 +#define DMA_RQ_C2_AC_NONE 0x00000000 +#define DMA_RQ_C2_AC_8_TO_16_BIT 0x00001000 +#define DMA_RQ_C2_AC_MONO_TO_STEREO 0x00002000 +#define DMA_RQ_C2_AC_ENDIAN_CONVERT 0x00004000 +#define DMA_RQ_C2_AC_SIGNED_CONVERT 0x00008000 +#define DMA_RQ_C2_LOOP_END_MASK 0x0FFF0000 +#define DMA_RQ_C2_LOOP_MASK 0x30000000 +#define DMA_RQ_C2_NO_LOOP 0x00000000 +#define DMA_RQ_C2_ONE_PAGE_LOOP 0x10000000 +#define DMA_RQ_C2_TWO_PAGE_LOOP 0x20000000 +#define DMA_RQ_C2_MULTI_PAGE_LOOP 0x30000000 +#define DMA_RQ_C2_SIGNAL_LOOP_BACK 0x40000000 +#define DMA_RQ_C2_SIGNAL_POST_BEGIN_PAGE 0x80000000 +#define DMA_RQ_C2_VIRTUAL_CHANNEL_SHIFT 0 +#define DMA_RQ_C2_LOOP_END_SHIFT 16 + +/* + * The following defines are for the flags in the source and destination words + * of the on-chip generic DMA requestor. + */ +#define DMA_RQ_SD_ADDRESS_MASK 0x0000FFFF +#define DMA_RQ_SD_MEMORY_ID_MASK 0x000F0000 +#define DMA_RQ_SD_SP_PARAM_ADDR 0x00000000 +#define DMA_RQ_SD_SP_SAMPLE_ADDR 0x00010000 +#define DMA_RQ_SD_SP_PROGRAM_ADDR 0x00020000 +#define DMA_RQ_SD_SP_DEBUG_ADDR 0x00030000 +#define DMA_RQ_SD_OMNIMEM_ADDR 0x000E0000 +#define DMA_RQ_SD_END_FLAG 0x40000000 +#define DMA_RQ_SD_ERROR_FLAG 0x80000000 +#define DMA_RQ_SD_ADDRESS_SHIFT 0 + +/* + * The following defines are for the flags in the page map address word of the + * on-chip generic DMA requestor. + */ +#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_MASK 0x00000FF8 +#define DMA_RQ_PMA_PAGE_TABLE_MASK 0xFFFFF000 +#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_SHIFT 3 +#define DMA_RQ_PMA_PAGE_TABLE_SHIFT 12 + +#define BA1_VARIDEC_BUF_1 0x000 + +#define BA1_PDTC 0x0c0 /* BA1_PLAY_DMA_TRANSACTION_COUNT_REG */ +#define BA1_PFIE 0x0c4 /* BA1_PLAY_FORMAT_&_INTERRUPT_ENABLE_REG */ +#define BA1_PBA 0x0c8 /* BA1_PLAY_BUFFER_ADDRESS */ +#define BA1_PVOL 0x0f8 /* BA1_PLAY_VOLUME_REG */ +#define BA1_PSRC 0x288 /* BA1_PLAY_SAMPLE_RATE_CORRECTION_REG */ +#define BA1_PCTL 0x2a4 /* BA1_PLAY_CONTROL_REG */ +#define BA1_PPI 0x2b4 /* BA1_PLAY_PHASE_INCREMENT_REG */ + +#define BA1_CCTL 0x064 /* BA1_CAPTURE_CONTROL_REG */ +#define BA1_CIE 0x104 /* BA1_CAPTURE_INTERRUPT_ENABLE_REG */ +#define BA1_CBA 0x10c /* BA1_CAPTURE_BUFFER_ADDRESS */ +#define BA1_CSRC 0x2c8 /* BA1_CAPTURE_SAMPLE_RATE_CORRECTION_REG */ +#define BA1_CCI 0x2d8 /* BA1_CAPTURE_COEFFICIENT_INCREMENT_REG */ +#define BA1_CD 0x2e0 /* BA1_CAPTURE_DELAY_REG */ +#define BA1_CPI 0x2f4 /* BA1_CAPTURE_PHASE_INCREMENT_REG */ +#define BA1_CVOL 0x2f8 /* BA1_CAPTURE_VOLUME_REG */ + +#define BA1_CFG1 0x134 /* BA1_CAPTURE_FRAME_GROUP_1_REG */ +#define BA1_CFG2 0x138 /* BA1_CAPTURE_FRAME_GROUP_2_REG */ +#define BA1_CCST 0x13c /* BA1_CAPTURE_CONSTANT_REG */ +#define BA1_CSPB 0x340 /* BA1_CAPTURE_SPB_ADDRESS */ + +/* + * + */ + +#define CS46XX_MODE_OUTPUT (1<<0) /* MIDI UART - output */ +#define CS46XX_MODE_INPUT (1<<1) /* MIDI UART - input */ + +/* + * + */ + +#define SAVE_REG_MAX 0x10 +#define POWER_DOWN_ALL 0x7f0f + +/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ +#define MAX_NR_AC97 4 +#define CS46XX_PRIMARY_CODEC_INDEX 0 +#define CS46XX_SECONDARY_CODEC_INDEX 1 +#define CS46XX_SECONDARY_CODEC_OFFSET 0x80 +#define CS46XX_DSP_CAPTURE_CHANNEL 1 + +/* capture */ +#define CS46XX_DSP_CAPTURE_CHANNEL 1 + +/* mixer */ +#define CS46XX_MIXER_SPDIF_INPUT_ELEMENT 1 +#define CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT 2 + + +struct snd_cs46xx_pcm { + struct snd_dma_buffer hw_buf; + + unsigned int ctl; + unsigned int shift; /* Shift count to trasform frames in bytes */ + struct snd_pcm_indirect pcm_rec; + struct snd_pcm_substream *substream; + + struct dsp_pcm_channel_descriptor * pcm_channel; + + int pcm_channel_id; /* Fron Rear, Center Lfe ... */ +}; + +struct snd_cs46xx_region { + char name[24]; + unsigned long base; + void __iomem *remap_addr; + unsigned long size; + struct resource *resource; +}; + +struct snd_cs46xx { + int irq; + unsigned long ba0_addr; + unsigned long ba1_addr; + union { + struct { + struct snd_cs46xx_region ba0; + struct snd_cs46xx_region data0; + struct snd_cs46xx_region data1; + struct snd_cs46xx_region pmem; + struct snd_cs46xx_region reg; + } name; + struct snd_cs46xx_region idx[5]; + } region; + + unsigned int mode; + + struct { + struct snd_dma_buffer hw_buf; + + unsigned int ctl; + unsigned int shift; /* Shift count to trasform frames in bytes */ + struct snd_pcm_indirect pcm_rec; + struct snd_pcm_substream *substream; + } capt; + + + int nr_ac97_codecs; + struct snd_ac97_bus *ac97_bus; + struct snd_ac97 *ac97[MAX_NR_AC97]; + + struct pci_dev *pci; + struct snd_card *card; + struct snd_pcm *pcm; + + struct snd_rawmidi *rmidi; + struct snd_rawmidi_substream *midi_input; + struct snd_rawmidi_substream *midi_output; + + spinlock_t reg_lock; + unsigned int midcr; + unsigned int uartm; + + int amplifier; + void (*amplifier_ctrl)(struct snd_cs46xx *, int); + void (*active_ctrl)(struct snd_cs46xx *, int); + void (*mixer_init)(struct snd_cs46xx *); + + int acpi_port; + struct snd_kcontrol *eapd_switch; /* for amplifier hack */ + int accept_valid; /* accept mmap valid (for OSS) */ + int in_suspend; + + struct gameport *gameport; + +#ifdef CONFIG_SND_CS46XX_NEW_DSP + struct mutex spos_mutex; + + struct dsp_spos_instance * dsp_spos_instance; + + struct snd_pcm *pcm_rear; + struct snd_pcm *pcm_center_lfe; + struct snd_pcm *pcm_iec958; +#else /* for compatibility */ + struct snd_cs46xx_pcm *playback_pcm; + unsigned int play_ctl; +#endif + +#ifdef CONFIG_PM + u32 *saved_regs; +#endif +}; + +int snd_cs46xx_create(struct snd_card *card, + struct pci_dev *pci, + int external_amp, int thinkpad, + struct snd_cs46xx **rcodec); +extern const struct dev_pm_ops snd_cs46xx_pm; + +int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); +int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); +int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); +int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device, struct snd_pcm **rpcm); +int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device); +int snd_cs46xx_midi(struct snd_cs46xx *chip, int device, struct snd_rawmidi **rmidi); +int snd_cs46xx_start_dsp(struct snd_cs46xx *chip); +int snd_cs46xx_gameport(struct snd_cs46xx *chip); + +#endif /* __SOUND_CS46XX_H */ diff --git a/sound/pci/cs46xx/cs46xx_dsp_scb_types.h b/sound/pci/cs46xx/cs46xx_dsp_scb_types.h new file mode 100644 index 000000000000..080857ad0ca2 --- /dev/null +++ b/sound/pci/cs46xx/cs46xx_dsp_scb_types.h @@ -0,0 +1,1213 @@ +/* + * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * NOTE: comments are copy/paste from cwcemb80.lst + * provided by Tom Woller at Cirrus (my only + * documentation about the SP OS running inside + * the DSP) + */ + +#ifndef __CS46XX_DSP_SCB_TYPES_H__ +#define __CS46XX_DSP_SCB_TYPES_H__ + +#include + +#ifndef ___DSP_DUAL_16BIT_ALLOC +#if defined(__LITTLE_ENDIAN) +#define ___DSP_DUAL_16BIT_ALLOC(a,b) u16 a; u16 b; +#elif defined(__BIG_ENDIAN) +#define ___DSP_DUAL_16BIT_ALLOC(a,b) u16 b; u16 a; +#else +#error Not __LITTLE_ENDIAN and not __BIG_ENDIAN, then what ??? +#endif +#endif + +/* This structs are used internally by the SP */ + +struct dsp_basic_dma_req { + /* DMA Requestor Word 0 (DCW) fields: + + 31 [30-28]27 [26:24] 23 22 21 20 [19:18] [17:16] 15 14 13 12 11 10 9 8 7 6 [5:0] + _______________________________________________________________________________________ + |S| SBT |D| DBT |wb|wb| | | LS | SS |Opt|Do|SSG|DSG| | | | | | | Dword | + |H|_____ |H|_________|S_|D |__|__|______|_______|___|ne|__ |__ |__|__|_|_|_|_|_Count -1| + */ + u32 dcw; /* DMA Control Word */ + u32 dmw; /* DMA Mode Word */ + u32 saw; /* Source Address Word */ + u32 daw; /* Destination Address Word */ +}; + +struct dsp_scatter_gather_ext { + u32 npaw; /* Next-Page Address Word */ + + /* DMA Requestor Word 5 (NPCW) fields: + + 31-30 29 28 [27:16] [15:12] [11:3] [2:0] + _________________________________________________________________________________________ + |SV |LE|SE| Sample-end byte offset | | Page-map entry offset for next | | + |page|__|__| ___________________________|_________|__page, if !sample-end___________|____| + */ + u32 npcw; /* Next-Page Control Word */ + u32 lbaw; /* Loop-Begin Address Word */ + u32 nplbaw; /* Next-Page after Loop-Begin Address Word */ + u32 sgaw; /* Scatter/Gather Address Word */ +}; + +struct dsp_volume_control { + ___DSP_DUAL_16BIT_ALLOC( + rightTarg, /* Target volume for left & right channels */ + leftTarg + ) + ___DSP_DUAL_16BIT_ALLOC( + rightVol, /* Current left & right channel volumes */ + leftVol + ) +}; + +/* Generic stream control block (SCB) structure definition */ +struct dsp_generic_scb { + /* For streaming I/O, the DSP should never alter any words in the DMA + requestor or the scatter/gather extension. Only ad hoc DMA request + streams are free to alter the requestor (currently only occur in the + DOS-based MIDI controller and in debugger-inserted code). + + If an SCB does not have any associated DMA requestor, these 9 ints + may be freed for use by other tasks, but the pointer to the SCB must + still be such that the insOrd:nextSCB appear at offset 9 from the + SCB pointer. + + Basic (non scatter/gather) DMA requestor (4 ints) + */ + + /* Initialized by the host, only modified by DMA + R/O for the DSP task */ + struct dsp_basic_dma_req basic_req; /* Optional */ + + /* Scatter/gather DMA requestor extension (5 ints) + Initialized by the host, only modified by DMA + DSP task never needs to even read these. + */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + + /* Sublist pointer & next stream control block (SCB) link. + Initialized & modified by the host R/O for the DSP task + */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + /* Pointer to this tasks parameter block & stream function pointer + Initialized by the host R/O for the DSP task */ + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + /* rsConfig register for stream buffer (rsDMA reg. + is loaded from basicReq.daw for incoming streams, or + basicReq.saw, for outgoing streams) + + 31 30 29 [28:24] [23:16] 15 14 13 12 11 10 9 8 7 6 5 4 [3:0] + ______________________________________________________________________________ + |DMA |D|maxDMAsize| streamNum|dir|p| | | | | | |ds |shr 1|rev Cy | mod | + |prio |_|__________|__________|___|_|__|__|__|__|_|_|___|_____|_______|_______| + 31 30 29 [28:24] [23:16] 15 14 13 12 11 10 9 8 7 6 5 4 [3:0] + + + Initialized by the host R/O for the DSP task + */ + u32 strm_rs_config; /* REQUIRED */ + // + /* On mixer input streams: indicates mixer input stream configuration + On Tees, this is copied from the stream being snooped + + Stream sample pointer & MAC-unit mode for this stream + + Initialized by the host Updated by the DSP task + */ + u32 strm_buf_ptr; /* REQUIRED */ + + /* On mixer input streams: points to next mixer input and is updated by the + mixer subroutine in the "parent" DSP task + (least-significant 16 bits are preserved, unused) + + On Tees, the pointer is copied from the stream being snooped on + initialization, and, subsequently, it is copied into the + stream being snooped. + + On wavetable/3D voices: the strmBufPtr will use all 32 bits to allow for + fractional phase accumulation + + Fractional increment per output sample in the input sample buffer + + (Not used on mixer input streams & redefined on Tees) + On wavetable/3D voices: this 32-bit word specifies the integer.fractional + increment per output sample. + */ + u32 strmPhiIncr; + + + /* Standard stereo volume control + Initialized by the host (host updates target volumes) + + Current volumes update by the DSP task + On mixer input streams: required & updated by the mixer subroutine in the + "parent" DSP task + + On Tees, both current & target volumes are copied up on initialization, + and, subsequently, the target volume is copied up while the current + volume is copied down. + + These two 32-bit words are redefined for wavetable & 3-D voices. + */ + struct dsp_volume_control vol_ctrl_t; /* Optional */ +}; + + +struct dsp_spos_control_block { + /* WARNING: Certain items in this structure are modified by the host + Any dword that can be modified by the host, must not be + modified by the SP as the host can only do atomic dword + writes, and to do otherwise, even a read modify write, + may lead to corrupted data on the SP. + + This rule does not apply to one off boot time initialisation prior to starting the SP + */ + + + ___DSP_DUAL_16BIT_ALLOC( + /* First element on the Hyper forground task tree */ + hfg_tree_root_ptr, /* HOST */ + /* First 3 dwords are written by the host and read-only on the DSP */ + hfg_stack_base /* HOST */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + /* Point to this data structure to enable easy access */ + spos_cb_ptr, /* SP */ + prev_task_tree_ptr /* SP && HOST */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + /* Currently Unused */ + xxinterval_timer_period, + /* Enable extension of SPOS data structure */ + HFGSPB_ptr + ) + + + ___DSP_DUAL_16BIT_ALLOC( + xxnum_HFG_ticks_thisInterval, + /* Modified by the DSP */ + xxnum_tntervals + ) + + + /* Set by DSP upon encountering a trap (breakpoint) or a spurious + interrupt. The host must clear this dword after reading it + upon receiving spInt1. */ + ___DSP_DUAL_16BIT_ALLOC( + spurious_int_flag, /* (Host & SP) Nature of the spurious interrupt */ + trap_flag /* (Host & SP) Nature of detected Trap */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + unused2, + invalid_IP_flag /* (Host & SP ) Indicate detection of invalid instruction pointer */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + /* pointer to forground task tree header for use in next task search */ + fg_task_tree_hdr_ptr, /* HOST */ + /* Data structure for controlling synchronous link update */ + hfg_sync_update_ptr /* HOST */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + begin_foreground_FCNT, /* SP */ + /* Place holder for holding sleep timing */ + last_FCNT_before_sleep /* SP */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + unused7, /* SP */ + next_task_treePtr /* SP */ + ) + + u32 unused5; + + ___DSP_DUAL_16BIT_ALLOC( + active_flags, /* SP */ + /* State flags, used to assist control of execution of Hyper Forground */ + HFG_flags /* SP */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + unused9, + unused8 + ) + + /* Space for saving enough context so that we can set up enough + to save some more context. + */ + u32 rFE_save_for_invalid_IP; + u32 r32_save_for_spurious_int; + u32 r32_save_for_trap; + u32 r32_save_for_HFG; +}; + +/* SPB for MIX_TO_OSTREAM algorithm family */ +struct dsp_mix2_ostream_spb +{ + /* 16b.16b integer.frac approximation to the + number of 3 sample triplets to output each + frame. (approximation must be floor, to + insure that the fractional error is always + positive) + */ + u32 outTripletsPerFrame; + + /* 16b.16b integer.frac accumulated number of + output triplets since the start of group + */ + u32 accumOutTriplets; +}; + +/* SCB for Timing master algorithm */ +struct dsp_timing_master_scb { + /* First 12 dwords from generic_scb_t */ + struct dsp_basic_dma_req basic_req; /* Optional */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + /* Initial values are 0000:xxxx */ + reserved, + extra_sample_accum + ) + + + /* Initial values are xxxx:0000 + hi: Current CODEC output FIFO pointer + (0 to 0x0f) + lo: Flag indicating that the CODEC + FIFO is sync'd (host clears to + resynchronize the FIFO pointer + upon start/restart) + */ + ___DSP_DUAL_16BIT_ALLOC( + codec_FIFO_syncd, + codec_FIFO_ptr + ) + + /* Init. 8000:0005 for 44.1k + 8000:0001 for 48k + hi: Fractional sample accumulator 0.16b + lo: Number of frames remaining to be + processed in the current group of + frames + */ + ___DSP_DUAL_16BIT_ALLOC( + frac_samp_accum_qm1, + TM_frms_left_in_group + ) + + /* Init. 0001:0005 for 44.1k + 0000:0001 for 48k + hi: Fractional sample correction factor 0.16b + to be added every frameGroupLength frames + to correct for truncation error in + nsamp_per_frm_q15 + lo: Number of frames in the group + */ + ___DSP_DUAL_16BIT_ALLOC( + frac_samp_correction_qm1, + TM_frm_group_length + ) + + /* Init. 44.1k*65536/8k = 0x00058333 for 44.1k + 48k*65536/8k = 0x00060000 for 48k + 16b.16b integer.frac approximation to the + number of samples to output each frame. + (approximation must be floor, to insure */ + u32 nsamp_per_frm_q15; +}; + +/* SCB for CODEC output algorithm */ +struct dsp_codec_output_scb { + /* First 13 dwords from generic_scb_t */ + struct dsp_basic_dma_req basic_req; /* Optional */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + u32 strm_rs_config; /* REQUIRED */ + + u32 strm_buf_ptr; /* REQUIRED */ + + /* NOTE: The CODEC output task reads samples from the first task on its + sublist at the stream buffer pointer (init. to lag DMA destination + address word). After the required number of samples is transferred, + the CODEC output task advances sub_list_ptr->strm_buf_ptr past the samples + consumed. + */ + + /* Init. 0000:0010 for SDout + 0060:0010 for SDout2 + 0080:0010 for SDout3 + hi: Base IO address of FIFO to which + the left-channel samples are to + be written. + lo: Displacement for the base IO + address for left-channel to obtain + the base IO address for the FIFO + to which the right-channel samples + are to be written. + */ + ___DSP_DUAL_16BIT_ALLOC( + left_chan_base_IO_addr, + right_chan_IO_disp + ) + + + /* Init: 0x0080:0004 for non-AC-97 + Init: 0x0080:0000 for AC-97 + hi: Exponential volume change rate + for input stream + lo: Positive shift count to shift the + 16-bit input sample to obtain the + 32-bit output word + */ + ___DSP_DUAL_16BIT_ALLOC( + CO_scale_shift_count, + CO_exp_vol_change_rate + ) + + /* Pointer to SCB at end of input chain */ + ___DSP_DUAL_16BIT_ALLOC( + reserved, + last_sub_ptr + ) +}; + +/* SCB for CODEC input algorithm */ +struct dsp_codec_input_scb { + /* First 13 dwords from generic_scb_t */ + struct dsp_basic_dma_req basic_req; /* Optional */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + u32 strm_rs_config; /* REQUIRED */ + u32 strm_buf_ptr; /* REQUIRED */ + + /* NOTE: The CODEC input task reads samples from the hardware FIFO + sublist at the DMA source address word (sub_list_ptr->basic_req.saw). + After the required number of samples is transferred, the CODEC + output task advances sub_list_ptr->basic_req.saw past the samples + consumed. SPuD must initialize the sub_list_ptr->basic_req.saw + to point half-way around from the initial sub_list_ptr->strm_nuf_ptr + to allow for lag/lead. + */ + + /* Init. 0000:0010 for SDout + 0060:0010 for SDout2 + 0080:0010 for SDout3 + hi: Base IO address of FIFO to which + the left-channel samples are to + be written. + lo: Displacement for the base IO + address for left-channel to obtain + the base IO address for the FIFO + to which the right-channel samples + are to be written. + */ + ___DSP_DUAL_16BIT_ALLOC( + rightChanINdisp, + left_chan_base_IN_addr + ) + /* Init. ?:fffc + lo: Negative shift count to shift the + 32-bit input dword to obtain the + 16-bit sample msb-aligned (count + is negative to shift left) + */ + ___DSP_DUAL_16BIT_ALLOC( + scaleShiftCount, + reserver1 + ) + + u32 reserved2; +}; + + +struct dsp_pcm_serial_input_scb { + /* First 13 dwords from generic_scb_t */ + struct dsp_basic_dma_req basic_req; /* Optional */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + u32 strm_buf_ptr; /* REQUIRED */ + u32 strm_rs_config; /* REQUIRED */ + + /* Init. Ptr to CODEC input SCB + hi: Pointer to the SCB containing the + input buffer to which CODEC input + samples are written + lo: Flag indicating the link to the CODEC + input task is to be initialized + */ + ___DSP_DUAL_16BIT_ALLOC( + init_codec_input_link, + codec_input_buf_scb + ) + + /* Initialized by the host (host updates target volumes) */ + struct dsp_volume_control psi_vol_ctrl; + +}; + +struct dsp_src_task_scb { + ___DSP_DUAL_16BIT_ALLOC( + frames_left_in_gof, + gofs_left_in_sec + ) + + ___DSP_DUAL_16BIT_ALLOC( + const2_thirds, + num_extra_tnput_samples + ) + + ___DSP_DUAL_16BIT_ALLOC( + cor_per_gof, + correction_per_sec + ) + + ___DSP_DUAL_16BIT_ALLOC( + output_buf_producer_ptr, + junk_DMA_MID + ) + + ___DSP_DUAL_16BIT_ALLOC( + gof_length, + gofs_per_sec + ) + + u32 input_buf_strm_config; + + ___DSP_DUAL_16BIT_ALLOC( + reserved_for_SRC_use, + input_buf_consumer_ptr + ) + + u32 accum_phi; + + ___DSP_DUAL_16BIT_ALLOC( + exp_src_vol_change_rate, + input_buf_producer_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + src_next_scb, + src_sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + src_entry_point, + src_this_sbp + ) + + u32 src_strm_rs_config; + u32 src_strm_buf_ptr; + + u32 phiIncr6int_26frac; + + struct dsp_volume_control src_vol_ctrl; +}; + +struct dsp_decimate_by_pow2_scb { + /* decimationFactor = 2, 4, or 8 (larger factors waste too much memory + when compared to cascading decimators) + */ + ___DSP_DUAL_16BIT_ALLOC( + dec2_coef_base_ptr, + dec2_coef_increment + ) + + /* coefIncrement = 128 / decimationFactor (for our ROM filter) + coefBasePtr = 0x8000 (for our ROM filter) + */ + ___DSP_DUAL_16BIT_ALLOC( + dec2_in_samples_per_out_triplet, + dec2_extra_in_samples + ) + /* extraInSamples: # of accumulated, unused input samples (init. to 0) + inSamplesPerOutTriplet = 3 * decimationFactor + */ + + ___DSP_DUAL_16BIT_ALLOC( + dec2_const2_thirds, + dec2_half_num_taps_mp5 + ) + /* halfNumTapsM5: (1/2 number of taps in decimation filter) minus 5 + const2thirds: constant 2/3 in 16Q0 format (sign.15) + */ + + ___DSP_DUAL_16BIT_ALLOC( + dec2_output_buf_producer_ptr, + dec2_junkdma_mid + ) + + u32 dec2_reserved2; + + u32 dec2_input_nuf_strm_config; + /* inputBufStrmConfig: rsConfig for the input buffer to the decimator + (buffer size = decimationFactor * 32 dwords) + */ + + ___DSP_DUAL_16BIT_ALLOC( + dec2_phi_incr, + dec2_input_buf_consumer_ptr + ) + /* inputBufConsumerPtr: Input buffer read pointer (into SRC filter) + phiIncr = decimationFactor * 4 + */ + + u32 dec2_reserved3; + + ___DSP_DUAL_16BIT_ALLOC( + dec2_exp_vol_change_rate, + dec2_input_buf_producer_ptr + ) + /* inputBufProducerPtr: Input buffer write pointer + expVolChangeRate: Exponential volume change rate for possible + future mixer on input streams + */ + + ___DSP_DUAL_16BIT_ALLOC( + dec2_next_scb, + dec2_sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + dec2_entry_point, + dec2_this_spb + ) + + u32 dec2_strm_rs_config; + u32 dec2_strm_buf_ptr; + + u32 dec2_reserved4; + + struct dsp_volume_control dec2_vol_ctrl; /* Not used! */ +}; + +struct dsp_vari_decimate_scb { + ___DSP_DUAL_16BIT_ALLOC( + vdec_frames_left_in_gof, + vdec_gofs_left_in_sec + ) + + ___DSP_DUAL_16BIT_ALLOC( + vdec_const2_thirds, + vdec_extra_in_samples + ) + /* extraInSamples: # of accumulated, unused input samples (init. to 0) + const2thirds: constant 2/3 in 16Q0 format (sign.15) */ + + ___DSP_DUAL_16BIT_ALLOC( + vdec_cor_per_gof, + vdec_correction_per_sec + ) + + ___DSP_DUAL_16BIT_ALLOC( + vdec_output_buf_producer_ptr, + vdec_input_buf_consumer_ptr + ) + /* inputBufConsumerPtr: Input buffer read pointer (into SRC filter) */ + ___DSP_DUAL_16BIT_ALLOC( + vdec_gof_length, + vdec_gofs_per_sec + ) + + u32 vdec_input_buf_strm_config; + /* inputBufStrmConfig: rsConfig for the input buffer to the decimator + (buffer size = 64 dwords) */ + u32 vdec_coef_increment; + /* coefIncrement = - 128.0 / decimationFactor (as a 32Q15 number) */ + + u32 vdec_accumphi; + /* accumPhi: accumulated fractional phase increment (6.26) */ + + ___DSP_DUAL_16BIT_ALLOC( + vdec_exp_vol_change_rate, + vdec_input_buf_producer_ptr + ) + /* inputBufProducerPtr: Input buffer write pointer + expVolChangeRate: Exponential volume change rate for possible + future mixer on input streams */ + + ___DSP_DUAL_16BIT_ALLOC( + vdec_next_scb, + vdec_sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + vdec_entry_point, + vdec_this_spb + ) + + u32 vdec_strm_rs_config; + u32 vdec_strm_buf_ptr; + + u32 vdec_phi_incr_6int_26frac; + + struct dsp_volume_control vdec_vol_ctrl; +}; + + +/* SCB for MIX_TO_OSTREAM algorithm family */ +struct dsp_mix2_ostream_scb { + /* First 13 dwords from generic_scb_t */ + struct dsp_basic_dma_req basic_req; /* Optional */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + u32 strm_rs_config; /* REQUIRED */ + u32 strm_buf_ptr; /* REQUIRED */ + + + /* hi: Number of mixed-down input triplets + computed since start of group + lo: Number of frames remaining to be + processed in the current group of + frames + */ + ___DSP_DUAL_16BIT_ALLOC( + frames_left_in_group, + accum_input_triplets + ) + + /* hi: Exponential volume change rate + for mixer on input streams + lo: Number of frames in the group + */ + ___DSP_DUAL_16BIT_ALLOC( + frame_group_length, + exp_vol_change_rate + ) + + ___DSP_DUAL_16BIT_ALLOC( + const_FFFF, + const_zero + ) +}; + + +/* SCB for S16_MIX algorithm */ +struct dsp_mix_only_scb { + /* First 13 dwords from generic_scb_t */ + struct dsp_basic_dma_req basic_req; /* Optional */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + u32 strm_rs_config; /* REQUIRED */ + u32 strm_buf_ptr; /* REQUIRED */ + + u32 reserved; + struct dsp_volume_control vol_ctrl; +}; + +/* SCB for the async. CODEC input algorithm */ +struct dsp_async_codec_input_scb { + u32 io_free2; + + u32 io_current_total; + u32 io_previous_total; + + u16 io_count; + u16 io_count_limit; + + u16 o_fifo_base_addr; + u16 ost_mo_format; + /* 1 = stereo; 0 = mono + xxx for ASER 1 (not allowed); 118 for ASER2 */ + + u32 ostrm_rs_config; + u32 ostrm_buf_ptr; + + ___DSP_DUAL_16BIT_ALLOC( + io_sclks_per_lr_clk, + io_io_enable + ) + + u32 io_free4; + + ___DSP_DUAL_16BIT_ALLOC( + io_next_scb, + io_sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + io_entry_point, + io_this_spb + ) + + u32 istrm_rs_config; + u32 istrm_buf_ptr; + + /* Init. 0000:8042: for ASER1 + 0000:8044: for ASER2 */ + ___DSP_DUAL_16BIT_ALLOC( + io_stat_reg_addr, + iofifo_pointer + ) + + /* Init 1 stero:100 ASER1 + Init 0 mono:110 ASER2 + */ + ___DSP_DUAL_16BIT_ALLOC( + ififo_base_addr, + ist_mo_format + ) + + u32 i_free; +}; + + +/* SCB for the SP/DIF CODEC input and output */ +struct dsp_spdifiscb { + ___DSP_DUAL_16BIT_ALLOC( + status_ptr, + status_start_ptr + ) + + u32 current_total; + u32 previous_total; + + ___DSP_DUAL_16BIT_ALLOC( + count, + count_limit + ) + + u32 status_data; + + ___DSP_DUAL_16BIT_ALLOC( + status, + free4 + ) + + u32 free3; + + ___DSP_DUAL_16BIT_ALLOC( + free2, + bit_count + ) + + u32 temp_status; + + ___DSP_DUAL_16BIT_ALLOC( + next_SCB, + sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, + this_spb + ) + + u32 strm_rs_config; + u32 strm_buf_ptr; + + ___DSP_DUAL_16BIT_ALLOC( + stat_reg_addr, + fifo_pointer + ) + + ___DSP_DUAL_16BIT_ALLOC( + fifo_base_addr, + st_mo_format + ) + + u32 free1; +}; + + +/* SCB for the SP/DIF CODEC input and output */ +struct dsp_spdifoscb { + + u32 free2; + + u32 free3[4]; + + /* Need to be here for compatibility with AsynchFGTxCode */ + u32 strm_rs_config; + + u32 strm_buf_ptr; + + ___DSP_DUAL_16BIT_ALLOC( + status, + free5 + ) + + u32 free4; + + ___DSP_DUAL_16BIT_ALLOC( + next_scb, + sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, + this_spb + ) + + u32 free6[2]; + + ___DSP_DUAL_16BIT_ALLOC( + stat_reg_addr, + fifo_pointer + ) + + ___DSP_DUAL_16BIT_ALLOC( + fifo_base_addr, + st_mo_format + ) + + u32 free1; +}; + + +struct dsp_asynch_fg_rx_scb { + ___DSP_DUAL_16BIT_ALLOC( + bot_buf_mask, + buf_Mask + ) + + ___DSP_DUAL_16BIT_ALLOC( + max, + min + ) + + ___DSP_DUAL_16BIT_ALLOC( + old_producer_pointer, + hfg_scb_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + delta, + adjust_count + ) + + u32 unused2[5]; + + ___DSP_DUAL_16BIT_ALLOC( + sibling_ptr, + child_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + code_ptr, + this_ptr + ) + + u32 strm_rs_config; + + u32 strm_buf_ptr; + + u32 unused_phi_incr; + + ___DSP_DUAL_16BIT_ALLOC( + right_targ, + left_targ + ) + + ___DSP_DUAL_16BIT_ALLOC( + right_vol, + left_vol + ) +}; + + +struct dsp_asynch_fg_tx_scb { + ___DSP_DUAL_16BIT_ALLOC( + not_buf_mask, + buf_mask + ) + + ___DSP_DUAL_16BIT_ALLOC( + max, + min + ) + + ___DSP_DUAL_16BIT_ALLOC( + unused1, + hfg_scb_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + delta, + adjust_count + ) + + u32 accum_phi; + + ___DSP_DUAL_16BIT_ALLOC( + unused2, + const_one_third + ) + + u32 unused3[3]; + + ___DSP_DUAL_16BIT_ALLOC( + sibling_ptr, + child_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + codePtr, + this_ptr + ) + + u32 strm_rs_config; + + u32 strm_buf_ptr; + + u32 phi_incr; + + ___DSP_DUAL_16BIT_ALLOC( + unused_right_targ, + unused_left_targ + ) + + ___DSP_DUAL_16BIT_ALLOC( + unused_right_vol, + unused_left_vol + ) +}; + + +struct dsp_output_snoop_scb { + /* First 13 dwords from generic_scb_t */ + struct dsp_basic_dma_req basic_req; /* Optional */ + struct dsp_scatter_gather_ext sg_ext; /* Optional */ + ___DSP_DUAL_16BIT_ALLOC( + next_scb, /* REQUIRED */ + sub_list_ptr /* REQUIRED */ + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* REQUIRED */ + this_spb /* REQUIRED */ + ) + + u32 strm_rs_config; /* REQUIRED */ + u32 strm_buf_ptr; /* REQUIRED */ + + ___DSP_DUAL_16BIT_ALLOC( + init_snoop_input_link, + snoop_child_input_scb + ) + + u32 snoop_input_buf_ptr; + + ___DSP_DUAL_16BIT_ALLOC( + reserved, + input_scb + ) +}; + +struct dsp_spio_write_scb { + ___DSP_DUAL_16BIT_ALLOC( + address1, + address2 + ) + + u32 data1; + + u32 data2; + + ___DSP_DUAL_16BIT_ALLOC( + address3, + address4 + ) + + u32 data3; + + u32 data4; + + ___DSP_DUAL_16BIT_ALLOC( + unused1, + data_ptr + ) + + u32 unused2[2]; + + ___DSP_DUAL_16BIT_ALLOC( + sibling_ptr, + child_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, + this_ptr + ) + + u32 unused3[5]; +}; + +struct dsp_magic_snoop_task { + u32 i0; + u32 i1; + + u32 strm_buf_ptr1; + + u16 i2; + u16 snoop_scb; + + u32 i3; + u32 i4; + u32 i5; + u32 i6; + + u32 i7; + + ___DSP_DUAL_16BIT_ALLOC( + next_scb, + sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, + this_ptr + ) + + u32 strm_buf_config; + u32 strm_buf_ptr2; + + u32 i8; + + struct dsp_volume_control vdec_vol_ctrl; +}; + + +struct dsp_filter_scb { + ___DSP_DUAL_16BIT_ALLOC( + a0_right, /* 0x00 */ + a0_left + ) + ___DSP_DUAL_16BIT_ALLOC( + a1_right, /* 0x01 */ + a1_left + ) + ___DSP_DUAL_16BIT_ALLOC( + a2_right, /* 0x02 */ + a2_left + ) + ___DSP_DUAL_16BIT_ALLOC( + output_buf_ptr, /* 0x03 */ + init + ) + + ___DSP_DUAL_16BIT_ALLOC( + filter_unused3, /* 0x04 */ + filter_unused2 + ) + + u32 prev_sample_output1; /* 0x05 */ + u32 prev_sample_output2; /* 0x06 */ + u32 prev_sample_input1; /* 0x07 */ + u32 prev_sample_input2; /* 0x08 */ + + ___DSP_DUAL_16BIT_ALLOC( + next_scb_ptr, /* 0x09 */ + sub_list_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + entry_point, /* 0x0A */ + spb_ptr + ) + + u32 strm_rs_config; /* 0x0B */ + u32 strm_buf_ptr; /* 0x0C */ + + ___DSP_DUAL_16BIT_ALLOC( + b0_right, /* 0x0D */ + b0_left + ) + ___DSP_DUAL_16BIT_ALLOC( + b1_right, /* 0x0E */ + b1_left + ) + ___DSP_DUAL_16BIT_ALLOC( + b2_right, /* 0x0F */ + b2_left + ) +}; +#endif /* __DSP_SCB_TYPES_H__ */ diff --git a/sound/pci/cs46xx/cs46xx_dsp_spos.h b/sound/pci/cs46xx/cs46xx_dsp_spos.h new file mode 100644 index 000000000000..8008c59288a6 --- /dev/null +++ b/sound/pci/cs46xx/cs46xx_dsp_spos.h @@ -0,0 +1,234 @@ +/* + * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __CS46XX_DSP_SPOS_H__ +#define __CS46XX_DSP_SPOS_H__ + +#include "cs46xx_dsp_scb_types.h" +#include "cs46xx_dsp_task_types.h" + +#define SYMBOL_CONSTANT 0x0 +#define SYMBOL_SAMPLE 0x1 +#define SYMBOL_PARAMETER 0x2 +#define SYMBOL_CODE 0x3 + +#define SEGTYPE_SP_PROGRAM 0x00000001 +#define SEGTYPE_SP_PARAMETER 0x00000002 +#define SEGTYPE_SP_SAMPLE 0x00000003 +#define SEGTYPE_SP_COEFFICIENT 0x00000004 + +#define DSP_SPOS_UU 0x0deadul /* unused */ +#define DSP_SPOS_DC 0x0badul /* don't care */ +#define DSP_SPOS_DC_DC 0x0bad0badul /* don't care */ +#define DSP_SPOS_UUUU 0xdeadc0edul /* unused */ +#define DSP_SPOS_UUHI 0xdeadul +#define DSP_SPOS_UULO 0xc0edul +#define DSP_SPOS_DCDC 0x0badf1d0ul /* don't care */ +#define DSP_SPOS_DCDCHI 0x0badul +#define DSP_SPOS_DCDCLO 0xf1d0ul + +#define DSP_MAX_TASK_NAME 60 +#define DSP_MAX_SYMBOL_NAME 100 +#define DSP_MAX_SCB_NAME 60 +#define DSP_MAX_SCB_DESC 200 +#define DSP_MAX_TASK_DESC 50 + +#define DSP_MAX_PCM_CHANNELS 32 +#define DSP_MAX_SRC_NR 14 + +#define DSP_PCM_MAIN_CHANNEL 1 +#define DSP_PCM_REAR_CHANNEL 2 +#define DSP_PCM_CENTER_LFE_CHANNEL 3 +#define DSP_PCM_S71_CHANNEL 4 /* surround 7.1 */ +#define DSP_IEC958_CHANNEL 5 + +#define DSP_SPDIF_STATUS_OUTPUT_ENABLED 1 +#define DSP_SPDIF_STATUS_PLAYBACK_OPEN 2 +#define DSP_SPDIF_STATUS_HW_ENABLED 4 +#define DSP_SPDIF_STATUS_INPUT_CTRL_ENABLED 8 + +struct dsp_symbol_entry { + u32 address; + char symbol_name[DSP_MAX_SYMBOL_NAME]; + int symbol_type; + + /* initialized by driver */ + struct dsp_module_desc * module; + int deleted; +}; + +struct dsp_symbol_desc { + int nsymbols; + + struct dsp_symbol_entry *symbols; + + /* initialized by driver */ + int highest_frag_index; +}; + +struct dsp_segment_desc { + int segment_type; + u32 offset; + u32 size; + u32 * data; +}; + +struct dsp_module_desc { + char * module_name; + struct dsp_symbol_desc symbol_table; + int nsegments; + struct dsp_segment_desc * segments; + + /* initialized by driver */ + u32 overlay_begin_address; + u32 load_address; + int nfixups; +}; + +struct dsp_scb_descriptor { + char scb_name[DSP_MAX_SCB_NAME]; + u32 address; + int index; + u32 *data; + + struct dsp_scb_descriptor * sub_list_ptr; + struct dsp_scb_descriptor * next_scb_ptr; + struct dsp_scb_descriptor * parent_scb_ptr; + + struct dsp_symbol_entry * task_entry; + struct dsp_symbol_entry * scb_symbol; + + struct snd_info_entry *proc_info; + int ref_count; + + u16 volume[2]; + unsigned int deleted :1; + unsigned int updated :1; + unsigned int volume_set :1; +}; + +struct dsp_task_descriptor { + char task_name[DSP_MAX_TASK_NAME]; + int size; + u32 address; + int index; + u32 *data; +}; + +struct dsp_pcm_channel_descriptor { + int active; + int src_slot; + int pcm_slot; + u32 sample_rate; + u32 unlinked; + struct dsp_scb_descriptor * pcm_reader_scb; + struct dsp_scb_descriptor * src_scb; + struct dsp_scb_descriptor * mixer_scb; + + void * private_data; +}; + +struct dsp_spos_instance { + struct dsp_symbol_desc symbol_table; /* currently available loaded symbols in SP */ + + int nmodules; + struct dsp_module_desc * modules; /* modules loaded into SP */ + + struct dsp_segment_desc code; + + /* Main PCM playback mixer */ + struct dsp_scb_descriptor * master_mix_scb; + u16 dac_volume_right; + u16 dac_volume_left; + + /* Rear/surround PCM playback mixer */ + struct dsp_scb_descriptor * rear_mix_scb; + + /* Center/LFE mixer */ + struct dsp_scb_descriptor * center_lfe_mix_scb; + + int npcm_channels; + int nsrc_scb; + struct dsp_pcm_channel_descriptor pcm_channels[DSP_MAX_PCM_CHANNELS]; + int src_scb_slots[DSP_MAX_SRC_NR]; + + /* cache this symbols */ + struct dsp_symbol_entry * null_algorithm; /* used by PCMreaderSCB's */ + struct dsp_symbol_entry * s16_up; /* used by SRCtaskSCB's */ + + /* proc fs */ + struct snd_card *snd_card; + struct snd_info_entry * proc_dsp_dir; + struct snd_info_entry * proc_sym_info_entry; + struct snd_info_entry * proc_modules_info_entry; + struct snd_info_entry * proc_parameter_dump_info_entry; + struct snd_info_entry * proc_sample_dump_info_entry; + + /* SCB's descriptors */ + int nscb; + int scb_highest_frag_index; + struct dsp_scb_descriptor scbs[DSP_MAX_SCB_DESC]; + struct snd_info_entry * proc_scb_info_entry; + struct dsp_scb_descriptor * the_null_scb; + + /* Task's descriptors */ + int ntask; + struct dsp_task_descriptor tasks[DSP_MAX_TASK_DESC]; + struct snd_info_entry * proc_task_info_entry; + + /* SPDIF status */ + int spdif_status_out; + int spdif_status_in; + u16 spdif_input_volume_right; + u16 spdif_input_volume_left; + /* spdif channel status, + left right and user validity bits */ + unsigned int spdif_csuv_default; + unsigned int spdif_csuv_stream; + + /* SPDIF input sample rate converter */ + struct dsp_scb_descriptor * spdif_in_src; + /* SPDIF input asynch. receiver */ + struct dsp_scb_descriptor * asynch_rx_scb; + + /* Capture record mixer SCB */ + struct dsp_scb_descriptor * record_mixer_scb; + + /* CODEC input SCB */ + struct dsp_scb_descriptor * codec_in_scb; + + /* reference snooper */ + struct dsp_scb_descriptor * ref_snoop_scb; + + /* SPDIF output PCM reference */ + struct dsp_scb_descriptor * spdif_pcm_input_scb; + + /* asynch TX task */ + struct dsp_scb_descriptor * asynch_tx_scb; + + /* record sources */ + struct dsp_scb_descriptor * pcm_input; + struct dsp_scb_descriptor * adc_input; + + int spdif_in_sample_rate; +}; + +#endif /* __DSP_SPOS_H__ */ diff --git a/sound/pci/cs46xx/cs46xx_dsp_task_types.h b/sound/pci/cs46xx/cs46xx_dsp_task_types.h new file mode 100644 index 000000000000..5cf920bfda27 --- /dev/null +++ b/sound/pci/cs46xx/cs46xx_dsp_task_types.h @@ -0,0 +1,252 @@ +/* + * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * NOTE: comments are copy/paste from cwcemb80.lst + * provided by Tom Woller at Cirrus (my only + * documentation about the SP OS running inside + * the DSP) + */ + +#ifndef __CS46XX_DSP_TASK_TYPES_H__ +#define __CS46XX_DSP_TASK_TYPES_H__ + +#include "cs46xx_dsp_scb_types.h" + +/********************************************************************************************* +Example hierarchy of stream control blocks in the SP + +hfgTree +Ptr____Call (c) + \ + -------+------ ------------- ------------- ------------- ----- +| SBlaster IF |______\| Foreground |___\| Middlegr'nd |___\| Background |___\| Nul | +| |Goto /| tree header |g /| tree header |g /| tree header |g /| SCB |r + -------------- (g) ------------- ------------- ------------- ----- + |c |c |c |c + | | | | + \/ ------------- ------------- ------------- + | Foreground |_\ | Middlegr'nd |_\ | Background |_\ + | tree |g/ | tree |g/ | tree |g/ + ------------- ------------- ------------- + |c |c |c + | | | + \/ \/ \/ + +*********************************************************************************************/ + +#define HFG_FIRST_EXECUTE_MODE 0x0001 +#define HFG_FIRST_EXECUTE_MODE_BIT 0 +#define HFG_CONTEXT_SWITCH_MODE 0x0002 +#define HFG_CONTEXT_SWITCH_MODE_BIT 1 + +#define MAX_FG_STACK_SIZE 32 /* THESE NEED TO BE COMPUTED PROPERLY */ +#define MAX_MG_STACK_SIZE 16 +#define MAX_BG_STACK_SIZE 9 +#define MAX_HFG_STACK_SIZE 4 + +#define SLEEP_ACTIVE_INCREMENT 0 /* Enable task tree thread to go to sleep + This should only ever be used on the Background thread */ +#define STANDARD_ACTIVE_INCREMENT 1 /* Task tree thread normal operation */ +#define SUSPEND_ACTIVE_INCREMENT 2 /* Cause execution to suspend in the task tree thread + This should only ever be used on the Background thread */ + +#define HOSTFLAGS_DISABLE_BG_SLEEP 0 /* Host-controlled flag that determines whether we go to sleep + at the end of BG */ + +/* Minimal context save area for Hyper Forground */ +struct dsp_hf_save_area { + u32 r10_save; + u32 r54_save; + u32 r98_save; + + ___DSP_DUAL_16BIT_ALLOC( + status_save, + ind_save + ) + + ___DSP_DUAL_16BIT_ALLOC( + rci1_save, + rci0_save + ) + + u32 r32_save; + u32 r76_save; + u32 rsd2_save; + + ___DSP_DUAL_16BIT_ALLOC( + rsi2_save, /* See TaskTreeParameterBlock for + remainder of registers */ + rsa2Save + ) + /* saved as part of HFG context */ +}; + + +/* Task link data structure */ +struct dsp_tree_link { + ___DSP_DUAL_16BIT_ALLOC( + /* Pointer to sibling task control block */ + next_scb, + /* Pointer to child task control block */ + sub_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + /* Pointer to code entry point */ + entry_point, + /* Pointer to local data */ + this_spb + ) +}; + + +struct dsp_task_tree_data { + ___DSP_DUAL_16BIT_ALLOC( + /* Initial tock count; controls task tree execution rate */ + tock_count_limit, + /* Tock down counter */ + tock_count + ) + + /* Add to ActiveCount when TockCountLimit reached: + Subtract on task tree termination */ + ___DSP_DUAL_16BIT_ALLOC( + active_tncrement, + /* Number of pending activations for task tree */ + active_count + ) + + ___DSP_DUAL_16BIT_ALLOC( + /* BitNumber to enable modification of correct bit in ActiveTaskFlags */ + active_bit, + /* Pointer to OS location for indicating current activity on task level */ + active_task_flags_ptr + ) + + /* Data structure for controlling movement of memory blocks:- + currently unused */ + ___DSP_DUAL_16BIT_ALLOC( + mem_upd_ptr, + /* Data structure for controlling synchronous link update */ + link_upd_ptr + ) + + ___DSP_DUAL_16BIT_ALLOC( + /* Save area for remainder of full context. */ + save_area, + /* Address of start of local stack for data storage */ + data_stack_base_ptr + ) + +}; + + +struct dsp_interval_timer_data +{ + /* These data items have the same relative locations to those */ + ___DSP_DUAL_16BIT_ALLOC( + interval_timer_period, + itd_unused + ) + + /* used for this data in the SPOS control block for SPOS 1.0 */ + ___DSP_DUAL_16BIT_ALLOC( + num_FG_ticks_this_interval, + num_intervals + ) +}; + + +/* This structure contains extra storage for the task tree + Currently, this additional data is related only to a full context save */ +struct dsp_task_tree_context_block { + /* Up to 10 values are saved onto the stack. 8 for the task tree, 1 for + The access to the context switch (call or interrupt), and 1 spare that + users should never use. This last may be required by the system */ + ___DSP_DUAL_16BIT_ALLOC( + stack1, + stack0 + ) + ___DSP_DUAL_16BIT_ALLOC( + stack3, + stack2 + ) + ___DSP_DUAL_16BIT_ALLOC( + stack5, + stack4 + ) + ___DSP_DUAL_16BIT_ALLOC( + stack7, + stack6 + ) + ___DSP_DUAL_16BIT_ALLOC( + stack9, + stack8 + ) + + u32 saverfe; + + /* Value may be overwriten by stack save algorithm. + Retain the size of the stack data saved here if used */ + ___DSP_DUAL_16BIT_ALLOC( + reserved1, + stack_size + ) + u32 saverba; /* (HFG) */ + u32 saverdc; + u32 savers_config_23; /* (HFG) */ + u32 savers_DMA23; /* (HFG) */ + u32 saversa0; + u32 saversi0; + u32 saversa1; + u32 saversi1; + u32 saversa3; + u32 saversd0; + u32 saversd1; + u32 saversd3; + u32 savers_config01; + u32 savers_DMA01; + u32 saveacc0hl; + u32 saveacc1hl; + u32 saveacc0xacc1x; + u32 saveacc2hl; + u32 saveacc3hl; + u32 saveacc2xacc3x; + u32 saveaux0hl; + u32 saveaux1hl; + u32 saveaux0xaux1x; + u32 saveaux2hl; + u32 saveaux3hl; + u32 saveaux2xaux3x; + u32 savershouthl; + u32 savershoutxmacmode; +}; + + +struct dsp_task_tree_control_block { + struct dsp_hf_save_area context; + struct dsp_tree_link links; + struct dsp_task_tree_data data; + struct dsp_task_tree_context_block context_blk; + struct dsp_interval_timer_data int_timer; +}; + + +#endif /* __DSP_TASK_TYPES_H__ */ diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 28b9747becc9..f75f5ffdfdfb 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -61,7 +61,7 @@ #include #include #include -#include +#include "cs46xx.h" #include diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index e377287192aa..56fec0bc0efb 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include "cs46xx.h" #include "cs46xx_lib.h" #include "dsp_spos.h" diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c index 00b148a10239..c2c695b07f8c 100644 --- a/sound/pci/cs46xx/dsp_spos_scb_lib.c +++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include "cs46xx.h" #include "cs46xx_lib.h" #include "dsp_spos.h" diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index f61346a555bb..d36e6ca147e1 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include "trident.h" #include MODULE_AUTHOR("Jaroslav Kysela , "); diff --git a/sound/pci/trident/trident.h b/sound/pci/trident/trident.h new file mode 100644 index 000000000000..5f110eb56e47 --- /dev/null +++ b/sound/pci/trident/trident.h @@ -0,0 +1,444 @@ +#ifndef __SOUND_TRIDENT_H +#define __SOUND_TRIDENT_H + +/* + * audio@tridentmicro.com + * Fri Feb 19 15:55:28 MST 1999 + * Definitions for Trident 4DWave DX/NX chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#define TRIDENT_DEVICE_ID_DX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_DX) +#define TRIDENT_DEVICE_ID_NX ((PCI_VENDOR_ID_TRIDENT<<16)|PCI_DEVICE_ID_TRIDENT_4DWAVE_NX) +#define TRIDENT_DEVICE_ID_SI7018 ((PCI_VENDOR_ID_SI<<16)|PCI_DEVICE_ID_SI_7018) + +#define SNDRV_TRIDENT_VOICE_TYPE_PCM 0 +#define SNDRV_TRIDENT_VOICE_TYPE_SYNTH 1 +#define SNDRV_TRIDENT_VOICE_TYPE_MIDI 2 + +#define SNDRV_TRIDENT_VFLG_RUNNING (1<<0) + +/* TLB code constants */ +#define SNDRV_TRIDENT_PAGE_SIZE 4096 +#define SNDRV_TRIDENT_PAGE_SHIFT 12 +#define SNDRV_TRIDENT_PAGE_MASK ((1<port + (x)) + +#define ID_4DWAVE_DX 0x2000 +#define ID_4DWAVE_NX 0x2001 + +/* Bank definitions */ + +#define T4D_BANK_A 0 +#define T4D_BANK_B 1 +#define T4D_NUM_BANKS 2 + +/* Register definitions */ + +/* Global registers */ + +enum global_control_bits { + CHANNEL_IDX = 0x0000003f, + OVERRUN_IE = 0x00000400, /* interrupt enable: capture overrun */ + UNDERRUN_IE = 0x00000800, /* interrupt enable: playback underrun */ + ENDLP_IE = 0x00001000, /* interrupt enable: end of buffer */ + MIDLP_IE = 0x00002000, /* interrupt enable: middle buffer */ + ETOG_IE = 0x00004000, /* interrupt enable: envelope toggling */ + EDROP_IE = 0x00008000, /* interrupt enable: envelope drop */ + BANK_B_EN = 0x00010000, /* SiS: enable bank B (64 channels) */ + PCMIN_B_MIX = 0x00020000, /* SiS: PCM IN B mixing enable */ + I2S_OUT_ASSIGN = 0x00040000, /* SiS: I2S Out contains surround PCM */ + SPDIF_OUT_ASSIGN= 0x00080000, /* SiS: 0=S/PDIF L/R | 1=PCM Out FIFO */ + MAIN_OUT_ASSIGN = 0x00100000, /* SiS: 0=PCM Out FIFO | 1=MMC Out buffer */ +}; + +enum miscint_bits { + PB_UNDERRUN_IRQ = 0x00000001, REC_OVERRUN_IRQ = 0x00000002, + SB_IRQ = 0x00000004, MPU401_IRQ = 0x00000008, + OPL3_IRQ = 0x00000010, ADDRESS_IRQ = 0x00000020, + ENVELOPE_IRQ = 0x00000040, PB_UNDERRUN = 0x00000100, + REC_OVERRUN = 0x00000200, MIXER_UNDERFLOW = 0x00000400, + MIXER_OVERFLOW = 0x00000800, NX_SB_IRQ_DISABLE = 0x00001000, + ST_TARGET_REACHED = 0x00008000, + PB_24K_MODE = 0x00010000, ST_IRQ_EN = 0x00800000, + ACGPIO_IRQ = 0x01000000 +}; + +/* T2 legacy dma control registers. */ +#define LEGACY_DMAR0 0x00 // ADR0 +#define LEGACY_DMAR4 0x04 // CNT0 +#define LEGACY_DMAR6 0x06 // CNT0 - High bits +#define LEGACY_DMAR11 0x0b // MOD +#define LEGACY_DMAR15 0x0f // MMR + +#define T4D_START_A 0x80 +#define T4D_STOP_A 0x84 +#define T4D_DLY_A 0x88 +#define T4D_SIGN_CSO_A 0x8c +#define T4D_CSPF_A 0x90 +#define T4D_CSPF_B 0xbc +#define T4D_CEBC_A 0x94 +#define T4D_AINT_A 0x98 +#define T4D_AINTEN_A 0x9c +#define T4D_LFO_GC_CIR 0xa0 +#define T4D_MUSICVOL_WAVEVOL 0xa8 +#define T4D_SBDELTA_DELTA_R 0xac +#define T4D_MISCINT 0xb0 +#define T4D_START_B 0xb4 +#define T4D_STOP_B 0xb8 +#define T4D_SBBL_SBCL 0xc0 +#define T4D_SBCTRL_SBE2R_SBDD 0xc4 +#define T4D_STIMER 0xc8 +#define T4D_AINT_B 0xd8 +#define T4D_AINTEN_B 0xdc +#define T4D_RCI 0x70 + +/* MPU-401 UART */ +#define T4D_MPU401_BASE 0x20 +#define T4D_MPUR0 0x20 +#define T4D_MPUR1 0x21 +#define T4D_MPUR2 0x22 +#define T4D_MPUR3 0x23 + +/* S/PDIF Registers */ +#define NX_SPCTRL_SPCSO 0x24 +#define NX_SPLBA 0x28 +#define NX_SPESO 0x2c +#define NX_SPCSTATUS 0x64 + +/* Joystick */ +#define GAMEPORT_GCR 0x30 +#define GAMEPORT_MODE_ADC 0x80 +#define GAMEPORT_LEGACY 0x31 +#define GAMEPORT_AXES 0x34 + +/* NX Specific Registers */ +#define NX_TLBC 0x6c + +/* Channel Registers */ + +#define CH_START 0xe0 + +#define CH_DX_CSO_ALPHA_FMS 0xe0 +#define CH_DX_ESO_DELTA 0xe8 +#define CH_DX_FMC_RVOL_CVOL 0xec + +#define CH_NX_DELTA_CSO 0xe0 +#define CH_NX_DELTA_ESO 0xe8 +#define CH_NX_ALPHA_FMS_FMC_RVOL_CVOL 0xec + +#define CH_LBA 0xe4 +#define CH_GVSEL_PAN_VOL_CTRL_EC 0xf0 +#define CH_EBUF1 0xf4 +#define CH_EBUF2 0xf8 + +/* AC-97 Registers */ + +#define DX_ACR0_AC97_W 0x40 +#define DX_ACR1_AC97_R 0x44 +#define DX_ACR2_AC97_COM_STAT 0x48 + +#define NX_ACR0_AC97_COM_STAT 0x40 +#define NX_ACR1_AC97_W 0x44 +#define NX_ACR2_AC97_R_PRIMARY 0x48 +#define NX_ACR3_AC97_R_SECONDARY 0x4c + +#define SI_AC97_WRITE 0x40 +#define SI_AC97_READ 0x44 +#define SI_SERIAL_INTF_CTRL 0x48 +#define SI_AC97_GPIO 0x4c +#define SI_ASR0 0x50 +#define SI_SPDIF_CS 0x70 +#define SI_GPIO 0x7c + +enum trident_nx_ac97_bits { + /* ACR1-3 */ + NX_AC97_BUSY_WRITE = 0x0800, + NX_AC97_BUSY_READ = 0x0800, + NX_AC97_BUSY_DATA = 0x0400, + NX_AC97_WRITE_SECONDARY = 0x0100, + /* ACR0 */ + NX_AC97_SECONDARY_READY = 0x0040, + NX_AC97_SECONDARY_RECORD = 0x0020, + NX_AC97_SURROUND_OUTPUT = 0x0010, + NX_AC97_PRIMARY_READY = 0x0008, + NX_AC97_PRIMARY_RECORD = 0x0004, + NX_AC97_PCM_OUTPUT = 0x0002, + NX_AC97_WARM_RESET = 0x0001 +}; + +enum trident_dx_ac97_bits { + DX_AC97_BUSY_WRITE = 0x8000, + DX_AC97_BUSY_READ = 0x8000, + DX_AC97_READY = 0x0010, + DX_AC97_RECORD = 0x0008, + DX_AC97_PLAYBACK = 0x0002 +}; + +enum sis7018_ac97_bits { + SI_AC97_BUSY_WRITE = 0x00008000, + SI_AC97_AUDIO_BUSY = 0x00004000, + SI_AC97_MODEM_BUSY = 0x00002000, + SI_AC97_BUSY_READ = 0x00008000, + SI_AC97_SECONDARY = 0x00000080, +}; + +enum serial_intf_ctrl_bits { + WARM_RESET = 0x00000001, + COLD_RESET = 0x00000002, + I2S_CLOCK = 0x00000004, + PCM_SEC_AC97 = 0x00000008, + AC97_DBL_RATE = 0x00000010, + SPDIF_EN = 0x00000020, + I2S_OUTPUT_EN = 0x00000040, + I2S_INPUT_EN = 0x00000080, + PCMIN = 0x00000100, + LINE1IN = 0x00000200, + MICIN = 0x00000400, + LINE2IN = 0x00000800, + HEAD_SET_IN = 0x00001000, + GPIOIN = 0x00002000, + /* 7018 spec says id = 01 but the demo board routed to 10 + SECONDARY_ID= 0x00004000, */ + SECONDARY_ID = 0x00004000, + PCMOUT = 0x00010000, + SURROUT = 0x00020000, + CENTEROUT = 0x00040000, + LFEOUT = 0x00080000, + LINE1OUT = 0x00100000, + LINE2OUT = 0x00200000, + GPIOOUT = 0x00400000, + SI_AC97_PRIMARY_READY = 0x01000000, + SI_AC97_SECONDARY_READY = 0x02000000, + SI_AC97_POWERDOWN = 0x04000000, +}; + +/* PCM defaults */ + +#define T4D_DEFAULT_PCM_VOL 10 /* 0 - 255 */ +#define T4D_DEFAULT_PCM_PAN 0 /* 0 - 127 */ +#define T4D_DEFAULT_PCM_RVOL 127 /* 0 - 127 */ +#define T4D_DEFAULT_PCM_CVOL 127 /* 0 - 127 */ + +struct snd_trident; +struct snd_trident_voice; +struct snd_trident_pcm_mixer; + +struct snd_trident_port { + struct snd_midi_channel_set * chset; + struct snd_trident * trident; + int mode; /* operation mode */ + int client; /* sequencer client number */ + int port; /* sequencer port number */ + unsigned int midi_has_voices: 1; +}; + +struct snd_trident_memblk_arg { + short first_page, last_page; +}; + +struct snd_trident_tlb { + unsigned int * entries; /* 16k-aligned TLB table */ + dma_addr_t entries_dmaaddr; /* 16k-aligned PCI address to TLB table */ + unsigned long * shadow_entries; /* shadow entries with virtual addresses */ + struct snd_dma_buffer buffer; + struct snd_util_memhdr * memhdr; /* page allocation list */ + struct snd_dma_buffer silent_page; +}; + +struct snd_trident_voice { + unsigned int number; + unsigned int use: 1, + pcm: 1, + synth:1, + midi: 1; + unsigned int flags; + unsigned char client; + unsigned char port; + unsigned char index; + + struct snd_trident_sample_ops *sample_ops; + + /* channel parameters */ + unsigned int CSO; /* 24 bits (16 on DX) */ + unsigned int ESO; /* 24 bits (16 on DX) */ + unsigned int LBA; /* 30 bits */ + unsigned short EC; /* 12 bits */ + unsigned short Alpha; /* 12 bits */ + unsigned short Delta; /* 16 bits */ + unsigned short Attribute; /* 16 bits - SiS 7018 */ + unsigned short Vol; /* 12 bits (6.6) */ + unsigned char Pan; /* 7 bits (1.4.2) */ + unsigned char GVSel; /* 1 bit */ + unsigned char RVol; /* 7 bits (5.2) */ + unsigned char CVol; /* 7 bits (5.2) */ + unsigned char FMC; /* 2 bits */ + unsigned char CTRL; /* 4 bits */ + unsigned char FMS; /* 4 bits */ + unsigned char LFO; /* 8 bits */ + + unsigned int negCSO; /* nonzero - use negative CSO */ + + struct snd_util_memblk *memblk; /* memory block if TLB enabled */ + + /* PCM data */ + + struct snd_trident *trident; + struct snd_pcm_substream *substream; + struct snd_trident_voice *extra; /* extra PCM voice (acts as interrupt generator) */ + unsigned int running: 1, + capture: 1, + spdif: 1, + foldback: 1, + isync: 1, + isync2: 1, + isync3: 1; + int foldback_chan; /* foldback subdevice number */ + unsigned int stimer; /* global sample timer (to detect spurious interrupts) */ + unsigned int spurious_threshold; /* spurious threshold */ + unsigned int isync_mark; + unsigned int isync_max; + unsigned int isync_ESO; + + /* --- */ + + void *private_data; + void (*private_free)(struct snd_trident_voice *voice); +}; + +struct snd_4dwave { + int seq_client; + + struct snd_trident_port seq_ports[4]; + struct snd_trident_voice voices[64]; + + int ChanSynthCount; /* number of allocated synth channels */ + int max_size; /* maximum synth memory size in bytes */ + int current_size; /* current allocated synth mem in bytes */ +}; + +struct snd_trident_pcm_mixer { + struct snd_trident_voice *voice; /* active voice */ + unsigned short vol; /* front volume */ + unsigned char pan; /* pan control */ + unsigned char rvol; /* rear volume */ + unsigned char cvol; /* center volume */ + unsigned char pad; +}; + +struct snd_trident { + int irq; + + unsigned int device; /* device ID */ + + unsigned char bDMAStart; + + unsigned long port; + unsigned long midi_port; + + unsigned int spurious_irq_count; + unsigned int spurious_irq_max_delta; + + struct snd_trident_tlb tlb; /* TLB entries for NX cards */ + + unsigned char spdif_ctrl; + unsigned char spdif_pcm_ctrl; + unsigned int spdif_bits; + unsigned int spdif_pcm_bits; + struct snd_kcontrol *spdif_pcm_ctl; /* S/PDIF settings */ + unsigned int ac97_ctrl; + + unsigned int ChanMap[2]; /* allocation map for hardware channels */ + + int ChanPCM; /* max number of PCM channels */ + int ChanPCMcnt; /* actual number of PCM channels */ + + unsigned int ac97_detect: 1; /* 1 = AC97 in detection phase */ + unsigned int in_suspend: 1; /* 1 during suspend/resume */ + + struct snd_4dwave synth; /* synth specific variables */ + + spinlock_t event_lock; + spinlock_t voice_alloc; + + struct snd_dma_device dma_dev; + + struct pci_dev *pci; + struct snd_card *card; + struct snd_pcm *pcm; /* ADC/DAC PCM */ + struct snd_pcm *foldback; /* Foldback PCM */ + struct snd_pcm *spdif; /* SPDIF PCM */ + struct snd_rawmidi *rmidi; + + struct snd_ac97_bus *ac97_bus; + struct snd_ac97 *ac97; + struct snd_ac97 *ac97_sec; + + unsigned int musicvol_wavevol; + struct snd_trident_pcm_mixer pcm_mixer[32]; + struct snd_kcontrol *ctl_vol; /* front volume */ + struct snd_kcontrol *ctl_pan; /* pan */ + struct snd_kcontrol *ctl_rvol; /* rear volume */ + struct snd_kcontrol *ctl_cvol; /* center volume */ + + spinlock_t reg_lock; + + struct gameport *gameport; +}; + +int snd_trident_create(struct snd_card *card, + struct pci_dev *pci, + int pcm_streams, + int pcm_spdif_device, + int max_wavetable_size, + struct snd_trident ** rtrident); +int snd_trident_create_gameport(struct snd_trident *trident); + +int snd_trident_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm); +int snd_trident_foldback_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm); +int snd_trident_spdif_pcm(struct snd_trident * trident, int device, struct snd_pcm **rpcm); +int snd_trident_attach_synthesizer(struct snd_trident * trident); +struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, + int client, int port); +void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice); +void snd_trident_start_voice(struct snd_trident * trident, unsigned int voice); +void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice); +void snd_trident_write_voice_regs(struct snd_trident * trident, struct snd_trident_voice *voice); +extern const struct dev_pm_ops snd_trident_pm; + +/* TLB memory allocation */ +struct snd_util_memblk *snd_trident_alloc_pages(struct snd_trident *trident, + struct snd_pcm_substream *substream); +int snd_trident_free_pages(struct snd_trident *trident, struct snd_util_memblk *blk); +struct snd_util_memblk *snd_trident_synth_alloc(struct snd_trident *trident, unsigned int size); +int snd_trident_synth_free(struct snd_trident *trident, struct snd_util_memblk *blk); +int snd_trident_synth_copy_from_user(struct snd_trident *trident, struct snd_util_memblk *blk, + int offset, const char __user *data, int size); + +#endif /* __SOUND_TRIDENT_H */ diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index b4430c093bad..94011dcae731 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -41,7 +41,7 @@ #include #include #include -#include +#include "trident.h" #include #include diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c index f9779e23fe57..3102a579660b 100644 --- a/sound/pci/trident/trident_memory.c +++ b/sound/pci/trident/trident_memory.c @@ -29,7 +29,7 @@ #include #include -#include +#include "trident.h" /* page arguments of these two macros are Trident page (4096 bytes), not like * aligned pages in others diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 7e20ddb9123a..4810356b97ba 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include "ymfpci.h" #include #include #include diff --git a/sound/pci/ymfpci/ymfpci.h b/sound/pci/ymfpci/ymfpci.h new file mode 100644 index 000000000000..bddc4052286b --- /dev/null +++ b/sound/pci/ymfpci/ymfpci.h @@ -0,0 +1,389 @@ +#ifndef __SOUND_YMFPCI_H +#define __SOUND_YMFPCI_H + +/* + * Copyright (c) by Jaroslav Kysela + * Definitions for Yahama YMF724/740/744/754 chips + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +/* + * Direct registers + */ + +#define YMFREG(chip, reg) (chip->port + YDSXGR_##reg) + +#define YDSXGR_INTFLAG 0x0004 +#define YDSXGR_ACTIVITY 0x0006 +#define YDSXGR_GLOBALCTRL 0x0008 +#define YDSXGR_ZVCTRL 0x000A +#define YDSXGR_TIMERCTRL 0x0010 +#define YDSXGR_TIMERCOUNT 0x0012 +#define YDSXGR_SPDIFOUTCTRL 0x0018 +#define YDSXGR_SPDIFOUTSTATUS 0x001C +#define YDSXGR_EEPROMCTRL 0x0020 +#define YDSXGR_SPDIFINCTRL 0x0034 +#define YDSXGR_SPDIFINSTATUS 0x0038 +#define YDSXGR_DSPPROGRAMDL 0x0048 +#define YDSXGR_DLCNTRL 0x004C +#define YDSXGR_GPIOININTFLAG 0x0050 +#define YDSXGR_GPIOININTENABLE 0x0052 +#define YDSXGR_GPIOINSTATUS 0x0054 +#define YDSXGR_GPIOOUTCTRL 0x0056 +#define YDSXGR_GPIOFUNCENABLE 0x0058 +#define YDSXGR_GPIOTYPECONFIG 0x005A +#define YDSXGR_AC97CMDDATA 0x0060 +#define YDSXGR_AC97CMDADR 0x0062 +#define YDSXGR_PRISTATUSDATA 0x0064 +#define YDSXGR_PRISTATUSADR 0x0066 +#define YDSXGR_SECSTATUSDATA 0x0068 +#define YDSXGR_SECSTATUSADR 0x006A +#define YDSXGR_SECCONFIG 0x0070 +#define YDSXGR_LEGACYOUTVOL 0x0080 +#define YDSXGR_LEGACYOUTVOLL 0x0080 +#define YDSXGR_LEGACYOUTVOLR 0x0082 +#define YDSXGR_NATIVEDACOUTVOL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLL 0x0084 +#define YDSXGR_NATIVEDACOUTVOLR 0x0086 +#define YDSXGR_ZVOUTVOL 0x0088 +#define YDSXGR_ZVOUTVOLL 0x0088 +#define YDSXGR_ZVOUTVOLR 0x008A +#define YDSXGR_SECADCOUTVOL 0x008C +#define YDSXGR_SECADCOUTVOLL 0x008C +#define YDSXGR_SECADCOUTVOLR 0x008E +#define YDSXGR_PRIADCOUTVOL 0x0090 +#define YDSXGR_PRIADCOUTVOLL 0x0090 +#define YDSXGR_PRIADCOUTVOLR 0x0092 +#define YDSXGR_LEGACYLOOPVOL 0x0094 +#define YDSXGR_LEGACYLOOPVOLL 0x0094 +#define YDSXGR_LEGACYLOOPVOLR 0x0096 +#define YDSXGR_NATIVEDACLOOPVOL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLL 0x0098 +#define YDSXGR_NATIVEDACLOOPVOLR 0x009A +#define YDSXGR_ZVLOOPVOL 0x009C +#define YDSXGR_ZVLOOPVOLL 0x009E +#define YDSXGR_ZVLOOPVOLR 0x009E +#define YDSXGR_SECADCLOOPVOL 0x00A0 +#define YDSXGR_SECADCLOOPVOLL 0x00A0 +#define YDSXGR_SECADCLOOPVOLR 0x00A2 +#define YDSXGR_PRIADCLOOPVOL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLL 0x00A4 +#define YDSXGR_PRIADCLOOPVOLR 0x00A6 +#define YDSXGR_NATIVEADCINVOL 0x00A8 +#define YDSXGR_NATIVEADCINVOLL 0x00A8 +#define YDSXGR_NATIVEADCINVOLR 0x00AA +#define YDSXGR_NATIVEDACINVOL 0x00AC +#define YDSXGR_NATIVEDACINVOLL 0x00AC +#define YDSXGR_NATIVEDACINVOLR 0x00AE +#define YDSXGR_BUF441OUTVOL 0x00B0 +#define YDSXGR_BUF441OUTVOLL 0x00B0 +#define YDSXGR_BUF441OUTVOLR 0x00B2 +#define YDSXGR_BUF441LOOPVOL 0x00B4 +#define YDSXGR_BUF441LOOPVOLL 0x00B4 +#define YDSXGR_BUF441LOOPVOLR 0x00B6 +#define YDSXGR_SPDIFOUTVOL 0x00B8 +#define YDSXGR_SPDIFOUTVOLL 0x00B8 +#define YDSXGR_SPDIFOUTVOLR 0x00BA +#define YDSXGR_SPDIFLOOPVOL 0x00BC +#define YDSXGR_SPDIFLOOPVOLL 0x00BC +#define YDSXGR_SPDIFLOOPVOLR 0x00BE +#define YDSXGR_ADCSLOTSR 0x00C0 +#define YDSXGR_RECSLOTSR 0x00C4 +#define YDSXGR_ADCFORMAT 0x00C8 +#define YDSXGR_RECFORMAT 0x00CC +#define YDSXGR_P44SLOTSR 0x00D0 +#define YDSXGR_STATUS 0x0100 +#define YDSXGR_CTRLSELECT 0x0104 +#define YDSXGR_MODE 0x0108 +#define YDSXGR_SAMPLECOUNT 0x010C +#define YDSXGR_NUMOFSAMPLES 0x0110 +#define YDSXGR_CONFIG 0x0114 +#define YDSXGR_PLAYCTRLSIZE 0x0140 +#define YDSXGR_RECCTRLSIZE 0x0144 +#define YDSXGR_EFFCTRLSIZE 0x0148 +#define YDSXGR_WORKSIZE 0x014C +#define YDSXGR_MAPOFREC 0x0150 +#define YDSXGR_MAPOFEFFECT 0x0154 +#define YDSXGR_PLAYCTRLBASE 0x0158 +#define YDSXGR_RECCTRLBASE 0x015C +#define YDSXGR_EFFCTRLBASE 0x0160 +#define YDSXGR_WORKBASE 0x0164 +#define YDSXGR_DSPINSTRAM 0x1000 +#define YDSXGR_CTRLINSTRAM 0x4000 + +#define YDSXG_AC97READCMD 0x8000 +#define YDSXG_AC97WRITECMD 0x0000 + +#define PCIR_DSXG_LEGACY 0x40 +#define PCIR_DSXG_ELEGACY 0x42 +#define PCIR_DSXG_CTRL 0x48 +#define PCIR_DSXG_PWRCTRL1 0x4a +#define PCIR_DSXG_PWRCTRL2 0x4e +#define PCIR_DSXG_FMBASE 0x60 +#define PCIR_DSXG_SBBASE 0x62 +#define PCIR_DSXG_MPU401BASE 0x64 +#define PCIR_DSXG_JOYBASE 0x66 + +#define YDSXG_DSPLENGTH 0x0080 +#define YDSXG_CTRLLENGTH 0x3000 + +#define YDSXG_DEFAULT_WORK_SIZE 0x0400 + +#define YDSXG_PLAYBACK_VOICES 64 +#define YDSXG_CAPTURE_VOICES 2 +#define YDSXG_EFFECT_VOICES 5 + +#define YMFPCI_LEGACY_SBEN (1 << 0) /* soundblaster enable */ +#define YMFPCI_LEGACY_FMEN (1 << 1) /* OPL3 enable */ +#define YMFPCI_LEGACY_JPEN (1 << 2) /* joystick enable */ +#define YMFPCI_LEGACY_MEN (1 << 3) /* MPU401 enable */ +#define YMFPCI_LEGACY_MIEN (1 << 4) /* MPU RX irq enable */ +#define YMFPCI_LEGACY_IOBITS (1 << 5) /* i/o bits range, 0 = 16bit, 1 =10bit */ +#define YMFPCI_LEGACY_SDMA (3 << 6) /* SB DMA select */ +#define YMFPCI_LEGACY_SBIRQ (7 << 8) /* SB IRQ select */ +#define YMFPCI_LEGACY_MPUIRQ (7 << 11) /* MPU IRQ select */ +#define YMFPCI_LEGACY_SIEN (1 << 14) /* serialized IRQ */ +#define YMFPCI_LEGACY_LAD (1 << 15) /* legacy audio disable */ + +#define YMFPCI_LEGACY2_FMIO (3 << 0) /* OPL3 i/o address (724/740) */ +#define YMFPCI_LEGACY2_SBIO (3 << 2) /* SB i/o address (724/740) */ +#define YMFPCI_LEGACY2_MPUIO (3 << 4) /* MPU401 i/o address (724/740) */ +#define YMFPCI_LEGACY2_JSIO (3 << 6) /* joystick i/o address (724/740) */ +#define YMFPCI_LEGACY2_MAIM (1 << 8) /* MPU401 ack intr mask */ +#define YMFPCI_LEGACY2_SMOD (3 << 11) /* SB DMA mode */ +#define YMFPCI_LEGACY2_SBVER (3 << 13) /* SB version select */ +#define YMFPCI_LEGACY2_IMOD (1 << 15) /* legacy IRQ mode */ +/* SIEN:IMOD 0:0 = legacy irq, 0:1 = INTA, 1:0 = serialized IRQ */ + +#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#define SUPPORT_JOYSTICK +#endif + +/* + * + */ + +struct snd_ymfpci_playback_bank { + u32 format; + u32 loop_default; + u32 base; /* 32-bit address */ + u32 loop_start; /* 32-bit offset */ + u32 loop_end; /* 32-bit offset */ + u32 loop_frac; /* 8-bit fraction - loop_start */ + u32 delta_end; /* pitch delta end */ + u32 lpfK_end; + u32 eg_gain_end; + u32 left_gain_end; + u32 right_gain_end; + u32 eff1_gain_end; + u32 eff2_gain_end; + u32 eff3_gain_end; + u32 lpfQ; + u32 status; + u32 num_of_frames; + u32 loop_count; + u32 start; + u32 start_frac; + u32 delta; + u32 lpfK; + u32 eg_gain; + u32 left_gain; + u32 right_gain; + u32 eff1_gain; + u32 eff2_gain; + u32 eff3_gain; + u32 lpfD1; + u32 lpfD2; + }; + +struct snd_ymfpci_capture_bank { + u32 base; /* 32-bit address */ + u32 loop_end; /* 32-bit offset */ + u32 start; /* 32-bit offset */ + u32 num_of_loops; /* counter */ +}; + +struct snd_ymfpci_effect_bank { + u32 base; /* 32-bit address */ + u32 loop_end; /* 32-bit offset */ + u32 start; /* 32-bit offset */ + u32 temp; +}; + +struct snd_ymfpci_pcm; +struct snd_ymfpci; + +enum snd_ymfpci_voice_type { + YMFPCI_PCM, + YMFPCI_SYNTH, + YMFPCI_MIDI +}; + +struct snd_ymfpci_voice { + struct snd_ymfpci *chip; + int number; + unsigned int use: 1, + pcm: 1, + synth: 1, + midi: 1; + struct snd_ymfpci_playback_bank *bank; + dma_addr_t bank_addr; + void (*interrupt)(struct snd_ymfpci *chip, struct snd_ymfpci_voice *voice); + struct snd_ymfpci_pcm *ypcm; +}; + +enum snd_ymfpci_pcm_type { + PLAYBACK_VOICE, + CAPTURE_REC, + CAPTURE_AC97, + EFFECT_DRY_LEFT, + EFFECT_DRY_RIGHT, + EFFECT_EFF1, + EFFECT_EFF2, + EFFECT_EFF3 +}; + +struct snd_ymfpci_pcm { + struct snd_ymfpci *chip; + enum snd_ymfpci_pcm_type type; + struct snd_pcm_substream *substream; + struct snd_ymfpci_voice *voices[2]; /* playback only */ + unsigned int running: 1, + use_441_slot: 1, + output_front: 1, + output_rear: 1, + swap_rear: 1; + unsigned int update_pcm_vol; + u32 period_size; /* cached from runtime->period_size */ + u32 buffer_size; /* cached from runtime->buffer_size */ + u32 period_pos; + u32 last_pos; + u32 capture_bank_number; + u32 shift; +}; + +struct snd_ymfpci { + int irq; + + unsigned int device_id; /* PCI device ID */ + unsigned char rev; /* PCI revision */ + unsigned long reg_area_phys; + void __iomem *reg_area_virt; + struct resource *res_reg_area; + struct resource *fm_res; + struct resource *mpu_res; + + unsigned short old_legacy_ctrl; +#ifdef SUPPORT_JOYSTICK + struct gameport *gameport; +#endif + + struct snd_dma_buffer work_ptr; + + unsigned int bank_size_playback; + unsigned int bank_size_capture; + unsigned int bank_size_effect; + unsigned int work_size; + + void *bank_base_playback; + void *bank_base_capture; + void *bank_base_effect; + void *work_base; + dma_addr_t bank_base_playback_addr; + dma_addr_t bank_base_capture_addr; + dma_addr_t bank_base_effect_addr; + dma_addr_t work_base_addr; + struct snd_dma_buffer ac3_tmp_base; + + u32 *ctrl_playback; + struct snd_ymfpci_playback_bank *bank_playback[YDSXG_PLAYBACK_VOICES][2]; + struct snd_ymfpci_capture_bank *bank_capture[YDSXG_CAPTURE_VOICES][2]; + struct snd_ymfpci_effect_bank *bank_effect[YDSXG_EFFECT_VOICES][2]; + + int start_count; + + u32 active_bank; + struct snd_ymfpci_voice voices[64]; + int src441_used; + + struct snd_ac97_bus *ac97_bus; + struct snd_ac97 *ac97; + struct snd_rawmidi *rawmidi; + struct snd_timer *timer; + unsigned int timer_ticks; + + struct pci_dev *pci; + struct snd_card *card; + struct snd_pcm *pcm; + struct snd_pcm *pcm2; + struct snd_pcm *pcm_spdif; + struct snd_pcm *pcm_4ch; + struct snd_pcm_substream *capture_substream[YDSXG_CAPTURE_VOICES]; + struct snd_pcm_substream *effect_substream[YDSXG_EFFECT_VOICES]; + struct snd_kcontrol *ctl_vol_recsrc; + struct snd_kcontrol *ctl_vol_adcrec; + struct snd_kcontrol *ctl_vol_spdifrec; + unsigned short spdif_bits, spdif_pcm_bits; + struct snd_kcontrol *spdif_pcm_ctl; + int mode_dup4ch; + int rear_opened; + int spdif_opened; + struct snd_ymfpci_pcm_mixer { + u16 left; + u16 right; + struct snd_kcontrol *ctl; + } pcm_mixer[32]; + + spinlock_t reg_lock; + spinlock_t voice_lock; + wait_queue_head_t interrupt_sleep; + atomic_t interrupt_sleep_count; + struct snd_info_entry *proc_entry; + const struct firmware *dsp_microcode; + const struct firmware *controller_microcode; + +#ifdef CONFIG_PM + u32 *saved_regs; + u32 saved_ydsxgr_mode; + u16 saved_dsxg_legacy; + u16 saved_dsxg_elegacy; +#endif +}; + +int snd_ymfpci_create(struct snd_card *card, + struct pci_dev *pci, + unsigned short old_legacy_ctrl, + struct snd_ymfpci ** rcodec); +void snd_ymfpci_free_gameport(struct snd_ymfpci *chip); + +extern const struct dev_pm_ops snd_ymfpci_pm; + +int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); +int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); +int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); +int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, struct snd_pcm **rpcm); +int snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch); +int snd_ymfpci_timer(struct snd_ymfpci *chip, int device); + +#endif /* __SOUND_YMFPCI_H */ diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index c706901d6ff6..62b23635b754 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include "ymfpci.h" #include #include -- cgit v1.2.3 From e3e1a0bcb3f192fe2f95f86a74bd4e7967341e74 Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Mon, 2 Jul 2012 19:46:16 +0200 Subject: mac80211: reduce IEEE80211_TX_MAX_RATES IEEE80211_TX_MAX_RATES can be reduced from 5 to 4 as there is no current hardware supporting a rate chain with 5 multi rate stages (mrr), so 4 mrr stages are sufficient. The memory that is freed within the ieee80211_tx_info struct will be used in the upcoming Transmission Power Control (TPC) implementation. Suggested-by: Felix Fietkau Signed-off-by: Thomas Huehn [reword commit message] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/carl9170/tx.c | 6 +++--- drivers/net/wireless/p54/txrx.c | 6 +++--- include/net/mac80211.h | 8 ++++---- net/mac80211/tx.c | 3 +-- 4 files changed, 11 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index aed305177af6..ede0b572cebc 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -277,11 +277,11 @@ static void carl9170_tx_release(struct kref *ref) return; BUILD_BUG_ON( - offsetof(struct ieee80211_tx_info, status.ampdu_ack_len) != 23); + offsetof(struct ieee80211_tx_info, status.ack_signal) != 20); - memset(&txinfo->status.ampdu_ack_len, 0, + memset(&txinfo->status.ack_signal, 0, sizeof(struct ieee80211_tx_info) - - offsetof(struct ieee80211_tx_info, status.ampdu_ack_len)); + offsetof(struct ieee80211_tx_info, status.ack_signal)); if (atomic_read(&ar->tx_total_queued)) ar->tx_schedule = true; diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 82a1cac920bd..f38786e02623 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -422,11 +422,11 @@ static void p54_rx_frame_sent(struct p54_common *priv, struct sk_buff *skb) * Clear manually, ieee80211_tx_info_clear_status would * clear the counts too and we need them. */ - memset(&info->status.ampdu_ack_len, 0, + memset(&info->status.ack_signal, 0, sizeof(struct ieee80211_tx_info) - - offsetof(struct ieee80211_tx_info, status.ampdu_ack_len)); + offsetof(struct ieee80211_tx_info, status.ack_signal)); BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, - status.ampdu_ack_len) != 23); + status.ack_signal) != 20); if (entry_hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) pad = entry_data->align[0]; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index dc2a97af95e7..3f1b58cf9c8c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -475,7 +475,7 @@ enum mac80211_rate_control_flags { #define IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE 24 /* maximum number of rate stages */ -#define IEEE80211_TX_MAX_RATES 5 +#define IEEE80211_TX_MAX_RATES 4 /** * struct ieee80211_tx_rate - rate selection/status @@ -563,11 +563,11 @@ struct ieee80211_tx_info { } control; struct { struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES]; - u8 ampdu_ack_len; int ack_signal; + u8 ampdu_ack_len; u8 ampdu_len; u8 antenna; - /* 14 bytes free */ + /* 21 bytes free */ } status; struct { struct ieee80211_tx_rate driver_rates[ @@ -634,7 +634,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) info->status.rates[i].count = 0; BUILD_BUG_ON( - offsetof(struct ieee80211_tx_info, status.ampdu_ack_len) != 23); + offsetof(struct ieee80211_tx_info, status.ack_signal) != 20); memset(&info->status.ampdu_ack_len, 0, sizeof(struct ieee80211_tx_info) - offsetof(struct ieee80211_tx_info, status.ampdu_ack_len)); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 364a1e7b4afa..c9d2175d15c1 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -959,8 +959,7 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) info->control.rates[1].idx = -1; info->control.rates[2].idx = -1; info->control.rates[3].idx = -1; - info->control.rates[4].idx = -1; - BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 5); + BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 4); info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE; } else { hdr->frame_control &= ~morefrags; -- cgit v1.2.3 From a1845fc7c552977e23fe552ad3f5c6c279e3d550 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 27 Jun 2012 13:18:36 +0200 Subject: mac80211: add TX prepare API Some drivers require setup before being able to send management frames in managed mode, in particular in multi-channel cases. Introduce API to allow the drivers to do such setup while being able to sleep waiting for the setup to finish in the device. This isn't possible inside the TX call since that can't sleep. A future patch may also restructure the TX retry to wait for the driver to report the frame status, as suggested by Arik in http://mid.gmane.org/CA+XVXffKSEL6ZQPQ98x-zO-NL2=TNF1uN==mprRyUmAaRn254g@mail.gmail.com Signed-off-by: Johannes Berg --- include/net/mac80211.h | 15 +++++++++++++++ net/mac80211/driver-ops.h | 14 ++++++++++++++ net/mac80211/mlme.c | 8 ++++++++ net/mac80211/trace.h | 7 +++++++ 4 files changed, 44 insertions(+) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3f1b58cf9c8c..e3fa90ce9ecb 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2244,6 +2244,18 @@ enum ieee80211_rate_control_changed { * @get_rssi: Get current signal strength in dBm, the function is optional * and can sleep. * + * @mgd_prepare_tx: Prepare for transmitting a management frame for association + * before associated. In multi-channel scenarios, a virtual interface is + * bound to a channel before it is associated, but as it isn't associated + * yet it need not necessarily be given airtime, in particular since any + * transmission to a P2P GO needs to be synchronized against the GO's + * powersave state. mac80211 will call this function before transmitting a + * management frame prior to having successfully associated to allow the + * driver to give it channel time for the transmission, to get a response + * and to be able to synchronize with the GO. + * The callback will be called before each transmission and upon return + * mac80211 will transmit the frame right away. + * The callback is optional and can (should!) sleep. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); @@ -2383,6 +2395,9 @@ struct ieee80211_ops { u32 sset, u8 *data); int (*get_rssi)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, s8 *rssi_dbm); + + void (*mgd_prepare_tx)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); }; /** diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 5042151a3325..df9203199102 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -852,4 +852,18 @@ static inline int drv_get_rssi(struct ieee80211_local *local, return ret; } + +static inline void drv_mgd_prepare_tx(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + might_sleep(); + + check_sdata_in_driver(sdata); + WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION); + + trace_drv_mgd_prepare_tx(local, sdata); + if (local->ops->mgd_prepare_tx) + local->ops->mgd_prepare_tx(&local->hw, &sdata->vif); + trace_drv_return_void(local); +} #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e9c0d1b68fc8..d563f7c55531 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -541,6 +541,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) memcpy(pos, assoc_data->ie + offset, noffset - offset); } + drv_mgd_prepare_tx(local, sdata); + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; ieee80211_tx_skb(sdata, skb); } @@ -580,6 +582,9 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED)) IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + + drv_mgd_prepare_tx(local, sdata); + ieee80211_tx_skb(sdata, skb); } } @@ -1756,6 +1761,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, if (!elems.challenge) return; auth_data->expected_transaction = 4; + drv_mgd_prepare_tx(sdata->local, sdata); ieee80211_send_auth(sdata, 3, auth_data->algorithm, elems.challenge - 2, elems.challenge_len + 2, auth_data->bss->bssid, auth_data->bss->bssid, @@ -2641,6 +2647,8 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) return -ETIMEDOUT; } + drv_mgd_prepare_tx(local, sdata); + if (auth_data->bss->proberesp_ies) { sdata_info(sdata, "send auth to %pM (try %d/%d)\n", auth_data->bss->bssid, auth_data->tries, diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 2e60f4acd027..e1e9d10ec2e7 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1244,6 +1244,13 @@ TRACE_EVENT(drv_get_rssi, ) ); +DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + + TP_ARGS(local, sdata) +); + /* * Tracing for API calls that drivers call. */ -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 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') 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 From 08911475d1d0921401e37d83292b217e1411d10b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 29 Jun 2012 05:23:24 +0000 Subject: netfilter: nf_conntrack: generalize nf_ct_l4proto_net This patch generalizes nf_ct_l4proto_net by splitting it into chunks and moving the corresponding protocol part to where it really belongs to. To clarify, note that we follow two different approaches to support per-net depending if it's built-in or run-time loadable protocol tracker. Signed-off-by: Pablo Neira Ayuso Acked-by: Gao feng --- include/net/netfilter/nf_conntrack_l4proto.h | 3 +++ net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 6 ++++++ net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 6 ++++++ net/netfilter/nf_conntrack_proto.c | 22 ++++++---------------- net/netfilter/nf_conntrack_proto_generic.c | 6 ++++++ net/netfilter/nf_conntrack_proto_tcp.c | 7 +++++++ net/netfilter/nf_conntrack_proto_udp.c | 7 +++++++ 7 files changed, 41 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 08bb571b7abd..c3be4aef6bf7 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -99,6 +99,9 @@ struct nf_conntrack_l4proto { /* Init l4proto pernet data */ int (*init_net)(struct net *net, u_int16_t proto); + /* Return the per-net protocol part. */ + struct nf_proto_net *(*get_net_proto)(struct net *net); + /* Protocol name */ const char *name; diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index 9c2095c5571f..5241d997ab75 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -388,6 +388,11 @@ static int icmp_init_net(struct net *net, u_int16_t proto) return ret; } +static struct nf_proto_net *icmp_get_net_proto(struct net *net) +{ + return &net->ct.nf_ct_proto.icmp.pn; +} + struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly = { .l3proto = PF_INET, @@ -418,4 +423,5 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly = }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ .init_net = icmp_init_net, + .get_net_proto = icmp_get_net_proto, }; diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index 9fc5cf5f3e8b..2d54b2061d68 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -358,6 +358,11 @@ static int icmpv6_init_net(struct net *net, u_int16_t proto) return icmpv6_kmemdup_sysctl_table(pn, in); } +static struct nf_proto_net *icmpv6_get_net_proto(struct net *net) +{ + return &net->ct.nf_ct_proto.icmpv6.pn; +} + struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 __read_mostly = { .l3proto = PF_INET6, @@ -386,4 +391,5 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 __read_mostly = }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ .init_net = icmpv6_init_net, + .get_net_proto = icmpv6_get_net_proto, }; diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 21b850c4b3ab..0dc63854390f 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -303,22 +303,12 @@ EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister); static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, struct nf_conntrack_l4proto *l4proto) { - switch (l4proto->l4proto) { - case IPPROTO_TCP: - return (struct nf_proto_net *)&net->ct.nf_ct_proto.tcp; - case IPPROTO_UDP: - return (struct nf_proto_net *)&net->ct.nf_ct_proto.udp; - case IPPROTO_ICMP: - return (struct nf_proto_net *)&net->ct.nf_ct_proto.icmp; - case IPPROTO_ICMPV6: - return (struct nf_proto_net *)&net->ct.nf_ct_proto.icmpv6; - case 255: /* l4proto_generic */ - return (struct nf_proto_net *)&net->ct.nf_ct_proto.generic; - default: - if (l4proto->net_id) - return net_generic(net, *l4proto->net_id); - else - return NULL; + if (l4proto->get_net_proto) { + /* statically built-in protocols use static per-net */ + return l4proto->get_net_proto(net); + } else if (l4proto->net_id) { + /* ... and loadable protocols use dynamic per-net */ + return net_generic(net, *l4proto->net_id); } return NULL; } diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index 7c11c5444194..d25f29377648 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -186,6 +186,11 @@ static int generic_init_net(struct net *net, u_int16_t proto) return ret; } +static struct nf_proto_net *generic_get_net_proto(struct net *net) +{ + return &net->ct.nf_ct_proto.generic.pn; +} + struct nf_conntrack_l4proto nf_conntrack_l4proto_generic __read_mostly = { .l3proto = PF_UNSPEC, @@ -207,4 +212,5 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_generic __read_mostly = }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ .init_net = generic_init_net, + .get_net_proto = generic_get_net_proto, }; diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 44f0da830156..07e56ea2e9bf 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -1623,6 +1623,11 @@ static int tcp_init_net(struct net *net, u_int16_t proto) return ret; } +static struct nf_proto_net *tcp_get_net_proto(struct net *net) +{ + return &net->ct.nf_ct_proto.tcp.pn; +} + struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly = { .l3proto = PF_INET, @@ -1656,6 +1661,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly = }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ .init_net = tcp_init_net, + .get_net_proto = tcp_get_net_proto, }; EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_tcp4); @@ -1692,5 +1698,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 __read_mostly = }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ .init_net = tcp_init_net, + .get_net_proto = tcp_get_net_proto, }; EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_tcp6); diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index e7e0434c3056..59623cc56e8d 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -297,6 +297,11 @@ static int udp_init_net(struct net *net, u_int16_t proto) return ret; } +static struct nf_proto_net *udp_get_net_proto(struct net *net) +{ + return &net->ct.nf_ct_proto.udp.pn; +} + struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly = { .l3proto = PF_INET, @@ -325,6 +330,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly = }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ .init_net = udp_init_net, + .get_net_proto = udp_get_net_proto, }; EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udp4); @@ -356,5 +362,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6 __read_mostly = }, #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ .init_net = udp_init_net, + .get_net_proto = udp_get_net_proto, }; EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udp6); -- cgit v1.2.3 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') 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 From cab7faca5e446b84e829d57d2095035d72edba09 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 27 Jun 2012 09:53:47 +0200 Subject: Input: MT - Include win8 support The newly released HID protocol for win8 multitouch devices is capable of transmitting more information about each touch. In particular, it includes details useful for touch alignment. This patch completes the MT protocol with the ABS_MT_TOOL_X/Y events, and documents how to map win8 devices. Cc: Stephane Chatty Cc: Benjamin Tissoires Cc: Peter Hutterer Acked-by: Chase Douglas Signed-off-by: Henrik Rydberg --- Documentation/input/multi-touch-protocol.txt | 118 +++++++++++++++++++++------ include/linux/input.h | 8 +- 2 files changed, 97 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/Documentation/input/multi-touch-protocol.txt b/Documentation/input/multi-touch-protocol.txt index 543101c5bf26..2c179613f81b 100644 --- a/Documentation/input/multi-touch-protocol.txt +++ b/Documentation/input/multi-touch-protocol.txt @@ -162,26 +162,48 @@ are divided into categories, to allow for partial implementation. The minimum set consists of ABS_MT_POSITION_X and ABS_MT_POSITION_Y, which allows for multiple contacts to be tracked. If the device supports it, the ABS_MT_TOUCH_MAJOR and ABS_MT_WIDTH_MAJOR may be used to provide the size -of the contact area and approaching contact, respectively. +of the contact area and approaching tool, respectively. The TOUCH and WIDTH parameters have a geometrical interpretation; imagine looking through a window at someone gently holding a finger against the glass. You will see two regions, one inner region consisting of the part of the finger actually touching the glass, and one outer region formed by -the perimeter of the finger. The diameter of the inner region is the -ABS_MT_TOUCH_MAJOR, the diameter of the outer region is -ABS_MT_WIDTH_MAJOR. Now imagine the person pressing the finger harder -against the glass. The inner region will increase, and in general, the -ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR, which is always smaller than -unity, is related to the contact pressure. For pressure-based devices, +the perimeter of the finger. The center of the touching region (a) is +ABS_MT_POSITION_X/Y and the center of the approaching finger (b) is +ABS_MT_TOOL_X/Y. The touch diameter is ABS_MT_TOUCH_MAJOR and the finger +diameter is ABS_MT_WIDTH_MAJOR. Now imagine the person pressing the finger +harder against the glass. The touch region will increase, and in general, +the ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR, which is always smaller +than unity, is related to the contact pressure. For pressure-based devices, ABS_MT_PRESSURE may be used to provide the pressure on the contact area instead. Devices capable of contact hovering can use ABS_MT_DISTANCE to indicate the distance between the contact and the surface. -In addition to the MAJOR parameters, the oval shape of the contact can be -described by adding the MINOR parameters, such that MAJOR and MINOR are the -major and minor axis of an ellipse. Finally, the orientation of the oval -shape can be describe with the ORIENTATION parameter. + + Linux MT Win8 + __________ _______________________ + / \ | | + / \ | | + / ____ \ | | + / / \ \ | | + \ \ a \ \ | a | + \ \____/ \ | | + \ \ | | + \ b \ | b | + \ \ | | + \ \ | | + \ \ | | + \ / | | + \ / | | + \ / | | + \__________/ |_______________________| + + +In addition to the MAJOR parameters, the oval shape of the touch and finger +regions can be described by adding the MINOR parameters, such that MAJOR +and MINOR are the major and minor axis of an ellipse. The orientation of +the touch ellipse can be described with the ORIENTATION parameter, and the +direction of the finger ellipse is given by the vector (a - b). For type A devices, further specification of the touch shape is possible via ABS_MT_BLOB_ID. @@ -224,7 +246,7 @@ tool. Omit if circular [4]. The above four values can be used to derive additional information about the contact. The ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR approximates the notion of pressure. The fingers of the hand and the palm all have -different characteristic widths [1]. +different characteristic widths. ABS_MT_PRESSURE @@ -240,17 +262,24 @@ the contact is hovering above the surface. ABS_MT_ORIENTATION -The orientation of the ellipse. The value should describe a signed quarter -of a revolution clockwise around the touch center. The signed value range -is arbitrary, but zero should be returned for a finger aligned along the Y -axis of the surface, a negative value when finger is turned to the left, and -a positive value when finger turned to the right. When completely aligned with -the X axis, the range max should be returned. Orientation can be omitted -if the touching object is circular, or if the information is not available -in the kernel driver. Partial orientation support is possible if the device -can distinguish between the two axis, but not (uniquely) any values in -between. In such cases, the range of ABS_MT_ORIENTATION should be [0, 1] -[4]. +The orientation of the touching ellipse. The value should describe a signed +quarter of a revolution clockwise around the touch center. The signed value +range is arbitrary, but zero should be returned for an ellipse aligned with +the Y axis of the surface, a negative value when the ellipse is turned to +the left, and a positive value when the ellipse is turned to the +right. When completely aligned with the X axis, the range max should be +returned. + +Touch ellipsis are symmetrical by default. For devices capable of true 360 +degree orientation, the reported orientation must exceed the range max to +indicate more than a quarter of a revolution. For an upside-down finger, +range max * 2 should be returned. + +Orientation can be omitted if the touch area is circular, or if the +information is not available in the kernel driver. Partial orientation +support is possible if the device can distinguish between the two axis, but +not (uniquely) any values in between. In such cases, the range of +ABS_MT_ORIENTATION should be [0, 1] [4]. ABS_MT_POSITION_X @@ -260,6 +289,23 @@ ABS_MT_POSITION_Y The surface Y coordinate of the center of the touching ellipse. +ABS_MT_TOOL_X + +The surface X coordinate of the center of the approaching tool. Omit if +the device cannot distinguish between the intended touch point and the +tool itself. + +ABS_MT_TOOL_Y + +The surface Y coordinate of the center of the approaching tool. Omit if the +device cannot distinguish between the intended touch point and the tool +itself. + +The four position values can be used to separate the position of the touch +from the position of the tool. If both positions are present, the major +tool axis points towards the touch point [1]. Otherwise, the tool axes are +aligned with the touch axes. + ABS_MT_TOOL_TYPE The type of approaching tool. A lot of kernel drivers cannot distinguish @@ -305,6 +351,28 @@ The range of ABS_MT_ORIENTATION should be set to [0, 1], to indicate that the device can distinguish between a finger along the Y axis (0) and a finger along the X axis (1). +For win8 devices with both T and C coordinates, the position mapping is + + ABS_MT_POSITION_X := T_X + ABS_MT_POSITION_Y := T_Y + ABS_MT_TOOL_X := C_X + ABS_MT_TOOL_X := C_Y + +Unfortunately, there is not enough information to specify both the touching +ellipse and the tool ellipse, so one has to resort to approximations. One +simple scheme, which is compatible with earlier usage, is: + + ABS_MT_TOUCH_MAJOR := min(X, Y) + ABS_MT_TOUCH_MINOR := + ABS_MT_ORIENTATION := + ABS_MT_WIDTH_MAJOR := min(X, Y) + distance(T, C) + ABS_MT_WIDTH_MINOR := min(X, Y) + +Rationale: We have no information about the orientation of the touching +ellipse, so approximate it with an inscribed circle instead. The tool +ellipse should align with the the vector (T - C), so the diameter must +increase with distance(T, C). Finally, assume that the touch diameter is +equal to the tool thickness, and we arrive at the formulas above. Finger Tracking --------------- @@ -338,9 +406,7 @@ subsequent events of the same type refer to different fingers. For example usage of the type A protocol, see the bcm5974 driver. For example usage of the type B protocol, see the hid-egalax driver. -[1] With the extension ABS_MT_APPROACH_X and ABS_MT_APPROACH_Y, the -difference between the contact position and the approaching tool position -could be used to derive tilt. +[1] Also, the difference (TOOL_X - POSITION_X) can be used to model tilt. [2] The list can of course be extended. [3] The mtdev project: http://bitmath.org/code/mtdev/. [4] See the section on event computation. diff --git a/include/linux/input.h b/include/linux/input.h index a81671453575..cacb95343a7e 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -806,18 +806,20 @@ struct input_keymap_entry { #define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ #define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ #define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ -#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */ -#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */ +#define ABS_MT_POSITION_X 0x35 /* Center X touch position */ +#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */ #define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */ #define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ #define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ #define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ #define ABS_MT_DISTANCE 0x3b /* Contact hover distance */ +#define ABS_MT_TOOL_X 0x3c /* Center X tool position */ +#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */ #ifdef __KERNEL__ /* Implementation details, userspace should not care about these */ #define ABS_MT_FIRST ABS_MT_TOUCH_MAJOR -#define ABS_MT_LAST ABS_MT_DISTANCE +#define ABS_MT_LAST ABS_MT_TOOL_Y #endif #define ABS_MAX 0x3f -- cgit v1.2.3 From a263b3093641fb1ec377582c90986a7fd0625184 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 2 Jul 2012 02:02:15 -0700 Subject: ipv4: Make neigh lookups directly in output packet path. Do not use the dst cached neigh, we'll be getting rid of that. Signed-off-by: David S. Miller --- include/net/arp.h | 28 +++++++++++++++++++--------- include/net/neighbour.h | 11 +++++++++-- net/core/neighbour.c | 12 +++++++----- net/ipv4/ip_output.c | 12 ++++++++---- net/ipv4/route.c | 6 +----- 5 files changed, 44 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/net/arp.h b/include/net/arp.h index 4a1f3fb562eb..4617d9841132 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -15,24 +15,34 @@ static inline u32 arp_hashfn(u32 key, const struct net_device *dev, u32 hash_rnd return val * hash_rnd; } -static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32 key) +static inline struct neighbour *__ipv4_neigh_lookup_noref(struct net_device *dev, u32 key) { - struct neigh_hash_table *nht; + struct neigh_hash_table *nht = rcu_dereference_bh(arp_tbl.nht); struct neighbour *n; u32 hash_val; - rcu_read_lock_bh(); - nht = rcu_dereference_bh(arp_tbl.nht); + if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) + key = 0; + hash_val = arp_hashfn(key, dev, nht->hash_rnd[0]) >> (32 - nht->hash_shift); for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); n != NULL; n = rcu_dereference_bh(n->next)) { - if (n->dev == dev && *(u32 *)n->primary_key == key) { - if (!atomic_inc_not_zero(&n->refcnt)) - n = NULL; - break; - } + if (n->dev == dev && *(u32 *)n->primary_key == key) + return n; } + + return NULL; +} + +static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32 key) +{ + struct neighbour *n; + + rcu_read_lock_bh(); + n = __ipv4_neigh_lookup_noref(dev, key); + if (n && !atomic_inc_not_zero(&n->refcnt)) + n = NULL; rcu_read_unlock_bh(); return n; diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 6cdfeedb650b..e1d18bdeebb8 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -202,9 +202,16 @@ extern struct neighbour * neigh_lookup(struct neigh_table *tbl, extern struct neighbour * neigh_lookup_nodev(struct neigh_table *tbl, struct net *net, const void *pkey); -extern struct neighbour * neigh_create(struct neigh_table *tbl, +extern struct neighbour * __neigh_create(struct neigh_table *tbl, + const void *pkey, + struct net_device *dev, + bool want_ref); +static inline struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey, - struct net_device *dev); + struct net_device *dev) +{ + return __neigh_create(tbl, pkey, dev, true); +} extern void neigh_destroy(struct neighbour *neigh); extern int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb); extern int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, diff --git a/net/core/neighbour.c b/net/core/neighbour.c index d81d026138f0..a793af9af150 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -474,8 +474,8 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net, } EXPORT_SYMBOL(neigh_lookup_nodev); -struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey, - struct net_device *dev) +struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey, + struct net_device *dev, bool want_ref) { u32 hash_val; int key_len = tbl->key_len; @@ -535,14 +535,16 @@ struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey, n1 = rcu_dereference_protected(n1->next, lockdep_is_held(&tbl->lock))) { if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) { - neigh_hold(n1); + if (want_ref) + neigh_hold(n1); rc = n1; goto out_tbl_unlock; } } n->dead = 0; - neigh_hold(n); + if (want_ref) + neigh_hold(n); rcu_assign_pointer(n->next, rcu_dereference_protected(nht->hash_buckets[hash_val], lockdep_is_held(&tbl->lock))); @@ -558,7 +560,7 @@ out_neigh_release: neigh_release(n); goto out; } -EXPORT_SYMBOL(neigh_create); +EXPORT_SYMBOL(__neigh_create); static u32 pneigh_hash(const void *pkey, int key_len) { diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 2630900e480a..6e9a266a0535 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -170,6 +170,7 @@ static inline int ip_finish_output2(struct sk_buff *skb) struct net_device *dev = dst->dev; unsigned int hh_len = LL_RESERVED_SPACE(dev); struct neighbour *neigh; + u32 nexthop; if (rt->rt_type == RTN_MULTICAST) { IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTMCAST, skb->len); @@ -191,15 +192,18 @@ static inline int ip_finish_output2(struct sk_buff *skb) skb = skb2; } - rcu_read_lock(); - neigh = dst_get_neighbour_noref(dst); + rcu_read_lock_bh(); + nexthop = rt->rt_gateway ? rt->rt_gateway : ip_hdr(skb)->daddr; + neigh = __ipv4_neigh_lookup_noref(dev, nexthop); + if (unlikely(!neigh)) + neigh = __neigh_create(&arp_tbl, &nexthop, dev, false); if (neigh) { int res = neigh_output(neigh, skb); - rcu_read_unlock(); + rcu_read_unlock_bh(); return res; } - rcu_read_unlock(); + rcu_read_unlock_bh(); net_dbg_ratelimited("%s: No header cache and no neighbour!\n", __func__); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 6a5afc715558..2f40363e2851 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1098,17 +1098,13 @@ static int slow_chain_length(const struct rtable *head) static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, const void *daddr) { - static const __be32 inaddr_any = 0; struct net_device *dev = dst->dev; const __be32 *pkey = daddr; const struct rtable *rt; struct neighbour *n; rt = (const struct rtable *) dst; - - if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) - pkey = &inaddr_any; - else if (rt->rt_gateway) + if (rt->rt_gateway) pkey = (const __be32 *) &rt->rt_gateway; n = __ipv4_neigh_lookup(dev, *(__force u32 *)pkey); -- cgit v1.2.3 From 5110effee8fde2edfacac9cd12a9960ab2dc39ea Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 2 Jul 2012 02:21:03 -0700 Subject: net: Do delayed neigh confirmation. When a dst_confirm() happens, mark the confirmation as pending in the dst. Then on the next packet out, when we have the neigh in-hand, do the update. This removes the dependency in dst_confirm() of dst's having an attached neigh. While we're here, remove the explicit 'dst' NULL check, all except 2 or 3 call sites ensure it's not NULL. So just fix those cases up. Signed-off-by: David S. Miller --- include/net/dst.h | 29 +++++++++++++++++++++-------- include/net/neighbour.h | 15 --------------- net/core/dst.c | 3 ++- net/ipv4/ip_output.c | 2 +- net/ipv4/tcp_input.c | 19 +++++++++++++------ net/ipv6/ip6_output.c | 2 +- 6 files changed, 38 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index f0bf3b8d5911..84e7a3ff968d 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -51,7 +51,7 @@ struct dst_entry { int (*input)(struct sk_buff *); int (*output)(struct sk_buff *); - int flags; + unsigned short flags; #define DST_HOST 0x0001 #define DST_NOXFRM 0x0002 #define DST_NOPOLICY 0x0004 @@ -62,6 +62,8 @@ struct dst_entry { #define DST_FAKE_RTABLE 0x0080 #define DST_XFRM_TUNNEL 0x0100 + unsigned short pending_confirm; + short error; short obsolete; unsigned short header_len; /* more space at head required */ @@ -371,7 +373,8 @@ static inline struct dst_entry *skb_dst_pop(struct sk_buff *skb) extern int dst_discard(struct sk_buff *skb); extern void *dst_alloc(struct dst_ops *ops, struct net_device *dev, - int initial_ref, int initial_obsolete, int flags); + int initial_ref, int initial_obsolete, + unsigned short flags); extern void __dst_free(struct dst_entry *dst); extern struct dst_entry *dst_destroy(struct dst_entry *dst); @@ -395,14 +398,24 @@ static inline void dst_rcu_free(struct rcu_head *head) static inline void dst_confirm(struct dst_entry *dst) { - if (dst) { - struct neighbour *n; + dst->pending_confirm = 1; +} - rcu_read_lock(); - n = dst_get_neighbour_noref(dst); - neigh_confirm(n); - rcu_read_unlock(); +static inline int dst_neigh_output(struct dst_entry *dst, struct neighbour *n, + struct sk_buff *skb) +{ + struct hh_cache *hh; + + if (unlikely(dst->pending_confirm)) { + n->confirmed = jiffies; + dst->pending_confirm = 0; } + + hh = &n->hh; + if ((n->nud_state & NUD_CONNECTED) && hh->hh_len) + return neigh_hh_output(hh, skb); + else + return n->output(n, skb); } static inline struct neighbour *dst_neigh_lookup(const struct dst_entry *dst, const void *daddr) diff --git a/include/net/neighbour.h b/include/net/neighbour.h index e1d18bdeebb8..344d8988842a 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -309,12 +309,6 @@ static inline struct neighbour * neigh_clone(struct neighbour *neigh) #define neigh_hold(n) atomic_inc(&(n)->refcnt) -static inline void neigh_confirm(struct neighbour *neigh) -{ - if (neigh) - neigh->confirmed = jiffies; -} - static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) { unsigned long now = jiffies; @@ -358,15 +352,6 @@ static inline int neigh_hh_output(struct hh_cache *hh, struct sk_buff *skb) return dev_queue_xmit(skb); } -static inline int neigh_output(struct neighbour *n, struct sk_buff *skb) -{ - struct hh_cache *hh = &n->hh; - if ((n->nud_state & NUD_CONNECTED) && hh->hh_len) - return neigh_hh_output(hh, skb); - else - return n->output(n, skb); -} - static inline struct neighbour * __neigh_lookup(struct neigh_table *tbl, const void *pkey, struct net_device *dev, int creat) { diff --git a/net/core/dst.c b/net/core/dst.c index 43d94cedbf7c..a6e19a23a745 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -152,7 +152,7 @@ EXPORT_SYMBOL(dst_discard); const u32 dst_default_metrics[RTAX_MAX]; void *dst_alloc(struct dst_ops *ops, struct net_device *dev, - int initial_ref, int initial_obsolete, int flags) + int initial_ref, int initial_obsolete, unsigned short flags) { struct dst_entry *dst; @@ -188,6 +188,7 @@ void *dst_alloc(struct dst_ops *ops, struct net_device *dev, dst->__use = 0; dst->lastuse = jiffies; dst->flags = flags; + dst->pending_confirm = 0; dst->next = NULL; if (!(flags & DST_NOCOUNT)) dst_entries_add(ops, 1); diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 6e9a266a0535..cc52679790b2 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -198,7 +198,7 @@ static inline int ip_finish_output2(struct sk_buff *skb) if (unlikely(!neigh)) neigh = __neigh_create(&arp_tbl, &nexthop, dev, false); if (neigh) { - int res = neigh_output(neigh, skb); + int res = dst_neigh_output(dst, neigh, skb); rcu_read_unlock_bh(); return res; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 8416f8a68e65..ca0d0e7c9778 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -740,13 +740,13 @@ void tcp_update_metrics(struct sock *sk) if (sysctl_tcp_nometrics_save) return; - dst_confirm(dst); - if (dst && (dst->flags & DST_HOST)) { const struct inet_connection_sock *icsk = inet_csk(sk); int m; unsigned long rtt; + dst_confirm(dst); + if (icsk->icsk_backoff || !tp->srtt) { /* This session failed to estimate rtt. Why? * Probably, no packets returned in time. @@ -3869,9 +3869,11 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) tcp_cong_avoid(sk, ack, prior_in_flight); } - if ((flag & FLAG_FORWARD_PROGRESS) || !(flag & FLAG_NOT_DUP)) - dst_confirm(__sk_dst_get(sk)); - + if ((flag & FLAG_FORWARD_PROGRESS) || !(flag & FLAG_NOT_DUP)) { + struct dst_entry *dst = __sk_dst_get(sk); + if (dst) + dst_confirm(dst); + } return 1; no_queue: @@ -6140,9 +6142,14 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, case TCP_FIN_WAIT1: if (tp->snd_una == tp->write_seq) { + struct dst_entry *dst; + tcp_set_state(sk, TCP_FIN_WAIT2); sk->sk_shutdown |= SEND_SHUTDOWN; - dst_confirm(__sk_dst_get(sk)); + + dst = __sk_dst_get(sk); + if (dst) + dst_confirm(dst); if (!sock_flag(sk, SOCK_DEAD)) /* Wake up lingering close() */ diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index a233a7ccbc3a..c94e4aabe11b 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -125,7 +125,7 @@ static int ip6_finish_output2(struct sk_buff *skb) rcu_read_lock(); neigh = dst_get_neighbour_noref(dst); if (neigh) { - int res = neigh_output(neigh, skb); + int res = dst_neigh_output(dst, neigh, skb); rcu_read_unlock(); return res; -- cgit v1.2.3 From f894cbf847c9bea1955095bf37aca6c050553167 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 2 Jul 2012 21:52:24 -0700 Subject: net: Add optional SKB arg to dst_ops->neigh_lookup(). Causes the handler to use the daddr in the ipv4/ipv6 header when the route gateway is unspecified (local subnet). Signed-off-by: David S. Miller --- include/net/dst.h | 8 +++++++- include/net/dst_ops.h | 4 +++- net/bridge/br_netfilter.c | 4 +++- net/decnet/dn_route.c | 8 ++++++-- net/ipv4/route.c | 14 ++++++++++---- net/ipv6/route.c | 14 ++++++++++---- net/xfrm/xfrm_policy.c | 6 ++++-- 7 files changed, 43 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 84e7a3ff968d..295a70547e7d 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -420,7 +420,13 @@ static inline int dst_neigh_output(struct dst_entry *dst, struct neighbour *n, static inline struct neighbour *dst_neigh_lookup(const struct dst_entry *dst, const void *daddr) { - return dst->ops->neigh_lookup(dst, daddr); + return dst->ops->neigh_lookup(dst, NULL, daddr); +} + +static inline struct neighbour *dst_neigh_lookup_skb(const struct dst_entry *dst, + struct sk_buff *skb) +{ + return dst->ops->neigh_lookup(dst, skb, NULL); } static inline void dst_link_failure(struct sk_buff *skb) diff --git a/include/net/dst_ops.h b/include/net/dst_ops.h index 3682a0a076c1..4badc86e45d1 100644 --- a/include/net/dst_ops.h +++ b/include/net/dst_ops.h @@ -26,7 +26,9 @@ struct dst_ops { void (*link_failure)(struct sk_buff *); void (*update_pmtu)(struct dst_entry *dst, u32 mtu); int (*local_out)(struct sk_buff *skb); - struct neighbour * (*neigh_lookup)(const struct dst_entry *dst, const void *daddr); + struct neighbour * (*neigh_lookup)(const struct dst_entry *dst, + struct sk_buff *skb, + const void *daddr); struct kmem_cache *kmem_cachep; diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 20fa719889ee..4378775432b6 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -120,7 +120,9 @@ static u32 *fake_cow_metrics(struct dst_entry *dst, unsigned long old) return NULL; } -static struct neighbour *fake_neigh_lookup(const struct dst_entry *dst, const void *daddr) +static struct neighbour *fake_neigh_lookup(const struct dst_entry *dst, + struct sk_buff *skb, + const void *daddr) { return NULL; } diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 2493ed5bfecd..60e4c6e1bac0 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -117,7 +117,9 @@ static void dn_dst_destroy(struct dst_entry *); static struct dst_entry *dn_dst_negative_advice(struct dst_entry *); static void dn_dst_link_failure(struct sk_buff *); static void dn_dst_update_pmtu(struct dst_entry *dst, u32 mtu); -static struct neighbour *dn_dst_neigh_lookup(const struct dst_entry *dst, const void *daddr); +static struct neighbour *dn_dst_neigh_lookup(const struct dst_entry *dst, + struct sk_buff *skb, + const void *daddr); static int dn_route_input(struct sk_buff *); static void dn_run_flush(unsigned long dummy); @@ -828,7 +830,9 @@ static unsigned int dn_dst_mtu(const struct dst_entry *dst) return mtu ? : dst->dev->mtu; } -static struct neighbour *dn_dst_neigh_lookup(const struct dst_entry *dst, const void *daddr) +static struct neighbour *dn_dst_neigh_lookup(const struct dst_entry *dst, + struct sk_buff *skb, + const void *daddr) { return __neigh_lookup_errno(&dn_neigh_table, daddr, dst->dev); } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index bae36386e722..7453dfcdb439 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -188,7 +188,9 @@ static u32 *ipv4_cow_metrics(struct dst_entry *dst, unsigned long old) return p; } -static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, const void *daddr); +static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, + struct sk_buff *skb, + const void *daddr); static struct dst_ops ipv4_dst_ops = { .family = AF_INET, @@ -1088,7 +1090,9 @@ static int slow_chain_length(const struct rtable *head) return length >> FRACT_BITS; } -static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, const void *daddr) +static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, + struct sk_buff *skb, + const void *daddr) { struct net_device *dev = dst->dev; const __be32 *pkey = daddr; @@ -1098,6 +1102,8 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, const vo rt = (const struct rtable *) dst; if (rt->rt_gateway) pkey = (const __be32 *) &rt->rt_gateway; + else if (skb) + pkey = &ip_hdr(skb)->daddr; n = __ipv4_neigh_lookup(dev, *(__force u32 *)pkey); if (n) @@ -1107,7 +1113,7 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, const vo static int rt_bind_neighbour(struct rtable *rt) { - struct neighbour *n = ipv4_neigh_lookup(&rt->dst, &rt->rt_gateway); + struct neighbour *n = ipv4_neigh_lookup(&rt->dst, NULL, &rt->rt_gateway); if (IS_ERR(n)) return PTR_ERR(n); dst_set_neighbour(&rt->dst, n); @@ -1388,7 +1394,7 @@ static void check_peer_redir(struct dst_entry *dst, struct inet_peer *peer) rt->rt_gateway = peer->redirect_learned.a4; - n = ipv4_neigh_lookup(&rt->dst, &rt->rt_gateway); + n = ipv4_neigh_lookup(&rt->dst, NULL, &rt->rt_gateway); if (IS_ERR(n)) { rt->rt_gateway = orig_gw; return; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index c518e4ec0cea..4b581c675bb2 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -120,21 +120,27 @@ static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) return p; } -static inline const void *choose_neigh_daddr(struct rt6_info *rt, const void *daddr) +static inline const void *choose_neigh_daddr(struct rt6_info *rt, + struct sk_buff *skb, + const void *daddr) { struct in6_addr *p = &rt->rt6i_gateway; if (!ipv6_addr_any(p)) return (const void *) p; + else if (skb) + return &ipv6_hdr(skb)->daddr; return daddr; } -static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, const void *daddr) +static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst, + struct sk_buff *skb, + const void *daddr) { struct rt6_info *rt = (struct rt6_info *) dst; struct neighbour *n; - daddr = choose_neigh_daddr(rt, daddr); + daddr = choose_neigh_daddr(rt, skb, daddr); n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr); if (n) return n; @@ -1162,7 +1168,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, if (neigh) neigh_hold(neigh); else { - neigh = ip6_neigh_lookup(&rt->dst, &fl6->daddr); + neigh = ip6_neigh_lookup(&rt->dst, NULL, &fl6->daddr); if (IS_ERR(neigh)) { in6_dev_put(idev); dst_free(&rt->dst); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index ccfbd328a69d..a28a3f972d5b 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2404,9 +2404,11 @@ static unsigned int xfrm_mtu(const struct dst_entry *dst) return mtu ? : dst_mtu(dst->path); } -static struct neighbour *xfrm_neigh_lookup(const struct dst_entry *dst, const void *daddr) +static struct neighbour *xfrm_neigh_lookup(const struct dst_entry *dst, + struct sk_buff *skb, + const void *daddr) { - return dst_neigh_lookup(dst->path, daddr); + return dst->path->ops->neigh_lookup(dst, skb, daddr); } int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo) -- cgit v1.2.3 From fccd7d5c77ff61d5283e7ce8242791d5f00dcc17 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 2 Jul 2012 22:22:18 -0700 Subject: decnet: Use neighbours privately in dn_route struct. This allows an easy conversion away from dst_get_neighbour*(). Signed-off-by: David S. Miller --- include/net/dn_route.h | 2 ++ net/decnet/dn_neigh.c | 2 +- net/decnet/dn_route.c | 36 +++++++++++++++++++++++++++++------- 3 files changed, 32 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/dn_route.h b/include/net/dn_route.h index c507e05d172f..4f7d6a182381 100644 --- a/include/net/dn_route.h +++ b/include/net/dn_route.h @@ -67,6 +67,8 @@ extern void dn_rt_cache_flush(int delay); struct dn_route { struct dst_entry dst; + struct neighbour *n; + struct flowidn fld; __le16 rt_saddr; diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index 8e9a35b17df4..3aede1b459fd 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -202,7 +202,7 @@ static int dn_neigh_output_packet(struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct dn_route *rt = (struct dn_route *)dst; - struct neighbour *neigh = dst_get_neighbour_noref(dst); + struct neighbour *neigh = rt->n; struct net_device *dev = neigh->dev; char mac_addr[ETH_ALEN]; unsigned int seq; diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 60e4c6e1bac0..6e74b3f110bc 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -114,6 +114,7 @@ static struct dst_entry *dn_dst_check(struct dst_entry *, __u32); static unsigned int dn_dst_default_advmss(const struct dst_entry *dst); static unsigned int dn_dst_mtu(const struct dst_entry *dst); static void dn_dst_destroy(struct dst_entry *); +static void dn_dst_ifdown(struct dst_entry *, struct net_device *dev, int how); static struct dst_entry *dn_dst_negative_advice(struct dst_entry *); static void dn_dst_link_failure(struct sk_buff *); static void dn_dst_update_pmtu(struct dst_entry *dst, u32 mtu); @@ -140,6 +141,7 @@ static struct dst_ops dn_dst_ops = { .mtu = dn_dst_mtu, .cow_metrics = dst_cow_metrics_generic, .destroy = dn_dst_destroy, + .ifdown = dn_dst_ifdown, .negative_advice = dn_dst_negative_advice, .link_failure = dn_dst_link_failure, .update_pmtu = dn_dst_update_pmtu, @@ -148,9 +150,27 @@ static struct dst_ops dn_dst_ops = { static void dn_dst_destroy(struct dst_entry *dst) { + struct dn_route *rt = (struct dn_route *) dst; + + if (rt->n) + neigh_release(rt->n); dst_destroy_metrics_generic(dst); } +static void dn_dst_ifdown(struct dst_entry *dst, struct net_device *dev, int how) +{ + if (how) { + struct dn_route *rt = (struct dn_route *) dst; + struct neighbour *n = rt->n; + + if (n && n->dev == dev) { + n->dev = dev_net(dev)->loopback_dev; + dev_hold(n->dev); + dev_put(dev); + } + } +} + static __inline__ unsigned int dn_hash(__le16 src, __le16 dst) { __u16 tmp = (__u16 __force)(src ^ dst); @@ -246,7 +266,8 @@ static int dn_dst_gc(struct dst_ops *ops) */ static void dn_dst_update_pmtu(struct dst_entry *dst, u32 mtu) { - struct neighbour *n = dst_get_neighbour_noref(dst); + struct dn_route *rt = (struct dn_route *) dst; + struct neighbour *n = rt->n; u32 min_mtu = 230; struct dn_dev *dn; @@ -715,7 +736,8 @@ out: static int dn_to_neigh_output(struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); - struct neighbour *n = dst_get_neighbour_noref(dst); + struct dn_route *rt = (struct dn_route *) dst; + struct neighbour *n = rt->n; return n->output(n, skb); } @@ -729,7 +751,7 @@ static int dn_output(struct sk_buff *skb) int err = -EINVAL; - if (dst_get_neighbour_noref(dst) == NULL) + if (rt->n == NULL) goto error; skb->dev = dev; @@ -852,11 +874,11 @@ static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res) } rt->rt_type = res->type; - if (dev != NULL && dst_get_neighbour_noref(&rt->dst) == NULL) { + if (dev != NULL && rt->n == NULL) { n = __neigh_lookup_errno(&dn_neigh_table, &rt->rt_gateway, dev); if (IS_ERR(n)) return PTR_ERR(n); - dst_set_neighbour(&rt->dst, n); + rt->n = n; } if (dst_metric(&rt->dst, RTAX_MTU) > rt->dst.dev->mtu) @@ -1163,7 +1185,7 @@ make_route: rt->rt_dst_map = fld.daddr; rt->rt_src_map = fld.saddr; - dst_set_neighbour(&rt->dst, neigh); + rt->n = neigh; neigh = NULL; rt->dst.lastuse = jiffies; @@ -1433,7 +1455,7 @@ make_route: rt->fld.flowidn_iif = in_dev->ifindex; rt->fld.flowidn_mark = fld.flowidn_mark; - dst_set_neighbour(&rt->dst, neigh); + rt->n = neigh; rt->dst.lastuse = jiffies; rt->dst.output = dn_rt_bug; switch (res.type) { -- cgit v1.2.3 From 1d248b1cf4e09dbec8cef5f7fbeda5874248bd09 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 3 Jul 2012 01:01:51 -0700 Subject: net: Pass neighbours and dest address into NETEVENT_REDIRECT events. Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c | 23 +++++++++------------- include/net/netevent.h | 4 ++++ net/ipv6/route.c | 7 ++++++- 3 files changed, 19 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c index 55cf72af69ce..633c6029e53c 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c @@ -62,7 +62,8 @@ static const unsigned int MAX_ATIDS = 64 * 1024; static const unsigned int ATID_BASE = 0x10000; static void cxgb_neigh_update(struct neighbour *neigh); -static void cxgb_redirect(struct dst_entry *old, struct dst_entry *new); +static void cxgb_redirect(struct dst_entry *old, struct neighbour *old_neigh, + struct dst_entry *new, struct neighbour *new_neigh); static inline int offload_activated(struct t3cdev *tdev) { @@ -968,8 +969,9 @@ static int nb_callback(struct notifier_block *self, unsigned long event, } case (NETEVENT_REDIRECT):{ struct netevent_redirect *nr = ctx; - cxgb_redirect(nr->old, nr->new); - cxgb_neigh_update(dst_get_neighbour_noref(nr->new)); + cxgb_redirect(nr->old, nr->old_neigh, + nr->new, nr->new_neigh); + cxgb_neigh_update(nr->new_neigh); break; } default: @@ -1107,10 +1109,10 @@ static void set_l2t_ix(struct t3cdev *tdev, u32 tid, struct l2t_entry *e) tdev->send(tdev, skb); } -static void cxgb_redirect(struct dst_entry *old, struct dst_entry *new) +static void cxgb_redirect(struct dst_entry *old, struct neighbour *old_neigh, + struct dst_entry *new, struct neighbour *new_neigh) { struct net_device *olddev, *newdev; - struct neighbour *n; struct tid_info *ti; struct t3cdev *tdev; u32 tid; @@ -1118,15 +1120,8 @@ static void cxgb_redirect(struct dst_entry *old, struct dst_entry *new) struct l2t_entry *e; struct t3c_tid_entry *te; - n = dst_get_neighbour_noref(old); - if (!n) - return; - olddev = n->dev; - - n = dst_get_neighbour_noref(new); - if (!n) - return; - newdev = n->dev; + olddev = old_neigh->dev; + newdev = new_neigh->dev; if (!is_offloading(olddev)) return; diff --git a/include/net/netevent.h b/include/net/netevent.h index 086f8a5b59dc..3ce4988c9c08 100644 --- a/include/net/netevent.h +++ b/include/net/netevent.h @@ -12,10 +12,14 @@ */ struct dst_entry; +struct neighbour; struct netevent_redirect { struct dst_entry *old; + struct neighbour *old_neigh; struct dst_entry *new; + struct neighbour *new_neigh; + const void *daddr; }; enum netevent_notif_type { diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 4b581c675bb2..34b29881e22d 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1687,6 +1687,7 @@ void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src, struct rt6_info *rt, *nrt = NULL; struct netevent_redirect netevent; struct net *net = dev_net(neigh->dev); + struct neighbour *old_neigh; rt = ip6_route_redirect(dest, src, saddr, neigh->dev); @@ -1714,7 +1715,8 @@ void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src, dst_confirm(&rt->dst); /* Duplicate redirect: silently ignore. */ - if (neigh == dst_get_neighbour_noref_raw(&rt->dst)) + old_neigh = dst_get_neighbour_noref_raw(&rt->dst); + if (neigh == old_neigh) goto out; nrt = ip6_rt_copy(rt, dest); @@ -1732,7 +1734,10 @@ void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src, goto out; netevent.old = &rt->dst; + netevent.old_neigh = old_neigh; netevent.new = &nrt->dst; + netevent.new_neigh = neigh; + netevent.daddr = dest; call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); if (rt->rt6i_flags & RTF_CACHE) { -- cgit v1.2.3 From 97cac0821af4474ec4ba3a9e7a36b98ed9b6db88 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 2 Jul 2012 22:43:47 -0700 Subject: ipv6: Store route neighbour in rt6_info struct. This makes for a simplified conversion away from dst_get_neighbour*(). All code outside of ipv6 will use neigh lookups via dst_neigh_lookup*(). Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 2 ++ net/ipv6/ip6_output.c | 8 ++++++-- net/ipv6/route.c | 42 ++++++++++++++++++++++++++---------------- net/ipv6/xfrm6_policy.c | 1 + 4 files changed, 35 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index a192f7807659..0fedbd8d747a 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -86,6 +86,8 @@ struct fib6_table; struct rt6_info { struct dst_entry dst; + struct neighbour *n; + /* * Tail elements of dst_entry (__refcnt etc.) * and these elements (rarely used in hot path) are in diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index c94e4aabe11b..6d9c0abc8c20 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -88,6 +88,7 @@ static int ip6_finish_output2(struct sk_buff *skb) struct dst_entry *dst = skb_dst(skb); struct net_device *dev = dst->dev; struct neighbour *neigh; + struct rt6_info *rt; skb->protocol = htons(ETH_P_IPV6); skb->dev = dev; @@ -123,7 +124,8 @@ static int ip6_finish_output2(struct sk_buff *skb) } rcu_read_lock(); - neigh = dst_get_neighbour_noref(dst); + rt = (struct rt6_info *) dst; + neigh = rt->n; if (neigh) { int res = dst_neigh_output(dst, neigh, skb); @@ -944,6 +946,7 @@ static int ip6_dst_lookup_tail(struct sock *sk, struct net *net = sock_net(sk); #ifdef CONFIG_IPV6_OPTIMISTIC_DAD struct neighbour *n; + struct rt6_info *rt; #endif int err; @@ -972,7 +975,8 @@ static int ip6_dst_lookup_tail(struct sock *sk, * dst entry of the nexthop router */ rcu_read_lock(); - n = dst_get_neighbour_noref(*dst); + rt = (struct rt6_info *) dst; + n = rt->n; if (n && !(n->nud_state & NUD_VALID)) { struct inet6_ifaddr *ifp; struct flowi6 fl_gw6; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 34b29881e22d..ceff71d24f8e 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -155,7 +155,7 @@ static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev) if (IS_ERR(n)) return PTR_ERR(n); } - dst_set_neighbour(&rt->dst, n); + rt->n = n; return 0; } @@ -285,6 +285,9 @@ static void ip6_dst_destroy(struct dst_entry *dst) struct rt6_info *rt = (struct rt6_info *)dst; struct inet6_dev *idev = rt->rt6i_idev; + if (rt->n) + neigh_release(rt->n); + if (!(rt->dst.flags & DST_HOST)) dst_destroy_metrics_generic(dst); @@ -335,12 +338,19 @@ static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, struct net_device *loopback_dev = dev_net(dev)->loopback_dev; - if (dev != loopback_dev && idev && idev->dev == dev) { - struct inet6_dev *loopback_idev = - in6_dev_get(loopback_dev); - if (loopback_idev) { - rt->rt6i_idev = loopback_idev; - in6_dev_put(idev); + if (dev != loopback_dev) { + if (idev && idev->dev == dev) { + struct inet6_dev *loopback_idev = + in6_dev_get(loopback_dev); + if (loopback_idev) { + rt->rt6i_idev = loopback_idev; + in6_dev_put(idev); + } + } + if (rt->n && rt->n->dev == dev) { + rt->n->dev = loopback_dev; + dev_hold(loopback_dev); + dev_put(dev); } } } @@ -430,7 +440,7 @@ static void rt6_probe(struct rt6_info *rt) * to no more than one per minute. */ rcu_read_lock(); - neigh = rt ? dst_get_neighbour_noref(&rt->dst) : NULL; + neigh = rt ? rt->n : NULL; if (!neigh || (neigh->nud_state & NUD_VALID)) goto out; read_lock_bh(&neigh->lock); @@ -477,7 +487,7 @@ static inline int rt6_check_neigh(struct rt6_info *rt) int m; rcu_read_lock(); - neigh = dst_get_neighbour_noref(&rt->dst); + neigh = rt->n; if (rt->rt6i_flags & RTF_NONEXTHOP || !(rt->rt6i_flags & RTF_GATEWAY)) m = 1; @@ -824,7 +834,7 @@ static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, if (rt) { rt->rt6i_flags |= RTF_CACHE; - dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour_noref_raw(&ort->dst))); + rt->n = neigh_clone(ort->n); } return rt; } @@ -858,7 +868,7 @@ restart: dst_hold(&rt->dst); read_unlock_bh(&table->tb6_lock); - if (!dst_get_neighbour_noref_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP)) + if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP)) nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr); else if (!(rt->dst.flags & DST_HOST)) nrt = rt6_alloc_clone(rt, &fl6->daddr); @@ -1178,7 +1188,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, rt->dst.flags |= DST_HOST; rt->dst.output = ip6_output; - dst_set_neighbour(&rt->dst, neigh); + rt->n = neigh; atomic_set(&rt->dst.__refcnt, 1); rt->rt6i_dst.addr = fl6->daddr; rt->rt6i_dst.plen = 128; @@ -1715,7 +1725,7 @@ void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src, dst_confirm(&rt->dst); /* Duplicate redirect: silently ignore. */ - old_neigh = dst_get_neighbour_noref_raw(&rt->dst); + old_neigh = rt->n; if (neigh == old_neigh) goto out; @@ -1728,7 +1738,7 @@ void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src, nrt->rt6i_flags &= ~RTF_GATEWAY; nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; - dst_set_neighbour(&nrt->dst, neigh_clone(neigh)); + nrt->n = neigh_clone(neigh); if (ip6_ins_rt(nrt)) goto out; @@ -2442,7 +2452,7 @@ static int rt6_fill_node(struct net *net, goto nla_put_failure; rcu_read_lock(); - n = dst_get_neighbour_noref(&rt->dst); + n = rt->n; if (n) { if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) { rcu_read_unlock(); @@ -2666,7 +2676,7 @@ static int rt6_info_route(struct rt6_info *rt, void *p_arg) seq_puts(m, "00000000000000000000000000000000 00 "); #endif rcu_read_lock(); - n = dst_get_neighbour_noref(&rt->dst); + n = rt->n; if (n) { seq_printf(m, "%pi6", n->primary_key); } else { diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index d7494845efbf..bb02038b822b 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -103,6 +103,7 @@ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, /* Sheit... I remember I did this right. Apparently, * it was magically lost, so this code needs audit */ + xdst->u.rt6.n = neigh_clone(rt->n); xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST | RTF_LOCAL); xdst->u.rt6.rt6i_metric = rt->rt6i_metric; -- cgit v1.2.3 From 36bdbcae2fa2a6dfa99344d4190fcea0aa7b7c25 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 2 Jul 2012 22:58:02 -0700 Subject: net: Kill dst->_neighbour, accessors, and final uses. No longer used. Signed-off-by: David S. Miller --- include/net/dst.h | 17 +---------------- net/core/dst.c | 18 ------------------ 2 files changed, 1 insertion(+), 34 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 295a70547e7d..b2634e446613 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -42,7 +42,7 @@ struct dst_entry { struct dst_entry *from; }; struct dst_entry *path; - struct neighbour __rcu *_neighbour; + void *__pad0; #ifdef CONFIG_XFRM struct xfrm_state *xfrm; #else @@ -96,21 +96,6 @@ struct dst_entry { }; }; -static inline struct neighbour *dst_get_neighbour_noref(struct dst_entry *dst) -{ - return rcu_dereference(dst->_neighbour); -} - -static inline struct neighbour *dst_get_neighbour_noref_raw(struct dst_entry *dst) -{ - return rcu_dereference_raw(dst->_neighbour); -} - -static inline void dst_set_neighbour(struct dst_entry *dst, struct neighbour *neigh) -{ - rcu_assign_pointer(dst->_neighbour, neigh); -} - extern u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old); extern const u32 dst_default_metrics[RTAX_MAX]; diff --git a/net/core/dst.c b/net/core/dst.c index a6e19a23a745..07bacff84aa4 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -171,7 +171,6 @@ void *dst_alloc(struct dst_ops *ops, struct net_device *dev, dst_init_metrics(dst, dst_default_metrics, true); dst->expires = 0UL; dst->path = dst; - RCU_INIT_POINTER(dst->_neighbour, NULL); #ifdef CONFIG_XFRM dst->xfrm = NULL; #endif @@ -225,19 +224,12 @@ EXPORT_SYMBOL(__dst_free); struct dst_entry *dst_destroy(struct dst_entry * dst) { struct dst_entry *child; - struct neighbour *neigh; smp_rmb(); again: - neigh = rcu_dereference_protected(dst->_neighbour, 1); child = dst->child; - if (neigh) { - RCU_INIT_POINTER(dst->_neighbour, NULL); - neigh_release(neigh); - } - if (!(dst->flags & DST_NOCOUNT)) dst_entries_add(dst->ops, -1); @@ -361,19 +353,9 @@ static void dst_ifdown(struct dst_entry *dst, struct net_device *dev, if (!unregister) { dst->input = dst->output = dst_discard; } else { - struct neighbour *neigh; - dst->dev = dev_net(dst->dev)->loopback_dev; dev_hold(dst->dev); dev_put(dev); - rcu_read_lock(); - neigh = dst_get_neighbour_noref(dst); - if (neigh && neigh->dev == dev) { - neigh->dev = dst->dev; - dev_hold(dst->dev); - dev_put(dev); - } - rcu_read_unlock(); } } -- cgit v1.2.3 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') 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 From 1464189f8c2a5341722437ef916786afaf241c44 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 5 Jul 2012 12:15:01 +0100 Subject: ALSA: pcm: Make constraints lists const They aren't modified by the core so the drivers can declare them const. Signed-off-by: Mark Brown Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 2 +- sound/core/pcm_lib.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 68372bc1e11b..e91e6047ca6f 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -810,7 +810,7 @@ int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_pa int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, - struct snd_pcm_hw_constraint_list *l); + const struct snd_pcm_hw_constraint_list *l); int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 8f312fa6c282..7ae671923393 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1250,10 +1250,10 @@ static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, - struct snd_pcm_hw_constraint_list *l) + const struct snd_pcm_hw_constraint_list *l) { return snd_pcm_hw_rule_add(runtime, cond, var, - snd_pcm_hw_rule_list, l, + snd_pcm_hw_rule_list, (void *)l, var, -1); } -- cgit v1.2.3 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') 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 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') 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 From 95ddc1fc4519ed48ddc7d622bd5c84dff3eebb0a Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 5 Jul 2012 14:25:50 +0300 Subject: cfg80211: bitrate calculation for 60g 60g band uses different from .11n MCS scheme, so bitrate should be calculated differently Signed-off-by: Vladimir Kondratiev Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ net/wireless/util.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8837efc368f9..51f67a9003a9 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -580,11 +580,13 @@ enum station_info_flags { * @RATE_INFO_FLAGS_MCS: @tx_bitrate_mcs filled * @RATE_INFO_FLAGS_40_MHZ_WIDTH: 40 Mhz width transmission * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval + * @RATE_INFO_FLAGS_60G: 60gHz MCS */ enum rate_info_flags { RATE_INFO_FLAGS_MCS = 1<<0, RATE_INFO_FLAGS_40_MHZ_WIDTH = 1<<1, RATE_INFO_FLAGS_SHORT_GI = 1<<2, + RATE_INFO_FLAGS_60G = 1<<3, }; /** diff --git a/net/wireless/util.c b/net/wireless/util.c index 6e52726f7fe3..e31f1dba79ec 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -900,12 +900,61 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, return err; } +static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate) +{ + static const u32 __mcs2bitrate[] = { + /* control PHY */ + [0] = 275, + /* SC PHY */ + [1] = 3850, + [2] = 7700, + [3] = 9625, + [4] = 11550, + [5] = 12512, /* 1251.25 mbps */ + [6] = 15400, + [7] = 19250, + [8] = 23100, + [9] = 25025, + [10] = 30800, + [11] = 38500, + [12] = 46200, + /* OFDM PHY */ + [13] = 6930, + [14] = 8662, /* 866.25 mbps */ + [15] = 13860, + [16] = 17325, + [17] = 20790, + [18] = 27720, + [19] = 34650, + [20] = 41580, + [21] = 45045, + [22] = 51975, + [23] = 62370, + [24] = 67568, /* 6756.75 mbps */ + /* LP-SC PHY */ + [25] = 6260, + [26] = 8340, + [27] = 11120, + [28] = 12510, + [29] = 16680, + [30] = 22240, + [31] = 25030, + }; + + if (WARN_ON_ONCE(rate->mcs >= ARRAY_SIZE(__mcs2bitrate))) + return 0; + + return __mcs2bitrate[rate->mcs]; +} + u32 cfg80211_calculate_bitrate(struct rate_info *rate) { int modulation, streams, bitrate; if (!(rate->flags & RATE_INFO_FLAGS_MCS)) return rate->legacy; + if (rate->flags & RATE_INFO_FLAGS_60G) + return cfg80211_calculate_bitrate_60g(rate); /* the formula below does only work for MCS values smaller than 32 */ if (WARN_ON_ONCE(rate->mcs >= 32)) -- cgit v1.2.3 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') 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 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') 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 From 2b719d7baf490e24ce7d817c6337b7c87fda84c1 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 2 May 2012 09:40:03 -0300 Subject: [media] v4l: drop v4l2_buffer.input and V4L2_BUF_FLAG_INPUT Remove input field in struct v4l2_buffer and flag V4L2_BUF_FLAG_INPUT which tells the former is valid. The flag is used by no driver currently. Also change the documentation accordingly. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/compat.xml | 12 ++++++++++++ Documentation/DocBook/media/v4l/io.xml | 19 +++++-------------- Documentation/DocBook/media/v4l/vidioc-qbuf.xml | 9 +++------ drivers/media/video/cpia2/cpia2_v4l.c | 2 +- drivers/media/video/v4l2-compat-ioctl32.c | 11 +++++------ drivers/media/video/videobuf-core.c | 16 ---------------- drivers/media/video/videobuf2-core.c | 5 ++--- include/linux/videodev2.h | 3 +-- 8 files changed, 29 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index ea42ef824948..a8b374930405 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2458,6 +2458,18 @@ details. +
+ V4L2 in Linux 3.5 + + + Replaced input in + v4l2_buffer by + reserved2 and removed + V4L2_BUF_FLAG_INPUT. + + +
+
Relation of V4L2 to other Linux multimedia APIs diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index fd6aca2922b6..1885cc0755cb 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -683,14 +683,12 @@ memory, set by the application. See for details. __u32 - input + reserved2 - Some video capture drivers support rapid and -synchronous video input changes, a function useful for example in -video surveillance applications. For this purpose applications set the -V4L2_BUF_FLAG_INPUT flag, and this field to the -number of a video input as in &v4l2-input; field -index. + A place holder for future extensions and custom +(driver defined) buffer types +V4L2_BUF_TYPE_PRIVATE and higher. Applications +should set this to 0. __u32 @@ -921,13 +919,6 @@ previous key frame. The timecode field is valid. Drivers set or clear this flag when the VIDIOC_DQBUF ioctl is called. - - - V4L2_BUF_FLAG_INPUT - 0x0200 - The input field is valid. -Applications set or clear this flag before calling the -VIDIOC_QBUF ioctl. V4L2_BUF_FLAG_PREPARED diff --git a/Documentation/DocBook/media/v4l/vidioc-qbuf.xml b/Documentation/DocBook/media/v4l/vidioc-qbuf.xml index 9caa49af580f..77ff5be0809d 100644 --- a/Documentation/DocBook/media/v4l/vidioc-qbuf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-qbuf.xml @@ -71,12 +71,9 @@ initialize the bytesused, field and timestamp fields, see for details. -Applications must also set flags to 0. If a driver -supports capturing from specific video inputs and you want to specify a video -input, then flags should be set to -V4L2_BUF_FLAG_INPUT and the field -input must be initialized to the desired input. -The reserved field must be set to 0. When using +Applications must also set flags to 0. +The reserved2 and +reserved fields must be set to 0. When using the multi-planar API, the m.planes field must contain a userspace pointer to a filled-in array of &v4l2-plane; and the length diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 55e92902a76c..a62a7b739991 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -932,7 +932,7 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->sequence = cam->buffers[buf->index].seq; buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer; buf->length = cam->frame_size; - buf->input = 0; + buf->reserved2 = 0; buf->reserved = 0; memset(&buf->timecode, 0, sizeof(buf->timecode)); diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 5327ad3a6390..658ba46ee9d2 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -327,7 +327,7 @@ struct v4l2_buffer32 { compat_caddr_t planes; } m; __u32 length; - __u32 input; + __u32 reserved2; __u32 reserved; }; @@ -387,8 +387,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user get_user(kp->index, &up->index) || get_user(kp->type, &up->type) || get_user(kp->flags, &up->flags) || - get_user(kp->memory, &up->memory) || - get_user(kp->input, &up->input)) + get_user(kp->memory, &up->memory) return -EFAULT; if (V4L2_TYPE_IS_OUTPUT(kp->type)) @@ -472,8 +471,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user put_user(kp->index, &up->index) || put_user(kp->type, &up->type) || put_user(kp->flags, &up->flags) || - put_user(kp->memory, &up->memory) || - put_user(kp->input, &up->input)) + put_user(kp->memory, &up->memory) return -EFAULT; if (put_user(kp->bytesused, &up->bytesused) || @@ -482,7 +480,8 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) || copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) || put_user(kp->sequence, &up->sequence) || - put_user(kp->reserved, &up->reserved)) + put_user(kp->reserved2, &up->reserved2) || + put_user(kp->reserved, &up->reserved) return -EFAULT; if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) { diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index ffdf59cfe405..bf7a326b1cdc 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -359,11 +359,6 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, break; } - if (vb->input != UNSET) { - b->flags |= V4L2_BUF_FLAG_INPUT; - b->input = vb->input; - } - b->field = vb->field; b->timestamp = vb->ts; b->bytesused = vb->size; @@ -402,7 +397,6 @@ int __videobuf_mmap_setup(struct videobuf_queue *q, break; q->bufs[i]->i = i; - q->bufs[i]->input = UNSET; q->bufs[i]->memory = memory; q->bufs[i]->bsize = bsize; switch (memory) { @@ -566,16 +560,6 @@ int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b) goto done; } - if (b->flags & V4L2_BUF_FLAG_INPUT) { - if (b->input >= q->inputs) { - dprintk(1, "qbuf: wrong input.\n"); - goto done; - } - buf->input = b->input; - } else { - buf->input = UNSET; - } - switch (b->memory) { case V4L2_MEMORY_MMAP: if (0 == buf->baddr) { diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 9d4e9edbd2e7..ec24718b3ed0 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -336,9 +336,9 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) struct vb2_queue *q = vb->vb2_queue; int ret; - /* Copy back data such as timestamp, flags, input, etc. */ + /* Copy back data such as timestamp, flags, etc. */ memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m)); - b->input = vb->v4l2_buf.input; + b->reserved2 = vb->v4l2_buf.reserved2; b->reserved = vb->v4l2_buf.reserved; if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) { @@ -860,7 +860,6 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b, vb->v4l2_buf.field = b->field; vb->v4l2_buf.timestamp = b->timestamp; - vb->v4l2_buf.input = b->input; vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS; return 0; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index f79d0cc565ab..a61edb353273 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -657,7 +657,7 @@ struct v4l2_buffer { struct v4l2_plane *planes; } m; __u32 length; - __u32 input; + __u32 reserved2; __u32 reserved; }; @@ -671,7 +671,6 @@ struct v4l2_buffer { /* Buffer is ready, but the data contained within is corrupted. */ #define V4L2_BUF_FLAG_ERROR 0x0040 #define V4L2_BUF_FLAG_TIMECODE 0x0100 /* timecode field is valid */ -#define V4L2_BUF_FLAG_INPUT 0x0200 /* input field is valid */ #define V4L2_BUF_FLAG_PREPARED 0x0400 /* Buffer is prepared for queuing */ /* Cache handling flags */ #define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE 0x0800 -- cgit v1.2.3 From 31361fc4632f20e3a108f56b1a1a9c9bf2dfc07c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 5 Jul 2012 18:01:55 -0300 Subject: [media] videobuf-core.h: remove input fields Now that the input fields got removed from the userspace API, there's no sense to keep there at the VB struct. Remove it. Cc: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/media/videobuf-core.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/media/videobuf-core.h b/include/media/videobuf-core.h index 90ed895e217d..8c6e825940e5 100644 --- a/include/media/videobuf-core.h +++ b/include/media/videobuf-core.h @@ -72,7 +72,6 @@ struct videobuf_buffer { unsigned int height; unsigned int bytesperline; /* use only if != 0 */ unsigned long size; - unsigned int input; enum v4l2_field field; enum videobuf_state state; struct list_head stream; /* QBUF/DQBUF list */ @@ -142,7 +141,6 @@ struct videobuf_queue { wait_queue_head_t wait; /* wait if queue is empty */ enum v4l2_buf_type type; - unsigned int inputs; /* for V4L2_BUF_FLAG_INPUT */ unsigned int msize; enum v4l2_field field; enum v4l2_field last; /* for field=V4L2_FIELD_ALTERNATE */ -- cgit v1.2.3 From b5ab5e24e960b9f780a4cc96815cfd4b0d412720 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Wed, 30 May 2012 22:01:25 +0300 Subject: remoteproc: maintain a generic child device for each rproc For each registered rproc, maintain a generic remoteproc device whose parent is the low level platform-specific device (commonly a pdev, but it may certainly be any other type of device too). With this in hand, the resulting device hierarchy might then look like: omap-rproc.0 | - remoteproc0 <---- new ! | - virtio0 | - virtio1 | - rpmsg0 | - rpmsg1 | - rpmsg2 Where: - omap-rproc.0 is the low level device that's bound to the driver which invokes rproc_register() - remoteproc0 is the result of this patch, and will be added by the remoteproc framework when rproc_register() is invoked - virtio0 and virtio1 are vdevs that are registered by remoteproc when it realizes that they are supported by the firmware of the physical remote processor represented by omap-rproc.0 - rpmsg0, rpmsg1 and rpmsg2 are rpmsg devices that represent rpmsg channels, and are registerd by the rpmsg bus when it gets notified about their existence Technically, this patch: - changes 'struct rproc' to contain this generic remoteproc.x device - creates a new "remoteproc" type, to which this new generic remoteproc.x device belong to. - adds a super simple enumeration method for the indices of the remoteproc.x devices - updates all dev_* messaging to use the generic remoteproc.x device instead of the low level platform-specific device - updates all dma_* allocations to use the parent of remoteproc.x (where the platform-specific memory pools, most commonly CMA, are to be found) Adding this generic device has several merits: - we can now add remoteproc runtime PM support simply by hooking onto the new "remoteproc" type - all remoteproc log messages will now carry a common name prefix instead of having a platform-specific one - having a device as part of the rproc struct makes it possible to simplify refcounting (see subsequent patch) Thanks to Stephen Boyd for suggesting and discussing these ideas in one of the remoteproc review threads and to Fernando Guzman Lugo for trying them out with the (upcoming) runtime PM support for remoteproc. Cc: Fernando Guzman Lugo Reviewed-by: Stephen Boyd Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/omap_remoteproc.c | 17 ++-- drivers/remoteproc/remoteproc_core.c | 135 ++++++++++++++++++++++---------- drivers/remoteproc/remoteproc_debugfs.c | 4 +- drivers/remoteproc/remoteproc_virtio.c | 13 +-- drivers/rpmsg/virtio_rpmsg_bus.c | 3 +- include/linux/remoteproc.h | 6 +- 6 files changed, 117 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index 69425c4e86f3..b5e6d2981741 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -66,7 +66,7 @@ static int omap_rproc_mbox_callback(struct notifier_block *this, { mbox_msg_t msg = (mbox_msg_t) data; struct omap_rproc *oproc = container_of(this, struct omap_rproc, nb); - struct device *dev = oproc->rproc->dev; + struct device *dev = oproc->rproc->dev.parent; const char *name = oproc->rproc->name; dev_dbg(dev, "mbox msg: 0x%x\n", msg); @@ -92,12 +92,13 @@ static int omap_rproc_mbox_callback(struct notifier_block *this, static void omap_rproc_kick(struct rproc *rproc, int vqid) { struct omap_rproc *oproc = rproc->priv; + struct device *dev = rproc->dev.parent; int ret; /* send the index of the triggered virtqueue in the mailbox payload */ ret = omap_mbox_msg_send(oproc->mbox, vqid); if (ret) - dev_err(rproc->dev, "omap_mbox_msg_send failed: %d\n", ret); + dev_err(dev, "omap_mbox_msg_send failed: %d\n", ret); } /* @@ -110,7 +111,8 @@ static void omap_rproc_kick(struct rproc *rproc, int vqid) static int omap_rproc_start(struct rproc *rproc) { struct omap_rproc *oproc = rproc->priv; - struct platform_device *pdev = to_platform_device(rproc->dev); + struct device *dev = rproc->dev.parent; + struct platform_device *pdev = to_platform_device(dev); struct omap_rproc_pdata *pdata = pdev->dev.platform_data; int ret; @@ -120,7 +122,7 @@ static int omap_rproc_start(struct rproc *rproc) oproc->mbox = omap_mbox_get(pdata->mbox_name, &oproc->nb); if (IS_ERR(oproc->mbox)) { ret = PTR_ERR(oproc->mbox); - dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret); + dev_err(dev, "omap_mbox_get failed: %d\n", ret); return ret; } @@ -133,13 +135,13 @@ static int omap_rproc_start(struct rproc *rproc) */ ret = omap_mbox_msg_send(oproc->mbox, RP_MBOX_ECHO_REQUEST); if (ret) { - dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret); + dev_err(dev, "omap_mbox_get failed: %d\n", ret); goto put_mbox; } ret = pdata->device_enable(pdev); if (ret) { - dev_err(rproc->dev, "omap_device_enable failed: %d\n", ret); + dev_err(dev, "omap_device_enable failed: %d\n", ret); goto put_mbox; } @@ -153,7 +155,8 @@ put_mbox: /* power off the remote processor */ static int omap_rproc_stop(struct rproc *rproc) { - struct platform_device *pdev = to_platform_device(rproc->dev); + struct device *dev = rproc->dev.parent; + struct platform_device *pdev = to_platform_device(dev); struct omap_rproc_pdata *pdata = pdev->dev.platform_data; struct omap_rproc *oproc = rproc->priv; int ret; diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 288d4175bbf6..25f937843836 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,9 @@ typedef int (*rproc_handle_resources_t)(struct rproc *rproc, struct resource_table *table, int len); typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail); +/* Unique indices for remoteproc devices */ +static DEFINE_IDA(rproc_dev_index); + /* * This is the IOMMU fault handler we register with the IOMMU API * (when relevant; not all remote processors access memory through @@ -92,7 +96,7 @@ static int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev, static int rproc_enable_iommu(struct rproc *rproc) { struct iommu_domain *domain; - struct device *dev = rproc->dev; + struct device *dev = rproc->dev.parent; int ret; /* @@ -137,7 +141,7 @@ free_domain: static void rproc_disable_iommu(struct rproc *rproc) { struct iommu_domain *domain = rproc->domain; - struct device *dev = rproc->dev; + struct device *dev = rproc->dev.parent; if (!domain) return; @@ -217,7 +221,7 @@ static void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) static int rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; struct elf32_hdr *ehdr; struct elf32_phdr *phdr; int i, ret = 0; @@ -282,7 +286,7 @@ rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len) int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) { struct rproc *rproc = rvdev->rproc; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; struct rproc_vring *rvring = &rvdev->vring[i]; dma_addr_t dma; void *va; @@ -301,9 +305,9 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) * this call will also configure the IOMMU for us * TODO: let the rproc know the da of this vring */ - va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL); + va = dma_alloc_coherent(dev->parent, size, &dma, GFP_KERNEL); if (!va) { - dev_err(dev, "dma_alloc_coherent failed\n"); + dev_err(dev->parent, "dma_alloc_coherent failed\n"); return -EINVAL; } @@ -316,7 +320,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) ret = idr_get_new(&rproc->notifyids, rvring, ¬ifyid); if (ret) { dev_err(dev, "idr_get_new failed: %d\n", ret); - dma_free_coherent(dev, size, va, dma); + dma_free_coherent(dev->parent, size, va, dma); return ret; } @@ -334,7 +338,7 @@ static int rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) { struct rproc *rproc = rvdev->rproc; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; struct fw_rsc_vdev_vring *vring = &rsc->vring[i]; struct rproc_vring *rvring = &rvdev->vring[i]; @@ -366,7 +370,7 @@ void rproc_free_vring(struct rproc_vring *rvring) int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); struct rproc *rproc = rvring->rvdev->rproc; - dma_free_coherent(rproc->dev, size, rvring->va, rvring->dma); + dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma); idr_remove(&rproc->notifyids, rvring->notifyid); } @@ -400,14 +404,14 @@ void rproc_free_vring(struct rproc_vring *rvring) static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, int avail) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; struct rproc_vdev *rvdev; int i, ret; /* make sure resource isn't truncated */ if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring) + rsc->config_len > avail) { - dev_err(rproc->dev, "vdev rsc is truncated\n"); + dev_err(dev, "vdev rsc is truncated\n"); return -EINVAL; } @@ -476,12 +480,12 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, int avail) { struct rproc_mem_entry *trace; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; void *ptr; char name[15]; if (sizeof(*rsc) > avail) { - dev_err(rproc->dev, "trace rsc is truncated\n"); + dev_err(dev, "trace rsc is truncated\n"); return -EINVAL; } @@ -558,6 +562,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc, int avail) { struct rproc_mem_entry *mapping; + struct device *dev = &rproc->dev; int ret; /* no point in handling this resource without a valid iommu domain */ @@ -565,25 +570,25 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc, return -EINVAL; if (sizeof(*rsc) > avail) { - dev_err(rproc->dev, "devmem rsc is truncated\n"); + dev_err(dev, "devmem rsc is truncated\n"); return -EINVAL; } /* make sure reserved bytes are zeroes */ if (rsc->reserved) { - dev_err(rproc->dev, "devmem rsc has non zero reserved bytes\n"); + dev_err(dev, "devmem rsc has non zero reserved bytes\n"); return -EINVAL; } mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); if (!mapping) { - dev_err(rproc->dev, "kzalloc mapping failed\n"); + dev_err(dev, "kzalloc mapping failed\n"); return -ENOMEM; } ret = iommu_map(rproc->domain, rsc->da, rsc->pa, rsc->len, rsc->flags); if (ret) { - dev_err(rproc->dev, "failed to map devmem: %d\n", ret); + dev_err(dev, "failed to map devmem: %d\n", ret); goto out; } @@ -598,7 +603,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc, mapping->len = rsc->len; list_add_tail(&mapping->node, &rproc->mappings); - dev_dbg(rproc->dev, "mapped devmem pa 0x%x, da 0x%x, len 0x%x\n", + dev_dbg(dev, "mapped devmem pa 0x%x, da 0x%x, len 0x%x\n", rsc->pa, rsc->da, rsc->len); return 0; @@ -630,13 +635,13 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_rsc_carveout *rsc, int avail) { struct rproc_mem_entry *carveout, *mapping; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; dma_addr_t dma; void *va; int ret; if (sizeof(*rsc) > avail) { - dev_err(rproc->dev, "carveout rsc is truncated\n"); + dev_err(dev, "carveout rsc is truncated\n"); return -EINVAL; } @@ -662,9 +667,9 @@ static int rproc_handle_carveout(struct rproc *rproc, goto free_mapping; } - va = dma_alloc_coherent(dev, rsc->len, &dma, GFP_KERNEL); + va = dma_alloc_coherent(dev->parent, rsc->len, &dma, GFP_KERNEL); if (!va) { - dev_err(dev, "failed to dma alloc carveout: %d\n", rsc->len); + dev_err(dev->parent, "dma_alloc_coherent err: %d\n", rsc->len); ret = -ENOMEM; goto free_carv; } @@ -735,7 +740,7 @@ static int rproc_handle_carveout(struct rproc *rproc, return 0; dma_free: - dma_free_coherent(dev, rsc->len, va, dma); + dma_free_coherent(dev->parent, rsc->len, va, dma); free_carv: kfree(carveout); free_mapping: @@ -758,7 +763,7 @@ static rproc_handle_resource_t rproc_handle_rsc[] = { static int rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; rproc_handle_resource_t handler; int ret = 0, i; @@ -797,7 +802,7 @@ rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len static int rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; int ret = 0, i; for (i = 0; i < table->num; i++) { @@ -850,7 +855,7 @@ rproc_find_rsc_table(struct rproc *rproc, const u8 *elf_data, size_t len, struct elf32_hdr *ehdr; struct elf32_shdr *shdr; const char *name_table; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; struct resource_table *table = NULL; int i; @@ -916,7 +921,7 @@ rproc_find_rsc_table(struct rproc *rproc, const u8 *elf_data, size_t len, static void rproc_resource_cleanup(struct rproc *rproc) { struct rproc_mem_entry *entry, *tmp; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; /* clean up debugfs trace entries */ list_for_each_entry_safe(entry, tmp, &rproc->traces, node) { @@ -928,7 +933,7 @@ static void rproc_resource_cleanup(struct rproc *rproc) /* clean up carveout allocations */ list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { - dma_free_coherent(dev, entry->len, entry->va, entry->dma); + dma_free_coherent(dev->parent, entry->len, entry->va, entry->dma); list_del(&entry->node); kfree(entry); } @@ -953,7 +958,7 @@ static void rproc_resource_cleanup(struct rproc *rproc) static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw) { const char *name = rproc->firmware; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; struct elf32_hdr *ehdr; char class; @@ -1014,7 +1019,7 @@ static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw) */ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; const char *name = rproc->firmware; struct elf32_hdr *ehdr; struct resource_table *table; @@ -1138,7 +1143,7 @@ int rproc_boot(struct rproc *rproc) return -EINVAL; } - dev = rproc->dev; + dev = &rproc->dev; ret = mutex_lock_interruptible(&rproc->lock); if (ret) { @@ -1154,7 +1159,7 @@ int rproc_boot(struct rproc *rproc) } /* prevent underlying implementation from being removed */ - if (!try_module_get(dev->driver->owner)) { + if (!try_module_get(dev->parent->driver->owner)) { dev_err(dev, "%s: can't get owner\n", __func__); ret = -EINVAL; goto unlock_mutex; @@ -1181,7 +1186,7 @@ int rproc_boot(struct rproc *rproc) downref_rproc: if (ret) { - module_put(dev->driver->owner); + module_put(dev->parent->driver->owner); atomic_dec(&rproc->power); } unlock_mutex: @@ -1215,7 +1220,7 @@ EXPORT_SYMBOL(rproc_boot); */ void rproc_shutdown(struct rproc *rproc) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; int ret; ret = mutex_lock_interruptible(&rproc->lock); @@ -1248,7 +1253,7 @@ void rproc_shutdown(struct rproc *rproc) out: mutex_unlock(&rproc->lock); if (!ret) - module_put(dev->driver->owner); + module_put(dev->parent->driver->owner); } EXPORT_SYMBOL(rproc_shutdown); @@ -1271,7 +1276,7 @@ void rproc_release(struct kref *kref) { struct rproc *rproc = container_of(kref, struct rproc, refcount); - dev_info(rproc->dev, "removing %s\n", rproc->name); + dev_info(&rproc->dev, "removing %s\n", rproc->name); rproc_delete_debug_dir(rproc); @@ -1403,13 +1408,17 @@ EXPORT_SYMBOL(rproc_put); */ int rproc_register(struct rproc *rproc) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; int ret = 0; + ret = device_add(dev); + if (ret < 0) + return ret; + /* expose to rproc_get_by_name users */ klist_add_tail(&rproc->node, &rprocs); - dev_info(rproc->dev, "%s is available\n", rproc->name); + dev_info(dev, "%s is available\n", rproc->name); dev_info(dev, "Note: remoteproc is still under development and considered experimental.\n"); dev_info(dev, "THE BINARY FORMAT IS NOT YET FINALIZED, and backward compatibility isn't yet guaranteed.\n"); @@ -1441,6 +1450,33 @@ int rproc_register(struct rproc *rproc) } EXPORT_SYMBOL(rproc_register); +/** + * rproc_type_release() - release a remote processor instance + * @dev: the rproc's device + * + * This function should _never_ be called directly. + * + * It will be called by the driver core when no one holds a valid pointer + * to @dev anymore. + */ +static void rproc_type_release(struct device *dev) +{ + struct rproc *rproc = container_of(dev, struct rproc, dev); + + idr_remove_all(&rproc->notifyids); + idr_destroy(&rproc->notifyids); + + if (rproc->index >= 0) + ida_simple_remove(&rproc_dev_index, rproc->index); + + kfree(rproc); +} + +static struct device_type rproc_type = { + .name = "remoteproc", + .release = rproc_type_release, +}; + /** * rproc_alloc() - allocate a remote processor handle * @dev: the underlying device @@ -1479,12 +1515,25 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, return NULL; } - rproc->dev = dev; rproc->name = name; rproc->ops = ops; rproc->firmware = firmware; rproc->priv = &rproc[1]; + device_initialize(&rproc->dev); + rproc->dev.parent = dev; + rproc->dev.type = &rproc_type; + + /* Assign a unique device index and name */ + rproc->index = ida_simple_get(&rproc_dev_index, 0, 0, GFP_KERNEL); + if (rproc->index < 0) { + dev_err(dev, "ida_simple_get failed: %d\n", rproc->index); + put_device(&rproc->dev); + return NULL; + } + + dev_set_name(&rproc->dev, "remoteproc%d", rproc->index); + atomic_set(&rproc->power, 0); kref_init(&rproc->refcount); @@ -1516,10 +1565,7 @@ EXPORT_SYMBOL(rproc_alloc); */ void rproc_free(struct rproc *rproc) { - idr_remove_all(&rproc->notifyids); - idr_destroy(&rproc->notifyids); - - kfree(rproc); + put_device(&rproc->dev); } EXPORT_SYMBOL(rproc_free); @@ -1560,6 +1606,8 @@ int rproc_unregister(struct rproc *rproc) /* the rproc is downref'ed as soon as it's removed from the klist */ klist_del(&rproc->node); + device_del(&rproc->dev); + /* the rproc will only be released after its refcount drops to zero */ kref_put(&rproc->refcount, rproc_release); @@ -1570,6 +1618,7 @@ EXPORT_SYMBOL(rproc_unregister); static int __init remoteproc_init(void) { rproc_init_debugfs(); + return 0; } module_init(remoteproc_init); diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index 85d31a69e117..03833850f214 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -124,7 +124,7 @@ struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, tfile = debugfs_create_file(name, 0400, rproc->dbg_dir, trace, &trace_rproc_ops); if (!tfile) { - dev_err(rproc->dev, "failed to create debugfs trace entry\n"); + dev_err(&rproc->dev, "failed to create debugfs trace entry\n"); return NULL; } @@ -141,7 +141,7 @@ void rproc_delete_debug_dir(struct rproc *rproc) void rproc_create_debug_dir(struct rproc *rproc) { - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; if (!rproc_dbg) return; diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index 26a7144e7f3b..b6621831a58a 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -36,7 +36,7 @@ static void rproc_virtio_notify(struct virtqueue *vq) struct rproc *rproc = rvring->rvdev->rproc; int notifyid = rvring->notifyid; - dev_dbg(rproc->dev, "kicking vq index: %d\n", notifyid); + dev_dbg(&rproc->dev, "kicking vq index: %d\n", notifyid); rproc->ops->kick(rproc, notifyid); } @@ -57,7 +57,7 @@ irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid) { struct rproc_vring *rvring; - dev_dbg(rproc->dev, "vq index %d is interrupted\n", notifyid); + dev_dbg(&rproc->dev, "vq index %d is interrupted\n", notifyid); rvring = idr_find(&rproc->notifyids, notifyid); if (!rvring || !rvring->vq) @@ -74,6 +74,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, { struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); struct rproc *rproc = vdev_to_rproc(vdev); + struct device *dev = &rproc->dev; struct rproc_vring *rvring; struct virtqueue *vq; void *addr; @@ -95,7 +96,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, size = vring_size(len, rvring->align); memset(addr, 0, size); - dev_dbg(rproc->dev, "vring%d: va %p qsz %d notifyid %d\n", + dev_dbg(dev, "vring%d: va %p qsz %d notifyid %d\n", id, addr, len, rvring->notifyid); /* @@ -105,7 +106,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, vq = vring_new_virtqueue(len, rvring->align, vdev, false, addr, rproc_virtio_notify, callback, name); if (!vq) { - dev_err(rproc->dev, "vring_new_virtqueue %s failed\n", name); + dev_err(dev, "vring_new_virtqueue %s failed\n", name); rproc_free_vring(rvring); return ERR_PTR(-ENOMEM); } @@ -152,7 +153,7 @@ static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, /* now that the vqs are all set, boot the remote processor */ ret = rproc_boot(rproc); if (ret) { - dev_err(rproc->dev, "rproc_boot() failed %d\n", ret); + dev_err(&rproc->dev, "rproc_boot() failed %d\n", ret); goto error; } @@ -254,7 +255,7 @@ static void rproc_vdev_release(struct device *dev) int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) { struct rproc *rproc = rvdev->rproc; - struct device *dev = rproc->dev; + struct device *dev = &rproc->dev; struct virtio_device *vdev = &rvdev->vdev; int ret; diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 75506ec2840e..691e52ec48d9 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -911,7 +911,8 @@ static int rpmsg_probe(struct virtio_device *vdev) vrp->svq = vqs[1]; /* allocate coherent memory for the buffers */ - bufs_va = dma_alloc_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE, + bufs_va = dma_alloc_coherent(vdev->dev.parent->parent, + RPMSG_TOTAL_BUF_SPACE, &vrp->bufs_dma, GFP_KERNEL); if (!bufs_va) goto vqs_del; diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index f1ffabb978d3..7f806dcf5278 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -369,7 +369,7 @@ enum rproc_state { * @firmware: name of firmware file to be loaded * @priv: private data which belongs to the platform-specific rproc module * @ops: platform-specific start/stop rproc handlers - * @dev: underlying device + * @dev: virtual device for refcounting and common remoteproc behavior * @refcount: refcount of users that have a valid pointer to this rproc * @power: refcount of users who need this rproc powered up * @state: state of the device @@ -383,6 +383,7 @@ enum rproc_state { * @bootaddr: address of first instruction to boot rproc with (optional) * @rvdevs: list of remote virtio devices * @notifyids: idr for dynamically assigning rproc-wide unique notify ids + * @index: index of this rproc device */ struct rproc { struct klist_node node; @@ -391,7 +392,7 @@ struct rproc { const char *firmware; void *priv; const struct rproc_ops *ops; - struct device *dev; + struct device dev; struct kref refcount; atomic_t power; unsigned int state; @@ -405,6 +406,7 @@ struct rproc { u32 bootaddr; struct list_head rvdevs; struct idr notifyids; + int index; }; /* we currently support only two vrings per rvdev */ -- cgit v1.2.3 From 7183a2a799b81490354973117ecd810c23cdc668 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Wed, 30 May 2012 22:02:24 +0300 Subject: remoteproc: remove the now-redundant kref Now that every rproc instance contains a device, we don't need a kref anymore to maintain the refcount of the rproc instances: that's what device are good with! This patch removes the now-redundant kref, and switches to {get, put}_device instead of kref_{get, put}. We also don't need the kref's release function anymore, and instead, we just utilize the class's release handler (which is now responsible for all memory de-allocations). Cc: Stephen Boyd Cc: Fernando Guzman Lugo Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/remoteproc_core.c | 50 ++++++++-------------------------- drivers/remoteproc/remoteproc_virtio.c | 8 +++--- include/linux/remoteproc.h | 3 -- 3 files changed, 15 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 25f937843836..aa713aade30e 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -1257,42 +1257,12 @@ out: } EXPORT_SYMBOL(rproc_shutdown); -/** - * rproc_release() - completely deletes the existence of a remote processor - * @kref: the rproc's kref - * - * This function should _never_ be called directly. - * - * The only reasonable location to use it is as an argument when kref_put'ing - * @rproc's refcount. - * - * This way it will be called when no one holds a valid pointer to this @rproc - * anymore (and obviously after it is removed from the rprocs klist). - * - * Note: this function is not static because rproc_vdev_release() needs it when - * it decrements @rproc's refcount. - */ -void rproc_release(struct kref *kref) -{ - struct rproc *rproc = container_of(kref, struct rproc, refcount); - - dev_info(&rproc->dev, "removing %s\n", rproc->name); - - rproc_delete_debug_dir(rproc); - - /* - * At this point no one holds a reference to rproc anymore, - * so we can directly unroll rproc_alloc() - */ - rproc_free(rproc); -} - /* will be called when an rproc is added to the rprocs klist */ static void klist_rproc_get(struct klist_node *n) { struct rproc *rproc = container_of(n, struct rproc, node); - kref_get(&rproc->refcount); + get_device(&rproc->dev); } /* will be called when an rproc is removed from the rprocs klist */ @@ -1300,7 +1270,7 @@ static void klist_rproc_put(struct klist_node *n) { struct rproc *rproc = container_of(n, struct rproc, node); - kref_put(&rproc->refcount, rproc_release); + put_device(&rproc->dev); } static struct rproc *next_rproc(struct klist_iter *i) @@ -1342,7 +1312,7 @@ struct rproc *rproc_get_by_name(const char *name) klist_iter_init(&rprocs, &i); while ((rproc = next_rproc(&i)) != NULL) if (!strcmp(rproc->name, name)) { - kref_get(&rproc->refcount); + get_device(&rproc->dev); break; } klist_iter_exit(&i); @@ -1355,7 +1325,7 @@ struct rproc *rproc_get_by_name(const char *name) ret = rproc_boot(rproc); if (ret < 0) { - kref_put(&rproc->refcount, rproc_release); + put_device(&rproc->dev); return NULL; } @@ -1382,7 +1352,7 @@ void rproc_put(struct rproc *rproc) rproc_shutdown(rproc); /* downref rproc's refcount */ - kref_put(&rproc->refcount, rproc_release); + put_device(&rproc->dev); } EXPORT_SYMBOL(rproc_put); @@ -1463,6 +1433,10 @@ static void rproc_type_release(struct device *dev) { struct rproc *rproc = container_of(dev, struct rproc, dev); + dev_info(&rproc->dev, "releasing %s\n", rproc->name); + + rproc_delete_debug_dir(rproc); + idr_remove_all(&rproc->notifyids); idr_destroy(&rproc->notifyids); @@ -1536,8 +1510,6 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, atomic_set(&rproc->power, 0); - kref_init(&rproc->refcount); - mutex_init(&rproc->lock); idr_init(&rproc->notifyids); @@ -1608,8 +1580,8 @@ int rproc_unregister(struct rproc *rproc) device_del(&rproc->dev); - /* the rproc will only be released after its refcount drops to zero */ - kref_put(&rproc->refcount, rproc_release); + /* unroll rproc_alloc. TODO: we may want to let the users do that */ + put_device(&rproc->dev); return 0; } diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index b6621831a58a..3541b4492f64 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -225,7 +225,7 @@ static struct virtio_config_ops rproc_virtio_config_ops = { /* * This function is called whenever vdev is released, and is responsible - * to decrement the remote processor's refcount taken when vdev was + * to decrement the remote processor's refcount which was taken when vdev was * added. * * Never call this function directly; it will be called by the driver @@ -240,7 +240,7 @@ static void rproc_vdev_release(struct device *dev) list_del(&rvdev->node); kfree(rvdev); - kref_put(&rproc->refcount, rproc_release); + put_device(&rproc->dev); } /** @@ -272,11 +272,11 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) * Therefore we must increment the rproc refcount here, and decrement * it _only_ when the vdev is released. */ - kref_get(&rproc->refcount); + get_device(&rproc->dev); ret = register_virtio_device(vdev); if (ret) { - kref_put(&rproc->refcount, rproc_release); + put_device(&rproc->dev); dev_err(dev, "failed to register vdev: %d\n", ret); goto out; } diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 7f806dcf5278..cbe8a51a21de 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -36,7 +36,6 @@ #define REMOTEPROC_H #include -#include #include #include #include @@ -370,7 +369,6 @@ enum rproc_state { * @priv: private data which belongs to the platform-specific rproc module * @ops: platform-specific start/stop rproc handlers * @dev: virtual device for refcounting and common remoteproc behavior - * @refcount: refcount of users that have a valid pointer to this rproc * @power: refcount of users who need this rproc powered up * @state: state of the device * @lock: lock which protects concurrent manipulations of the rproc @@ -393,7 +391,6 @@ struct rproc { void *priv; const struct rproc_ops *ops; struct device dev; - struct kref refcount; atomic_t power; unsigned int state; struct mutex lock; -- cgit v1.2.3 From 40e575b1d0b34b38519d361c10bdf8e0c688957b Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Mon, 2 Jul 2012 20:20:53 +0300 Subject: remoteproc: remove the get_by_name/put API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove rproc_get_by_name() and rproc_put(), and the associated remoteproc infrastructure that supports it (i.e. klist and friends), because: 1. No one uses them 2. Using them is highly discouraged, and any potential user will be deeply scrutinized and encouraged to move. If a user, that absolutely can't live with the direct boot/shutdown model, does show up one day, then bringing this functionality back is going to be trivial. At this point though, keeping this functionality around is way too much of a maintenance burden. Cc: Sjur Brændeland Cc: Loic Pallardy Cc: Ludovic BARRE Cc: Michal Simek Cc: Fernando Guzman Lugo Cc: Suman Anna Cc: Mark Grosen Acked-by: Stephen Boyd Signed-off-by: Ohad Ben-Cohen --- Documentation/remoteproc.txt | 27 +------- drivers/remoteproc/remoteproc_core.c | 130 ----------------------------------- include/linux/remoteproc.h | 3 - 3 files changed, 1 insertion(+), 159 deletions(-) (limited to 'include') diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt index ad6ded4bca5c..f33c3bbbc867 100644 --- a/Documentation/remoteproc.txt +++ b/Documentation/remoteproc.txt @@ -36,8 +36,7 @@ cost. Note: to use this function you should already have a valid rproc handle. There are several ways to achieve that cleanly (devres, pdata, the way remoteproc_rpmsg.c does this, or, if this becomes prevalent, we - might also consider using dev_archdata for this). See also - rproc_get_by_name() below. + might also consider using dev_archdata for this). void rproc_shutdown(struct rproc *rproc) - Power off a remote processor (previously booted with rproc_boot()). @@ -51,30 +50,6 @@ cost. which means that the @rproc handle stays valid even after rproc_shutdown() returns, and users can still use it with a subsequent rproc_boot(), if needed. - - don't call rproc_shutdown() to unroll rproc_get_by_name(), exactly - because rproc_shutdown() _does not_ decrement the refcount of @rproc. - To decrement the refcount of @rproc, use rproc_put() (but _only_ if - you acquired @rproc using rproc_get_by_name()). - - struct rproc *rproc_get_by_name(const char *name) - - Find an rproc handle using the remote processor's name, and then - boot it. If it's already powered on, then just immediately return - (successfully). Returns the rproc handle on success, and NULL on failure. - This function increments the remote processor's refcount, so always - use rproc_put() to decrement it back once rproc isn't needed anymore. - Note: currently rproc_get_by_name() and rproc_put() are not used anymore - by the rpmsg bus and its drivers. We need to scrutinize the use cases - that still need them, and see if we can migrate them to use the non - name-based boot/shutdown interface. - - void rproc_put(struct rproc *rproc) - - Decrement @rproc's power refcount and shut it down if it reaches zero - (essentially by just calling rproc_shutdown), and then decrement @rproc's - validity refcount too. - After this function returns, @rproc may _not_ be used anymore, and its - handle should be considered invalid. - This function should be called _iff_ the @rproc handle was grabbed by - calling rproc_get_by_name(). 3. Typical usage diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index c85db123ba0a..0c77c4fcf436 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -44,25 +43,6 @@ #include "remoteproc_internal.h" -static void klist_rproc_get(struct klist_node *n); -static void klist_rproc_put(struct klist_node *n); - -/* - * klist of the available remote processors. - * - * We need this in order to support name-based lookups (needed by the - * rproc_get_by_name()). - * - * That said, we don't use rproc_get_by_name() at this point. - * The use cases that do require its existence should be - * scrutinized, and hopefully migrated to rproc_boot() using device-based - * binding. - * - * If/when this materializes, we could drop the klist (and the by_name - * API). - */ -static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put); - typedef int (*rproc_handle_resources_t)(struct rproc *rproc, struct resource_table *table, int len); typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail); @@ -1217,10 +1197,6 @@ EXPORT_SYMBOL(rproc_boot); * which means that the @rproc handle stays valid even after rproc_shutdown() * returns, and users can still use it with a subsequent rproc_boot(), if * needed. - * - don't call rproc_shutdown() to unroll rproc_get_by_name(), exactly - * because rproc_shutdown() _does not_ decrement the refcount of @rproc. - * To decrement the refcount of @rproc, use rproc_put() (but _only_ if - * you acquired @rproc using rproc_get_by_name()). */ void rproc_shutdown(struct rproc *rproc) { @@ -1261,105 +1237,6 @@ out: } EXPORT_SYMBOL(rproc_shutdown); -/* will be called when an rproc is added to the rprocs klist */ -static void klist_rproc_get(struct klist_node *n) -{ - struct rproc *rproc = container_of(n, struct rproc, node); - - get_device(&rproc->dev); -} - -/* will be called when an rproc is removed from the rprocs klist */ -static void klist_rproc_put(struct klist_node *n) -{ - struct rproc *rproc = container_of(n, struct rproc, node); - - put_device(&rproc->dev); -} - -static struct rproc *next_rproc(struct klist_iter *i) -{ - struct klist_node *n; - - n = klist_next(i); - if (!n) - return NULL; - - return container_of(n, struct rproc, node); -} - -/** - * rproc_get_by_name() - find a remote processor by name and boot it - * @name: name of the remote processor - * - * Finds an rproc handle using the remote processor's name, and then - * boot it. If it's already powered on, then just immediately return - * (successfully). - * - * Returns the rproc handle on success, and NULL on failure. - * - * This function increments the remote processor's refcount, so always - * use rproc_put() to decrement it back once rproc isn't needed anymore. - * - * Note: currently this function (and its counterpart rproc_put()) are not - * being used. We need to scrutinize the use cases - * that still need them, and see if we can migrate them to use the non - * name-based boot/shutdown interface. - */ -struct rproc *rproc_get_by_name(const char *name) -{ - struct rproc *rproc; - struct klist_iter i; - int ret; - - /* find the remote processor, and upref its refcount */ - klist_iter_init(&rprocs, &i); - while ((rproc = next_rproc(&i)) != NULL) - if (!strcmp(rproc->name, name)) { - get_device(&rproc->dev); - break; - } - klist_iter_exit(&i); - - /* can't find this rproc ? */ - if (!rproc) { - pr_err("can't find remote processor %s\n", name); - return NULL; - } - - ret = rproc_boot(rproc); - if (ret < 0) { - put_device(&rproc->dev); - return NULL; - } - - return rproc; -} -EXPORT_SYMBOL(rproc_get_by_name); - -/** - * rproc_put() - decrement the refcount of a remote processor, and shut it down - * @rproc: the remote processor - * - * This function tries to shutdown @rproc, and it then decrements its - * refcount. - * - * After this function returns, @rproc may _not_ be used anymore, and its - * handle should be considered invalid. - * - * This function should be called _iff_ the @rproc handle was grabbed by - * calling rproc_get_by_name(). - */ -void rproc_put(struct rproc *rproc) -{ - /* try to power off the remote processor */ - rproc_shutdown(rproc); - - /* downref rproc's refcount */ - put_device(&rproc->dev); -} -EXPORT_SYMBOL(rproc_put); - /** * rproc_register() - register a remote processor * @rproc: the remote processor handle to register @@ -1389,9 +1266,6 @@ int rproc_register(struct rproc *rproc) if (ret < 0) return ret; - /* expose to rproc_get_by_name users */ - klist_add_tail(&rproc->node, &rprocs); - dev_info(dev, "%s is available\n", rproc->name); dev_info(dev, "Note: remoteproc is still under development and considered experimental.\n"); @@ -1417,7 +1291,6 @@ int rproc_register(struct rproc *rproc) if (ret < 0) { dev_err(dev, "request_firmware_nowait failed: %d\n", ret); complete_all(&rproc->firmware_loading_complete); - klist_remove(&rproc->node); } return ret; @@ -1573,9 +1446,6 @@ int rproc_unregister(struct rproc *rproc) list_for_each_entry_safe(rvdev, tmp, &rproc->rvdevs, node) rproc_remove_virtio_dev(rvdev); - /* the rproc is downref'ed as soon as it's removed from the klist */ - klist_del(&rproc->node); - device_del(&rproc->dev); return 0; diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index cbe8a51a21de..b88d6af5ba52 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -449,9 +449,6 @@ struct rproc_vdev { unsigned long gfeatures; }; -struct rproc *rproc_get_by_name(const char *name); -void rproc_put(struct rproc *rproc); - struct rproc *rproc_alloc(struct device *dev, const char *name, const struct rproc_ops *ops, const char *firmware, int len); -- cgit v1.2.3 From 160e7c840fe85836040c43e0058d5afced470c85 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Wed, 4 Jul 2012 16:25:06 +0300 Subject: remoteproc: adopt the driver core's alloc/add/del/put naming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To make remoteproc's API more intuitive for developers, we adopt the driver core's naming, i.e. alloc -> add -> del -> put. We'll also add register/unregister when their first user shows up. Otherwise - there's no functional change here. Suggested by Russell King . Cc: Russell King Cc: Fernando Guzman Lugo Cc: Sjur Brændeland Reviewed-by: Linus Walleij Acked-by: Stephen Boyd Signed-off-by: Ohad Ben-Cohen --- Documentation/remoteproc.txt | 18 +++++++++--------- drivers/remoteproc/omap_remoteproc.c | 8 ++++---- drivers/remoteproc/remoteproc_core.c | 32 ++++++++++++++++---------------- include/linux/remoteproc.h | 6 +++--- 4 files changed, 32 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt index f33c3bbbc867..23a09b884bc7 100644 --- a/Documentation/remoteproc.txt +++ b/Documentation/remoteproc.txt @@ -90,21 +90,21 @@ int dummy_rproc_example(struct rproc *my_rproc) This function should be used by rproc implementations during initialization of the remote processor. After creating an rproc handle using this function, and when ready, - implementations should then call rproc_register() to complete + implementations should then call rproc_add() to complete the registration of the remote processor. On success, the new rproc is returned, and on failure, NULL. Note: _never_ directly deallocate @rproc, even if it was not registered - yet. Instead, when you need to unroll rproc_alloc(), use rproc_free(). + yet. Instead, when you need to unroll rproc_alloc(), use rproc_put(). - void rproc_free(struct rproc *rproc) + void rproc_put(struct rproc *rproc) - Free an rproc handle that was allocated by rproc_alloc. This function essentially unrolls rproc_alloc(), by decrementing the rproc's refcount. It doesn't directly free rproc; that would happen only if there are no other references to rproc and its refcount now dropped to zero. - int rproc_register(struct rproc *rproc) + int rproc_add(struct rproc *rproc) - Register @rproc with the remoteproc framework, after it has been allocated with rproc_alloc(). This is called by the platform-specific rproc implementation, whenever @@ -117,15 +117,15 @@ int dummy_rproc_example(struct rproc *my_rproc) of registering this remote processor, additional virtio drivers might get probed. - int rproc_unregister(struct rproc *rproc) - - Unroll rproc_register(). + int rproc_del(struct rproc *rproc) + - Unroll rproc_add(). This function should be called when the platform specific rproc implementation decides to remove the rproc device. it should - _only_ be called if a previous invocation of rproc_register() + _only_ be called if a previous invocation of rproc_add() has completed successfully. - After rproc_unregister() returns, @rproc is still valid, and its - last refcount should be decremented by calling rproc_free(). + After rproc_del() returns, @rproc is still valid, and its + last refcount should be decremented by calling rproc_put(). Returns 0 on success and -EINVAL if @rproc isn't valid. diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index 4f2fe8fd7fe6..02bae3a5264f 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -199,14 +199,14 @@ static int __devinit omap_rproc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rproc); - ret = rproc_register(rproc); + ret = rproc_add(rproc); if (ret) goto free_rproc; return 0; free_rproc: - rproc_free(rproc); + rproc_put(rproc); return ret; } @@ -214,8 +214,8 @@ static int __devexit omap_rproc_remove(struct platform_device *pdev) { struct rproc *rproc = platform_get_drvdata(pdev); - rproc_unregister(rproc); - rproc_free(rproc); + rproc_del(rproc); + rproc_put(rproc); return 0; } diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 0c77c4fcf436..25fd9733d5df 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -1101,7 +1101,7 @@ static void rproc_fw_config_virtio(const struct firmware *fw, void *context) out: release_firmware(fw); - /* allow rproc_unregister() contexts, if any, to proceed */ + /* allow rproc_del() contexts, if any, to proceed */ complete_all(&rproc->firmware_loading_complete); } @@ -1238,7 +1238,7 @@ out: EXPORT_SYMBOL(rproc_shutdown); /** - * rproc_register() - register a remote processor + * rproc_add() - register a remote processor * @rproc: the remote processor handle to register * * Registers @rproc with the remoteproc framework, after it has been @@ -1257,7 +1257,7 @@ EXPORT_SYMBOL(rproc_shutdown); * of registering this remote processor, additional virtio drivers might be * probed. */ -int rproc_register(struct rproc *rproc) +int rproc_add(struct rproc *rproc) { struct device *dev = &rproc->dev; int ret = 0; @@ -1274,7 +1274,7 @@ int rproc_register(struct rproc *rproc) /* create debugfs entries */ rproc_create_debug_dir(rproc); - /* rproc_unregister() calls must wait until async loader completes */ + /* rproc_del() calls must wait until async loader completes */ init_completion(&rproc->firmware_loading_complete); /* @@ -1295,7 +1295,7 @@ int rproc_register(struct rproc *rproc) return ret; } -EXPORT_SYMBOL(rproc_register); +EXPORT_SYMBOL(rproc_add); /** * rproc_type_release() - release a remote processor instance @@ -1343,13 +1343,13 @@ static struct device_type rproc_type = { * of the remote processor. * * After creating an rproc handle using this function, and when ready, - * implementations should then call rproc_register() to complete + * implementations should then call rproc_add() to complete * the registration of the remote processor. * * On success the new rproc is returned, and on failure, NULL. * * Note: _never_ directly deallocate @rproc, even if it was not registered - * yet. Instead, when you need to unroll rproc_alloc(), use rproc_free(). + * yet. Instead, when you need to unroll rproc_alloc(), use rproc_put(). */ struct rproc *rproc_alloc(struct device *dev, const char *name, const struct rproc_ops *ops, @@ -1403,7 +1403,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, EXPORT_SYMBOL(rproc_alloc); /** - * rproc_free() - unroll rproc_alloc() + * rproc_put() - unroll rproc_alloc() * @rproc: the remote processor handle * * This function decrements the rproc dev refcount. @@ -1411,28 +1411,28 @@ EXPORT_SYMBOL(rproc_alloc); * If no one holds any reference to rproc anymore, then its refcount would * now drop to zero, and it would be freed. */ -void rproc_free(struct rproc *rproc) +void rproc_put(struct rproc *rproc) { put_device(&rproc->dev); } -EXPORT_SYMBOL(rproc_free); +EXPORT_SYMBOL(rproc_put); /** - * rproc_unregister() - unregister a remote processor + * rproc_del() - unregister a remote processor * @rproc: rproc handle to unregister * * This function should be called when the platform specific rproc * implementation decides to remove the rproc device. it should - * _only_ be called if a previous invocation of rproc_register() + * _only_ be called if a previous invocation of rproc_add() * has completed successfully. * - * After rproc_unregister() returns, @rproc isn't freed yet, because + * After rproc_del() returns, @rproc isn't freed yet, because * of the outstanding reference created by rproc_alloc. To decrement that - * one last refcount, one still needs to call rproc_free(). + * one last refcount, one still needs to call rproc_put(). * * Returns 0 on success and -EINVAL if @rproc isn't valid. */ -int rproc_unregister(struct rproc *rproc) +int rproc_del(struct rproc *rproc) { struct rproc_vdev *rvdev, *tmp; @@ -1450,7 +1450,7 @@ int rproc_unregister(struct rproc *rproc) return 0; } -EXPORT_SYMBOL(rproc_unregister); +EXPORT_SYMBOL(rproc_del); static int __init remoteproc_init(void) { diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index b88d6af5ba52..eea3ac86b2b7 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -452,9 +452,9 @@ struct rproc_vdev { struct rproc *rproc_alloc(struct device *dev, const char *name, const struct rproc_ops *ops, const char *firmware, int len); -void rproc_free(struct rproc *rproc); -int rproc_register(struct rproc *rproc); -int rproc_unregister(struct rproc *rproc); +void rproc_put(struct rproc *rproc); +int rproc_add(struct rproc *rproc); +int rproc_del(struct rproc *rproc); int rproc_boot(struct rproc *rproc); void rproc_shutdown(struct rproc *rproc); -- cgit v1.2.3 From e27947c767f5bed15048f4e4dad3e2eb69133697 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 23 May 2012 14:35:23 -0500 Subject: libceph: define and use an explicit CONNECTED state There is no state explicitly defined when a ceph connection is fully operational. So define one. It's set when the connection sequence completes successfully, and is cleared when the connection gets closed. Be a little more careful when examining the old state when a socket disconnect event is reported. Signed-off-by: Alex Elder Reviewed-by: Sage Weil --- include/linux/ceph/messenger.h | 1 + net/ceph/messenger.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index cc6f9bdcf466..002d504df3b7 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -120,6 +120,7 @@ struct ceph_msg_pos { */ #define CONNECTING 1 #define NEGOTIATING 2 +#define CONNECTED 5 #define STANDBY 8 /* no outgoing messages, socket closed. we keep * the ceph_connection around to maintain shared * state with the peer. */ diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 500207bad5d6..83bcf977e9b9 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -463,6 +463,7 @@ void ceph_con_close(struct ceph_connection *con) ceph_pr_addr(&con->peer_addr.in_addr)); clear_bit(NEGOTIATING, &con->state); clear_bit(CONNECTING, &con->state); + clear_bit(CONNECTED, &con->state); clear_bit(STANDBY, &con->state); /* avoid connect_seq bump */ set_bit(CLOSED, &con->state); @@ -1564,6 +1565,7 @@ static int process_connect(struct ceph_connection *con) } clear_bit(NEGOTIATING, &con->state); clear_bit(CONNECTING, &con->state); + set_bit(CONNECTED, &con->state); con->peer_global_seq = le32_to_cpu(con->in_reply.global_seq); con->connect_seq++; con->peer_features = server_feat; @@ -2114,6 +2116,7 @@ more: prepare_read_ack(con); break; case CEPH_MSGR_TAG_CLOSE: + clear_bit(CONNECTED, &con->state); set_bit(CLOSED, &con->state); /* fixme */ goto out; default: @@ -2190,11 +2193,13 @@ static void con_work(struct work_struct *work) mutex_lock(&con->mutex); restart: if (test_and_clear_bit(SOCK_CLOSED, &con->flags)) { - if (test_and_clear_bit(CONNECTING, &con->state)) { + if (test_and_clear_bit(CONNECTED, &con->state)) + con->error_msg = "socket closed"; + else if (test_and_clear_bit(CONNECTING, &con->state)) { clear_bit(NEGOTIATING, &con->state); con->error_msg = "connection failed"; } else { - con->error_msg = "socket closed"; + con->error_msg = "unrecognized con state"; } goto fault; } -- cgit v1.2.3 From 261030215d970c62f799e6e508e3c68fc7ec2aa9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 21 Jun 2012 12:49:23 -0700 Subject: libceph: drop declaration of ceph_con_get() For some reason the declaration of ceph_con_get() and ceph_con_put() did not get deleted in this commit: d59315ca libceph: drop ceph_con_get/put helpers and nref member Clean that up. Signed-off-by: Alex Elder --- include/linux/ceph/messenger.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 002d504df3b7..dd4ef1f8ec93 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -244,8 +244,6 @@ extern void ceph_msg_revoke(struct ceph_msg *msg); extern void ceph_msg_revoke_incoming(struct ceph_msg *msg); extern void ceph_con_keepalive(struct ceph_connection *con); -extern struct ceph_connection *ceph_con_get(struct ceph_connection *con); -extern void ceph_con_put(struct ceph_connection *con); extern struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags, bool can_fail); -- cgit v1.2.3 From b7a9e5dd40f17a48a72f249b8bbc989b63bae5fd Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 27 Jun 2012 12:24:08 -0700 Subject: libceph: set peer name on con_open, not init The peer name may change on each open attempt, even when the connection is reused. Signed-off-by: Sage Weil --- fs/ceph/mds_client.c | 7 ++++--- include/linux/ceph/messenger.h | 4 ++-- net/ceph/messenger.c | 12 +++++++----- net/ceph/mon_client.c | 4 ++-- net/ceph/osd_client.c | 10 ++++++---- 5 files changed, 21 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index ecd7f15741c1..5ac6434185ae 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -394,8 +394,7 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, s->s_seq = 0; mutex_init(&s->s_mutex); - ceph_con_init(&s->s_con, s, &mds_con_ops, &mdsc->fsc->client->msgr, - CEPH_ENTITY_TYPE_MDS, mds); + ceph_con_init(&s->s_con, s, &mds_con_ops, &mdsc->fsc->client->msgr); spin_lock_init(&s->s_gen_ttl_lock); s->s_cap_gen = 0; @@ -437,7 +436,8 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc, mdsc->sessions[mds] = s; atomic_inc(&s->s_ref); /* one ref to sessions[], one to caller */ - ceph_con_open(&s->s_con, ceph_mdsmap_get_addr(mdsc->mdsmap, mds)); + ceph_con_open(&s->s_con, CEPH_ENTITY_TYPE_MDS, mds, + ceph_mdsmap_get_addr(mdsc->mdsmap, mds)); return s; @@ -2529,6 +2529,7 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, session->s_seq = 0; ceph_con_open(&session->s_con, + CEPH_ENTITY_TYPE_MDS, mds, ceph_mdsmap_get_addr(mdsc->mdsmap, mds)); /* replay unsafe requests */ diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index dd4ef1f8ec93..478f814f2100 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -232,9 +232,9 @@ extern void ceph_messenger_init(struct ceph_messenger *msgr, extern void ceph_con_init(struct ceph_connection *con, void *private, const struct ceph_connection_operations *ops, - struct ceph_messenger *msgr, __u8 entity_type, - __u64 entity_num); + struct ceph_messenger *msgr); extern void ceph_con_open(struct ceph_connection *con, + __u8 entity_type, __u64 entity_num, struct ceph_entity_addr *addr); extern bool ceph_con_opened(struct ceph_connection *con); extern void ceph_con_close(struct ceph_connection *con); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index dcc50e4cd5cd..ae082d95fc72 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -523,12 +523,17 @@ EXPORT_SYMBOL(ceph_con_close); /* * Reopen a closed connection, with a new peer address. */ -void ceph_con_open(struct ceph_connection *con, struct ceph_entity_addr *addr) +void ceph_con_open(struct ceph_connection *con, + __u8 entity_type, __u64 entity_num, + struct ceph_entity_addr *addr) { dout("con_open %p %s\n", con, ceph_pr_addr(&addr->in_addr)); set_bit(OPENING, &con->state); WARN_ON(!test_and_clear_bit(CLOSED, &con->state)); + con->peer_name.type = (__u8) entity_type; + con->peer_name.num = cpu_to_le64(entity_num); + memcpy(&con->peer_addr, addr, sizeof(*addr)); con->delay = 0; /* reset backoff memory */ queue_con(con); @@ -548,7 +553,7 @@ bool ceph_con_opened(struct ceph_connection *con) */ void ceph_con_init(struct ceph_connection *con, void *private, const struct ceph_connection_operations *ops, - struct ceph_messenger *msgr, __u8 entity_type, __u64 entity_num) + struct ceph_messenger *msgr) { dout("con_init %p\n", con); memset(con, 0, sizeof(*con)); @@ -558,9 +563,6 @@ void ceph_con_init(struct ceph_connection *con, void *private, con_sock_state_init(con); - con->peer_name.type = (__u8) entity_type; - con->peer_name.num = cpu_to_le64(entity_num); - mutex_init(&con->mutex); INIT_LIST_HEAD(&con->out_queue); INIT_LIST_HEAD(&con->out_sent); diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index e9db3de20b2e..bcc80a0e2a98 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -143,11 +143,11 @@ static int __open_session(struct ceph_mon_client *monc) monc->want_next_osdmap = !!monc->want_next_osdmap; ceph_con_init(&monc->con, monc, &mon_con_ops, - &monc->client->msgr, - CEPH_ENTITY_TYPE_MON, monc->cur_mon); + &monc->client->msgr); dout("open_session mon%d opening\n", monc->cur_mon); ceph_con_open(&monc->con, + CEPH_ENTITY_TYPE_MON, monc->cur_mon, &monc->monmap->mon_inst[monc->cur_mon].addr); /* initiatiate authentication handshake */ diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index db2da54f7336..c2527113d2ae 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -639,8 +639,7 @@ static struct ceph_osd *create_osd(struct ceph_osd_client *osdc, int onum) INIT_LIST_HEAD(&osd->o_osd_lru); osd->o_incarnation = 1; - ceph_con_init(&osd->o_con, osd, &osd_con_ops, &osdc->client->msgr, - CEPH_ENTITY_TYPE_OSD, onum); + ceph_con_init(&osd->o_con, osd, &osd_con_ops, &osdc->client->msgr); INIT_LIST_HEAD(&osd->o_keepalive_item); return osd; @@ -750,7 +749,8 @@ static int __reset_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) ret = -EAGAIN; } else { ceph_con_close(&osd->o_con); - ceph_con_open(&osd->o_con, &osdc->osdmap->osd_addr[osd->o_osd]); + ceph_con_open(&osd->o_con, CEPH_ENTITY_TYPE_OSD, osd->o_osd, + &osdc->osdmap->osd_addr[osd->o_osd]); osd->o_incarnation++; } return ret; @@ -1005,7 +1005,9 @@ static int __map_request(struct ceph_osd_client *osdc, dout("map_request osd %p is osd%d\n", req->r_osd, o); __insert_osd(osdc, req->r_osd); - ceph_con_open(&req->r_osd->o_con, &osdc->osdmap->osd_addr[o]); + ceph_con_open(&req->r_osd->o_con, + CEPH_ENTITY_TYPE_OSD, o, + &osdc->osdmap->osd_addr[o]); } if (req->r_osd) { -- cgit v1.2.3 From f4530fa574df4d833506c53697ed1daa0d390bf4 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 5 Jul 2012 22:13:13 -0700 Subject: ipv4: Avoid overhead when no custom FIB rules are installed. If the user hasn't actually installed any custom rules, or fiddled with the default ones, don't go through the whole FIB rules layer. It's just pure overhead. Instead do what we do with CONFIG_IP_MULTIPLE_TABLES disabled, check the individual tables by hand, one by one. Also, move fib_num_tclassid_users into the ipv4 network namespace. Signed-off-by: David S. Miller --- include/net/ip_fib.h | 36 ++++++++++++++++++++++++++++++++---- include/net/netns/ipv4.h | 8 ++++++++ net/ipv4/fib_frontend.c | 27 ++++++++++++++++++++++----- net/ipv4/fib_rules.c | 12 ++++++++---- net/ipv4/fib_semantics.c | 6 +++--- 5 files changed, 73 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 3dc7c96bbeab..539c6721f810 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -220,11 +220,33 @@ extern void __net_exit fib4_rules_exit(struct net *net); extern u32 fib_rules_tclass(const struct fib_result *res); #endif -extern int fib_lookup(struct net *n, struct flowi4 *flp, struct fib_result *res); - extern struct fib_table *fib_new_table(struct net *net, u32 id); extern struct fib_table *fib_get_table(struct net *net, u32 id); +extern int __fib_lookup(struct net *net, struct flowi4 *flp, + struct fib_result *res); + +static inline int fib_lookup(struct net *net, struct flowi4 *flp, + struct fib_result *res) +{ + if (!net->ipv4.fib_has_custom_rules) { + if (net->ipv4.fib_local && + !fib_table_lookup(net->ipv4.fib_local, flp, res, + FIB_LOOKUP_NOREF)) + return 0; + if (net->ipv4.fib_main && + !fib_table_lookup(net->ipv4.fib_main, flp, res, + FIB_LOOKUP_NOREF)) + return 0; + if (net->ipv4.fib_default && + !fib_table_lookup(net->ipv4.fib_default, flp, res, + FIB_LOOKUP_NOREF)) + return 0; + return -ENETUNREACH; + } + return __fib_lookup(net, flp, res); +} + #endif /* CONFIG_IP_MULTIPLE_TABLES */ /* Exported by fib_frontend.c */ @@ -236,9 +258,15 @@ extern int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, struct in_device *idev, u32 *itag); extern void fib_select_default(struct fib_result *res); #ifdef CONFIG_IP_ROUTE_CLASSID -extern int fib_num_tclassid_users; +static inline int fib_num_tclassid_users(struct net *net) +{ + return net->ipv4.fib_num_tclassid_users; +} #else -#define fib_num_tclassid_users 0 +static inline int fib_num_tclassid_users(struct net *net) +{ + return 0; +} #endif /* Exported by fib_semantics.c */ diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 227f0cd9d3f6..599e48fa97cb 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -11,6 +11,7 @@ struct ctl_table_header; struct ipv4_devconf; struct fib_rules_ops; struct hlist_head; +struct fib_table; struct sock; struct netns_ipv4 { @@ -24,6 +25,13 @@ struct netns_ipv4 { struct ipv4_devconf *devconf_dflt; #ifdef CONFIG_IP_MULTIPLE_TABLES struct fib_rules_ops *rules_ops; + bool fib_has_custom_rules; + struct fib_table *fib_local; + struct fib_table *fib_main; + struct fib_table *fib_default; +#endif +#ifdef CONFIG_IP_ROUTE_CLASSID + int fib_num_tclassid_users; #endif struct hlist_head *fib_table_hash; struct sock *fibnl; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 3e11ea225dad..81f85716a894 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -86,6 +86,24 @@ struct fib_table *fib_new_table(struct net *net, u32 id) tb = fib_trie_table(id); if (!tb) return NULL; + + switch (id) { + case RT_TABLE_LOCAL: + net->ipv4.fib_local = tb; + break; + + case RT_TABLE_MAIN: + net->ipv4.fib_main = tb; + break; + + case RT_TABLE_DEFAULT: + net->ipv4.fib_default = tb; + break; + + default: + break; + } + h = id & (FIB_TABLE_HASHSZ - 1); hlist_add_head_rcu(&tb->tb_hlist, &net->ipv4.fib_table_hash[h]); return tb; @@ -218,10 +236,6 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb) return inet_select_addr(dev, ip_hdr(skb)->saddr, scope); } -#ifdef CONFIG_IP_ROUTE_CLASSID -int fib_num_tclassid_users __read_mostly; -#endif - /* Given (packet source, input interface) and optional (dst, oif, tos): * - (main) check, that source is valid i.e. not broadcast or our local * address. @@ -312,7 +326,7 @@ int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, { int r = secpath_exists(skb) ? 0 : IN_DEV_RPFILTER(idev); - if (!r && !fib_num_tclassid_users) { + if (!r && !fib_num_tclassid_users(dev_net(dev))) { *itag = 0; return 0; } @@ -1134,6 +1148,9 @@ static int __net_init fib_net_init(struct net *net) { int error; +#ifdef CONFIG_IP_ROUTE_CLASSID + net->ipv4.fib_num_tclassid_users = 0; +#endif error = ip_fib_net_init(net); if (error < 0) goto out; diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index b23fd952c84f..c06da93b0b70 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -54,7 +54,7 @@ u32 fib_rules_tclass(const struct fib_result *res) } #endif -int fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res) +int __fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res) { struct fib_lookup_arg arg = { .result = res, @@ -67,7 +67,7 @@ int fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res) return err; } -EXPORT_SYMBOL_GPL(fib_lookup); +EXPORT_SYMBOL_GPL(__fib_lookup); static int fib4_rule_action(struct fib_rule *rule, struct flowi *flp, int flags, struct fib_lookup_arg *arg) @@ -172,7 +172,7 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, if (tb[FRA_FLOW]) { rule4->tclassid = nla_get_u32(tb[FRA_FLOW]); if (rule4->tclassid) - fib_num_tclassid_users++; + net->ipv4.fib_num_tclassid_users++; } #endif @@ -182,6 +182,7 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, rule4->dstmask = inet_make_mask(rule4->dst_len); rule4->tos = frh->tos; + net->ipv4.fib_has_custom_rules = true; err = 0; errout: return err; @@ -189,12 +190,14 @@ errout: static void fib4_rule_delete(struct fib_rule *rule) { + struct net *net = rule->fr_net; #ifdef CONFIG_IP_ROUTE_CLASSID struct fib4_rule *rule4 = (struct fib4_rule *) rule; if (rule4->tclassid) - fib_num_tclassid_users--; + net->ipv4.fib_num_tclassid_users--; #endif + net->ipv4.fib_has_custom_rules = true; } static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, @@ -309,6 +312,7 @@ int __net_init fib4_rules_init(struct net *net) if (err < 0) goto fail; net->ipv4.rules_ops = ops; + net->ipv4.fib_has_custom_rules = false; return 0; fail: diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index c46c20b6b0b6..ae301c897a19 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -166,7 +166,7 @@ void free_fib_info(struct fib_info *fi) #ifdef CONFIG_IP_ROUTE_CLASSID change_nexthops(fi) { if (nexthop_nh->nh_tclassid) - fib_num_tclassid_users--; + fi->fib_net->ipv4.fib_num_tclassid_users--; } endfor_nexthops(fi); #endif call_rcu(&fi->rcu, free_fib_info_rcu); @@ -428,7 +428,7 @@ static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh, nla = nla_find(attrs, attrlen, RTA_FLOW); nexthop_nh->nh_tclassid = nla ? nla_get_u32(nla) : 0; if (nexthop_nh->nh_tclassid) - fib_num_tclassid_users++; + fi->fib_net->ipv4.fib_num_tclassid_users++; #endif } @@ -824,7 +824,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg) #ifdef CONFIG_IP_ROUTE_CLASSID nh->nh_tclassid = cfg->fc_flow; if (nh->nh_tclassid) - fib_num_tclassid_users++; + fi->fib_net->ipv4.fib_num_tclassid_users++; #endif #ifdef CONFIG_IP_ROUTE_MULTIPATH nh->nh_weight = 1; -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 From 19181bc50e1b8e92a7a3b3d78637c6dc5c0b5a1b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 4 Jul 2012 09:18:02 +0200 Subject: usbdevfs: Add a USBDEVFS_GET_CAPABILITIES ioctl There are a few (new) usbdevfs capabilities which an application cannot discover in any other way then checking the kernel version. There are 3 problems with this: 1) It is just not very pretty. 2) Given the tendency of enterprise distros to backport stuff it is not reliable. 3) As discussed in length on the mailinglist, USBDEVFS_URB_BULK_CONTINUATION does not work as it should when combined with USBDEVFS_URB_SHORT_NOT_OK (which is its intended use) on devices attached to an XHCI controller. So the availability of these features can be host controller dependent, making depending on them based on the kernel version not a good idea. This patch besides adding the new ioctl also adds flags for the following existing capabilities: USBDEVFS_CAP_ZERO_PACKET, available since 2.6.31 USBDEVFS_CAP_BULK_CONTINUATION, available since 2.6.32, except for XHCI USBDEVFS_CAP_NO_PACKET_SIZE_LIM, available since 3.3 Note that this patch only does not advertise the USBDEVFS_URB_BULK_CONTINUATION cap for XHCI controllers, bulk transfers with this flag set will still be accepted when submitted to XHCI controllers. Returning -EINVAL for them would break existing apps, and in most cases the troublesome scenario wrt USBDEVFS_URB_SHORT_NOT_OK urbs on XHCI controllers will never get hit, so this would break working use cases. The disadvantage of not returning -EINVAL is that cases were it is causing real trouble may go undetected / the cause of the trouble may be unclear, but this is the best we can do. Signed-off-by: Hans de Goede Acked-by: Alan Stern Acked-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 17 +++++++++++++++++ drivers/usb/host/xhci.c | 2 ++ include/linux/usb.h | 5 +++++ include/linux/usbdevice_fs.h | 7 +++++++ 4 files changed, 31 insertions(+) (limited to 'include') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 62679bc031fb..0b387c1a8b7e 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1824,6 +1824,20 @@ static int proc_release_port(struct dev_state *ps, void __user *arg) return usb_hub_release_port(ps->dev, portnum, ps); } +static int proc_get_capabilities(struct dev_state *ps, void __user *arg) +{ + __u32 caps; + + caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM; + if (!ps->dev->bus->no_stop_on_short) + caps |= USBDEVFS_CAP_BULK_CONTINUATION; + + if (put_user(caps, (__u32 __user *)arg)) + return -EFAULT; + + return 0; +} + /* * NOTE: All requests here that have interface numbers as parameters * are assuming that somehow the configuration has been prevented from @@ -1994,6 +2008,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__); ret = proc_release_port(ps, p); break; + case USBDEVFS_GET_CAPABILITIES: + ret = proc_get_capabilities(ps, p); + break; } usb_unlock_device(dev); if (ret >= 0) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index a979cd0dbe0f..7648b2d4b268 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4450,6 +4450,8 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0; + /* XHCI controllers don't stop the ep queue on short packets :| */ + hcd->self.no_stop_on_short = 1; if (usb_hcd_is_primary_hcd(hcd)) { xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL); diff --git a/include/linux/usb.h b/include/linux/usb.h index f717fbdaee8e..d4f9de1acd45 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -331,6 +331,11 @@ struct usb_bus { u8 otg_port; /* 0, or number of OTG/HNP port */ unsigned is_b_host:1; /* true during some HNP roleswitches */ unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */ + unsigned no_stop_on_short:1; /* + * Quirk: some controllers don't stop + * the ep queue on a short transfer + * with the URB_SHORT_NOT_OK flag set. + */ unsigned sg_tablesize; /* 0 or largest number of sg list entries */ int devnum_next; /* Next open device number in diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 15591d2ea400..07b2ceaaad70 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -125,6 +125,11 @@ struct usbdevfs_hub_portinfo { char port [127]; /* e.g. port 3 connects to device 27 */ }; +/* Device capability flags */ +#define USBDEVFS_CAP_ZERO_PACKET 0x01 +#define USBDEVFS_CAP_BULK_CONTINUATION 0x02 +#define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04 + #ifdef __KERNEL__ #ifdef CONFIG_COMPAT #include @@ -204,4 +209,6 @@ struct usbdevfs_ioctl32 { #define USBDEVFS_CONNECT _IO('U', 23) #define USBDEVFS_CLAIM_PORT _IOR('U', 24, unsigned int) #define USBDEVFS_RELEASE_PORT _IOR('U', 25, unsigned int) +#define USBDEVFS_GET_CAPABILITIES _IOR('U', 26, __u32) + #endif /* _LINUX_USBDEVICE_FS_H */ -- cgit v1.2.3 From 3d97ff63f8997761f12c8fbe8082996c6eeaba1a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 4 Jul 2012 09:18:03 +0200 Subject: usbdevfs: Use scatter-gather lists for large bulk transfers When using urb->transfer_buffer we need to allocate physical contiguous buffers for the entire transfer, which is pretty much guaranteed to fail with large transfers. Currently userspace works around this by breaking large transfers into multiple urbs. For large bulk transfers this leads to all kind of complications. This patch makes it possible for userspace to reliable submit large bulk transfers to scatter-gather capable host controllers in one go, by using a scatterlist to break the transfer up in managable chunks. Signed-off-by: Hans de Goede Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 152 ++++++++++++++++++++++++++++++++++--------- include/linux/usbdevice_fs.h | 1 + 2 files changed, 122 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 0b387c1a8b7e..ebb8a9de8b5f 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ #define USB_MAXBUS 64 #define USB_DEVICE_MAX USB_MAXBUS * 128 +#define USB_SG_SIZE 16384 /* split-size for large txs */ /* Mutual exclusion for removal, open, and release */ DEFINE_MUTEX(usbfs_mutex); @@ -285,9 +287,16 @@ static struct async *alloc_async(unsigned int numisoframes) static void free_async(struct async *as) { + int i; + put_pid(as->pid); if (as->cred) put_cred(as->cred); + for (i = 0; i < as->urb->num_sgs; i++) { + if (sg_page(&as->urb->sg[i])) + kfree(sg_virt(&as->urb->sg[i])); + } + kfree(as->urb->sg); kfree(as->urb->transfer_buffer); kfree(as->urb->setup_packet); usb_free_urb(as->urb); @@ -388,6 +397,53 @@ static void snoop_urb(struct usb_device *udev, } } +static void snoop_urb_data(struct urb *urb, unsigned len) +{ + int i, size; + + if (!usbfs_snoop) + return; + + if (urb->num_sgs == 0) { + print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1, + urb->transfer_buffer, len, 1); + return; + } + + for (i = 0; i < urb->num_sgs && len; i++) { + size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len; + print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1, + sg_virt(&urb->sg[i]), size, 1); + len -= size; + } +} + +static int copy_urb_data_to_user(u8 __user *userbuffer, struct urb *urb) +{ + unsigned i, len, size; + + if (urb->number_of_packets > 0) /* Isochronous */ + len = urb->transfer_buffer_length; + else /* Non-Isoc */ + len = urb->actual_length; + + if (urb->num_sgs == 0) { + if (copy_to_user(userbuffer, urb->transfer_buffer, len)) + return -EFAULT; + return 0; + } + + for (i = 0; i < urb->num_sgs && len; i++) { + size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len; + if (copy_to_user(userbuffer, sg_virt(&urb->sg[i]), size)) + return -EFAULT; + userbuffer += size; + len -= size; + } + + return 0; +} + #define AS_CONTINUATION 1 #define AS_UNLINK 2 @@ -454,9 +510,10 @@ static void async_completed(struct urb *urb) } snoop(&urb->dev->dev, "urb complete\n"); snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length, - as->status, COMPLETE, - ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_OUT) ? - NULL : urb->transfer_buffer, urb->actual_length); + as->status, COMPLETE, NULL, 0); + if ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_IN) + snoop_urb_data(urb, urb->actual_length); + if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET && as->status != -ENOENT) cancel_bulk_urbs(ps, as->bulk_addr); @@ -1114,8 +1171,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, struct async *as = NULL; struct usb_ctrlrequest *dr = NULL; unsigned int u, totlen, isofrmlen; - int ret, ifnum = -1; - int is_in; + int i, ret, is_in, num_sgs = 0, ifnum = -1; + void *buf; if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP | USBDEVFS_URB_SHORT_NOT_OK | @@ -1199,6 +1256,9 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, goto interrupt_urb; } uurb->number_of_packets = 0; + num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE); + if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize) + num_sgs = 0; break; case USBDEVFS_URB_TYPE_INTERRUPT: @@ -1255,26 +1315,67 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, ret = -ENOMEM; goto error; } - u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length; + + u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length + + num_sgs * sizeof(struct scatterlist); ret = usbfs_increase_memory_usage(u); if (ret) goto error; as->mem_usage = u; - if (uurb->buffer_length > 0) { + if (num_sgs) { + as->urb->sg = kmalloc(num_sgs * sizeof(struct scatterlist), + GFP_KERNEL); + if (!as->urb->sg) { + ret = -ENOMEM; + goto error; + } + as->urb->num_sgs = num_sgs; + sg_init_table(as->urb->sg, as->urb->num_sgs); + + totlen = uurb->buffer_length; + for (i = 0; i < as->urb->num_sgs; i++) { + u = (totlen > USB_SG_SIZE) ? USB_SG_SIZE : totlen; + buf = kmalloc(u, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto error; + } + sg_set_buf(&as->urb->sg[i], buf, u); + + if (!is_in) { + if (copy_from_user(buf, uurb->buffer, u)) { + ret = -EFAULT; + goto error; + } + } + totlen -= u; + } + } else if (uurb->buffer_length > 0) { as->urb->transfer_buffer = kmalloc(uurb->buffer_length, GFP_KERNEL); if (!as->urb->transfer_buffer) { ret = -ENOMEM; goto error; } - /* Isochronous input data may end up being discontiguous - * if some of the packets are short. Clear the buffer so - * that the gaps don't leak kernel data to userspace. - */ - if (is_in && uurb->type == USBDEVFS_URB_TYPE_ISO) + + if (!is_in) { + if (copy_from_user(as->urb->transfer_buffer, + uurb->buffer, + uurb->buffer_length)) { + ret = -EFAULT; + goto error; + } + } else if (uurb->type == USBDEVFS_URB_TYPE_ISO) { + /* + * Isochronous input data may end up being + * discontiguous if some of the packets are short. + * Clear the buffer so that the gaps don't leak + * kernel data to userspace. + */ memset(as->urb->transfer_buffer, 0, uurb->buffer_length); + } } as->urb->dev = ps->dev; as->urb->pipe = (uurb->type << 30) | @@ -1328,17 +1429,12 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, as->pid = get_pid(task_pid(current)); as->cred = get_current_cred(); security_task_getsecid(current, &as->secid); - if (!is_in && uurb->buffer_length > 0) { - if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, - uurb->buffer_length)) { - ret = -EFAULT; - goto error; - } - } snoop_urb(ps->dev, as->userurb, as->urb->pipe, as->urb->transfer_buffer_length, 0, SUBMIT, - is_in ? NULL : as->urb->transfer_buffer, - uurb->buffer_length); + NULL, 0); + if (!is_in) + snoop_urb_data(as->urb, as->urb->transfer_buffer_length); + async_newpending(as); if (usb_endpoint_xfer_bulk(&ep->desc)) { @@ -1433,11 +1529,7 @@ static int processcompl(struct async *as, void __user * __user *arg) unsigned int i; if (as->userbuffer && urb->actual_length) { - if (urb->number_of_packets > 0) /* Isochronous */ - i = urb->transfer_buffer_length; - else /* Non-Isoc */ - i = urb->actual_length; - if (copy_to_user(as->userbuffer, urb->transfer_buffer, i)) + if (copy_urb_data_to_user(as->userbuffer, urb)) goto err_out; } if (put_user(as->status, &userurb->status)) @@ -1605,11 +1697,7 @@ static int processcompl_compat(struct async *as, void __user * __user *arg) unsigned int i; if (as->userbuffer && urb->actual_length) { - if (urb->number_of_packets > 0) /* Isochronous */ - i = urb->transfer_buffer_length; - else /* Non-Isoc */ - i = urb->actual_length; - if (copy_to_user(as->userbuffer, urb->transfer_buffer, i)) + if (copy_urb_data_to_user(as->userbuffer, urb)) return -EFAULT; } if (put_user(as->status, &userurb->status)) @@ -1831,6 +1919,8 @@ static int proc_get_capabilities(struct dev_state *ps, void __user *arg) caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM; if (!ps->dev->bus->no_stop_on_short) caps |= USBDEVFS_CAP_BULK_CONTINUATION; + if (ps->dev->bus->sg_tablesize) + caps |= USBDEVFS_CAP_BULK_SCATTER_GATHER; if (put_user(caps, (__u32 __user *)arg)) return -EFAULT; diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 07b2ceaaad70..3b74666be027 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -129,6 +129,7 @@ struct usbdevfs_hub_portinfo { #define USBDEVFS_CAP_ZERO_PACKET 0x01 #define USBDEVFS_CAP_BULK_CONTINUATION 0x02 #define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04 +#define USBDEVFS_CAP_BULK_SCATTER_GATHER 0x08 #ifdef __KERNEL__ #ifdef CONFIG_COMPAT -- cgit v1.2.3 From 77c4400f2f0fd8384ab5cbe41d81ccc664896b2d Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Fri, 29 Jun 2012 17:48:53 +0800 Subject: USB: Chipidea: rename struct ci13xxx_udc_driver to struct ci13xxx_platform_data This patch rename struct ci13xxx_udc_driver and var with the type. ci13xxx_platform_data reflect it's passed from platfrom driver. Signed-off-by: Richard Zhao Reviewed-by: Felipe Balbi Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 4 ++-- drivers/usb/chipidea/ci13xxx_msm.c | 6 +++--- drivers/usb/chipidea/ci13xxx_pci.c | 20 ++++++++++---------- drivers/usb/chipidea/core.c | 12 ++++++------ drivers/usb/chipidea/host.c | 2 +- drivers/usb/chipidea/udc.c | 24 ++++++++++++------------ include/linux/usb/chipidea.h | 2 +- 7 files changed, 35 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 50911f8490d4..0b093308548d 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -125,7 +125,7 @@ struct hw_bank { * @remote_wakeup: host-enabled remote wakeup * @suspended: suspended by host * @test_mode: the selected test mode - * @udc_driver: platform specific information supplied by parent device + * @platdata: platform specific information supplied by parent device * @vbus_active: is VBUS active * @transceiver: pointer to USB PHY, if any * @hcd: pointer to usb_hcd for ehci host driver @@ -158,7 +158,7 @@ struct ci13xxx { u8 suspended; u8 test_mode; - struct ci13xxx_udc_driver *udc_driver; + struct ci13xxx_platform_data *platdata; int vbus_active; struct usb_phy *transceiver; struct usb_hcd *hcd; diff --git a/drivers/usb/chipidea/ci13xxx_msm.c b/drivers/usb/chipidea/ci13xxx_msm.c index 11a7befdc4ef..12c0dd6a300c 100644 --- a/drivers/usb/chipidea/ci13xxx_msm.c +++ b/drivers/usb/chipidea/ci13xxx_msm.c @@ -45,7 +45,7 @@ static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event) } } -static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = { +static struct ci13xxx_platform_data ci13xxx_msm_platdata = { .name = "ci13xxx_msm", .flags = CI13XXX_REGS_SHARED | CI13XXX_REQUIRE_TRANSCEIVER | @@ -75,8 +75,8 @@ static int __devinit ci13xxx_msm_probe(struct platform_device *pdev) goto put_platform; } - ret = platform_device_add_data(plat_ci, &ci13xxx_msm_udc_driver, - sizeof(ci13xxx_msm_udc_driver)); + ret = platform_device_add_data(plat_ci, &ci13xxx_msm_platdata, + sizeof(ci13xxx_msm_platdata)); if (ret) goto put_platform; diff --git a/drivers/usb/chipidea/ci13xxx_pci.c b/drivers/usb/chipidea/ci13xxx_pci.c index e3dab27f5c75..cdcac3a0e94d 100644 --- a/drivers/usb/chipidea/ci13xxx_pci.c +++ b/drivers/usb/chipidea/ci13xxx_pci.c @@ -23,17 +23,17 @@ /****************************************************************************** * PCI block *****************************************************************************/ -struct ci13xxx_udc_driver pci_driver = { +struct ci13xxx_platform_data pci_platdata = { .name = UDC_DRIVER_NAME, .capoffset = DEF_CAPOFFSET, }; -struct ci13xxx_udc_driver langwell_pci_driver = { +struct ci13xxx_platform_data langwell_pci_platdata = { .name = UDC_DRIVER_NAME, .capoffset = 0, }; -struct ci13xxx_udc_driver penwell_pci_driver = { +struct ci13xxx_platform_data penwell_pci_platdata = { .name = UDC_DRIVER_NAME, .capoffset = 0, .power_budget = 200, @@ -51,12 +51,12 @@ struct ci13xxx_udc_driver penwell_pci_driver = { static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - struct ci13xxx_udc_driver *driver = (void *)id->driver_data; + struct ci13xxx_platform_data *platdata = (void *)id->driver_data; struct platform_device *plat_ci; struct resource res[3]; int retval = 0, nres = 2; - if (!driver) { + if (!platdata) { dev_err(&pdev->dev, "device doesn't provide driver data\n"); return -ENODEV; } @@ -95,7 +95,7 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, goto put_platform; } - retval = platform_device_add_data(plat_ci, driver, sizeof(*driver)); + retval = platform_device_add_data(plat_ci, platdata, sizeof(*platdata)); if (retval) goto put_platform; @@ -147,19 +147,19 @@ static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev) static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = { { PCI_DEVICE(0x153F, 0x1004), - .driver_data = (kernel_ulong_t)&pci_driver, + .driver_data = (kernel_ulong_t)&pci_platdata, }, { PCI_DEVICE(0x153F, 0x1006), - .driver_data = (kernel_ulong_t)&pci_driver, + .driver_data = (kernel_ulong_t)&pci_platdata, }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0811), - .driver_data = (kernel_ulong_t)&langwell_pci_driver, + .driver_data = (kernel_ulong_t)&langwell_pci_platdata, }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0829), - .driver_data = (kernel_ulong_t)&penwell_pci_driver, + .driver_data = (kernel_ulong_t)&penwell_pci_platdata, }, { 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ } }; diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 15e03b308f8a..9a883bd5e113 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -179,7 +179,7 @@ static int hw_device_init(struct ci13xxx *ci, void __iomem *base) ci->hw_bank.abs = base; ci->hw_bank.cap = ci->hw_bank.abs; - ci->hw_bank.cap += ci->udc_driver->capoffset; + ci->hw_bank.cap += ci->platdata->capoffset; ci->hw_bank.op = ci->hw_bank.cap + ioread8(ci->hw_bank.cap); hw_alloc_regmap(ci, false); @@ -227,11 +227,11 @@ int hw_device_reset(struct ci13xxx *ci, u32 mode) udelay(10); /* not RTOS friendly */ - if (ci->udc_driver->notify_event) - ci->udc_driver->notify_event(ci, + if (ci->platdata->notify_event) + ci->platdata->notify_event(ci, CI13XXX_CONTROLLER_RESET_EVENT); - if (ci->udc_driver->flags & CI13XXX_DISABLE_STREAMING) + if (ci->platdata->flags & CI13XXX_DISABLE_STREAMING) hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); /* USBMODE should be configured step by step */ @@ -364,7 +364,7 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev) } ci->dev = dev; - ci->udc_driver = dev->platform_data; + ci->platdata = dev->platform_data; ret = hw_device_init(ci, base); if (ret < 0) { @@ -419,7 +419,7 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, ci); - ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->udc_driver->name, + ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->platdata->name, ci); if (ret) goto stop; diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 9eacd21c0cd9..4a4fdb8c65f8 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -116,7 +116,7 @@ static int host_start(struct ci13xxx *ci) hcd->regs = ci->hw_bank.abs; hcd->has_tt = 1; - hcd->power_budget = ci->udc_driver->power_budget; + hcd->power_budget = ci->platdata->power_budget; ehci = hcd_to_ehci(hcd); ehci->caps = ci->hw_bank.cap; diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 80e71021f186..3094c85dc0b5 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1363,7 +1363,7 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) unsigned long flags; int gadget_ready = 0; - if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS)) + if (!(udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS)) return -EOPNOTSUPP; spin_lock_irqsave(&udc->lock, flags); @@ -1379,8 +1379,8 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) hw_device_state(udc, udc->ep0out->qh.dma); } else { hw_device_state(udc, 0); - if (udc->udc_driver->notify_event) - udc->udc_driver->notify_event(udc, + if (udc->platdata->notify_event) + udc->platdata->notify_event(udc, CI13XXX_CONTROLLER_STOPPED_EVENT); _gadget_stop_activity(&udc->gadget); pm_runtime_put_sync(&_gadget->dev); @@ -1515,9 +1515,9 @@ static int ci13xxx_start(struct usb_gadget *gadget, udc->driver = driver; pm_runtime_get_sync(&udc->gadget.dev); - if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) { + if (udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS) { if (udc->vbus_active) { - if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) + if (udc->platdata->flags & CI13XXX_REGS_SHARED) hw_device_reset(udc, USBMODE_CM_DC); } else { pm_runtime_put_sync(&udc->gadget.dev); @@ -1545,11 +1545,11 @@ static int ci13xxx_stop(struct usb_gadget *gadget, spin_lock_irqsave(&udc->lock, flags); - if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) || + if (!(udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS) || udc->vbus_active) { hw_device_state(udc, 0); - if (udc->udc_driver->notify_event) - udc->udc_driver->notify_event(udc, + if (udc->platdata->notify_event) + udc->platdata->notify_event(udc, CI13XXX_CONTROLLER_STOPPED_EVENT); udc->driver = NULL; spin_unlock_irqrestore(&udc->lock, flags); @@ -1582,7 +1582,7 @@ static irqreturn_t udc_irq(struct ci13xxx *udc) spin_lock(&udc->lock); - if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) { + if (udc->platdata->flags & CI13XXX_REGS_SHARED) { if (hw_read(udc, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { spin_unlock(&udc->lock); @@ -1654,7 +1654,7 @@ static int udc_start(struct ci13xxx *udc) udc->gadget.speed = USB_SPEED_UNKNOWN; udc->gadget.max_speed = USB_SPEED_HIGH; udc->gadget.is_otg = 0; - udc->gadget.name = udc->udc_driver->name; + udc->gadget.name = udc->platdata->name; INIT_LIST_HEAD(&udc->gadget.ep_list); @@ -1687,14 +1687,14 @@ static int udc_start(struct ci13xxx *udc) udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) { + if (udc->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) { if (udc->transceiver == NULL) { retval = -ENODEV; goto free_pools; } } - if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) { + if (!(udc->platdata->flags & CI13XXX_REGS_SHARED)) { retval = hw_device_reset(udc, USBMODE_CM_DC); if (retval) goto put_transceiver; diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index edb90d6cfd12..d4cf970656fb 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -6,7 +6,7 @@ #define __LINUX_USB_CHIPIDEA_H struct ci13xxx; -struct ci13xxx_udc_driver { +struct ci13xxx_platform_data { const char *name; /* offset of the capability registers */ uintptr_t capoffset; -- cgit v1.2.3 From efcc3c61b9b1e4f764e14c48c553e6d477f40512 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 5 Jul 2012 17:24:19 +0100 Subject: ASoC: dapm: Allow routes to be deleted at runtime Since we're now relying on DAPM for things like enabling clocks when we reparent the clocks for widgets we need to either use conditional routes (which are expensive) or remove routes at runtime. Add a route removal API to support this use case. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc-dapm.h | 2 ++ sound/soc/soc-dapm.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 05559e571d44..abe373d57adc 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -374,6 +374,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm); void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); +int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route, int num); int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 19fda1339510..4ba47aab9801 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2264,6 +2264,59 @@ err: return ret; } +static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route) +{ + struct snd_soc_dapm_path *path, *p; + const char *sink; + const char *source; + char prefixed_sink[80]; + char prefixed_source[80]; + + if (route->control) { + dev_err(dapm->dev, + "Removal of routes with controls not supported\n"); + return -EINVAL; + } + + if (dapm->codec && dapm->codec->name_prefix) { + snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", + dapm->codec->name_prefix, route->sink); + sink = prefixed_sink; + snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", + dapm->codec->name_prefix, route->source); + source = prefixed_source; + } else { + sink = route->sink; + source = route->source; + } + + path = NULL; + list_for_each_entry(p, &dapm->card->paths, list) { + if (strcmp(p->source->name, source) != 0) + continue; + if (strcmp(p->sink->name, sink) != 0) + continue; + path = p; + break; + } + + if (path) { + dapm_mark_dirty(path->source, "Route removed"); + dapm_mark_dirty(path->sink, "Route removed"); + + list_del(&path->list); + list_del(&path->list_sink); + list_del(&path->list_source); + kfree(path); + } else { + dev_warn(dapm->dev, "Route %s->%s does not exist\n", + source, sink); + } + + return 0; +} + /** * snd_soc_dapm_add_routes - Add routes between DAPM widgets * @dapm: DAPM context @@ -2298,6 +2351,30 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); +/** + * snd_soc_dapm_del_routes - Remove routes between DAPM widgets + * @dapm: DAPM context + * @route: audio routes + * @num: number of routes + * + * Removes routes from the DAPM context. + */ +int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route, int num) +{ + int i, ret = 0; + + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + for (i = 0; i < num; i++) { + snd_soc_dapm_del_route(dapm, route); + route++; + } + mutex_unlock(&dapm->card->dapm_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes); + static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route) { -- cgit v1.2.3 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') 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 From 7078daa020db5ba501fa3de64b8653c221a640b4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 14 May 2012 04:34:34 -0300 Subject: [media] uvcvideo: Document the struct uvc_xu_control_query query field Several developers have reported that the lack of macros for the struct uvc_xu_control_query query field in uvcvideo.h was confusing and forced them to look at the driver source code to find out what applications were supposed to pass in that field. Add a comment to the header to clarify the expected usage of the query field. Reported-by: Paulo Assis Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- include/linux/uvcvideo.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/uvcvideo.h b/include/linux/uvcvideo.h index f46a53f060d7..3b081862b9e8 100644 --- a/include/linux/uvcvideo.h +++ b/include/linux/uvcvideo.h @@ -58,7 +58,8 @@ struct uvc_xu_control_mapping { struct uvc_xu_control_query { __u8 unit; __u8 selector; - __u8 query; + __u8 query; /* Video Class-Specific Request Code, */ + /* defined in linux/usb/video.h A.8. */ __u16 size; __u8 __user *data; }; -- cgit v1.2.3 From ebc04047b398d415627f82653c4e722e8fc2c083 Mon Sep 17 00:00:00 2001 From: Benoît Thébaudeau Date: Thu, 28 Jun 2012 12:12:13 -0300 Subject: [PATCH] media: add Analog Devices ADV7393 video encoder driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ADV7393 I²C-based video encoder driver. This driver has been tested on custom hardware. It has been tested for composite output. It is derived from the ADV7343 driver. Signed-off-by: Benoît Thébaudeau Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 9 + drivers/media/video/Makefile | 1 + drivers/media/video/adv7393.c | 487 +++++++++++++++++++++++++++++++++++++ drivers/media/video/adv7393_regs.h | 188 ++++++++++++++ include/media/adv7393.h | 28 +++ include/media/v4l2-chip-ident.h | 3 + 6 files changed, 716 insertions(+) create mode 100644 drivers/media/video/adv7393.c create mode 100644 drivers/media/video/adv7393_regs.h create mode 100644 include/media/adv7393.h (limited to 'include') diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index da363c44fab2..c128fac0ce2c 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -462,6 +462,15 @@ config VIDEO_ADV7343 To compile this driver as a module, choose M here: the module will be called adv7343. +config VIDEO_ADV7393 + tristate "ADV7393 video encoder" + depends on I2C + help + Support for Analog Devices I2C bus based ADV7393 encoder. + + To compile this driver as a module, choose M here: the + module will be called adv7393. + config VIDEO_AK881X tristate "AK8813/AK8814 video encoders" depends on I2C diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index d209de0e0ca8..b7da9faa3b0a 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o +obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_VS6624) += vs6624.o obj-$(CONFIG_VIDEO_BT819) += bt819.o diff --git a/drivers/media/video/adv7393.c b/drivers/media/video/adv7393.c new file mode 100644 index 000000000000..3dc6098c7267 --- /dev/null +++ b/drivers/media/video/adv7393.c @@ -0,0 +1,487 @@ +/* + * adv7393 - ADV7393 Video Encoder Driver + * + * The encoder hardware does not support SECAM. + * + * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ + * Benoît Thébaudeau + * + * Based on ADV7343 driver, + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "adv7393_regs.h" + +MODULE_DESCRIPTION("ADV7393 video encoder driver"); +MODULE_LICENSE("GPL"); + +static bool debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +struct adv7393_state { + struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + u8 reg00; + u8 reg01; + u8 reg02; + u8 reg35; + u8 reg80; + u8 reg82; + u32 output; + v4l2_std_id std; +}; + +static inline struct adv7393_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7393_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7393_state, hdl)->sd; +} + +static inline int adv7393_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static const u8 adv7393_init_reg_val[] = { + ADV7393_SOFT_RESET, ADV7393_SOFT_RESET_DEFAULT, + ADV7393_POWER_MODE_REG, ADV7393_POWER_MODE_REG_DEFAULT, + + ADV7393_HD_MODE_REG1, ADV7393_HD_MODE_REG1_DEFAULT, + ADV7393_HD_MODE_REG2, ADV7393_HD_MODE_REG2_DEFAULT, + ADV7393_HD_MODE_REG3, ADV7393_HD_MODE_REG3_DEFAULT, + ADV7393_HD_MODE_REG4, ADV7393_HD_MODE_REG4_DEFAULT, + ADV7393_HD_MODE_REG5, ADV7393_HD_MODE_REG5_DEFAULT, + ADV7393_HD_MODE_REG6, ADV7393_HD_MODE_REG6_DEFAULT, + ADV7393_HD_MODE_REG7, ADV7393_HD_MODE_REG7_DEFAULT, + + ADV7393_SD_MODE_REG1, ADV7393_SD_MODE_REG1_DEFAULT, + ADV7393_SD_MODE_REG2, ADV7393_SD_MODE_REG2_DEFAULT, + ADV7393_SD_MODE_REG3, ADV7393_SD_MODE_REG3_DEFAULT, + ADV7393_SD_MODE_REG4, ADV7393_SD_MODE_REG4_DEFAULT, + ADV7393_SD_MODE_REG5, ADV7393_SD_MODE_REG5_DEFAULT, + ADV7393_SD_MODE_REG6, ADV7393_SD_MODE_REG6_DEFAULT, + ADV7393_SD_MODE_REG7, ADV7393_SD_MODE_REG7_DEFAULT, + ADV7393_SD_MODE_REG8, ADV7393_SD_MODE_REG8_DEFAULT, + + ADV7393_SD_TIMING_REG0, ADV7393_SD_TIMING_REG0_DEFAULT, + + ADV7393_SD_HUE_ADJUST, ADV7393_SD_HUE_ADJUST_DEFAULT, + ADV7393_SD_CGMS_WSS0, ADV7393_SD_CGMS_WSS0_DEFAULT, + ADV7393_SD_BRIGHTNESS_WSS, ADV7393_SD_BRIGHTNESS_WSS_DEFAULT, +}; + +/* + * 2^32 + * FSC(reg) = FSC (HZ) * -------- + * 27000000 + */ +static const struct adv7393_std_info stdinfo[] = { + { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443, + }, { + /* FSC(Hz) = 3,579,545.45 Hz */ + SD_STD_NTSC, 569408542, V4L2_STD_NTSC, + }, { + /* FSC(Hz) = 3,575,611.00 Hz */ + SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M, + }, { + /* FSC(Hz) = 3,582,056.00 Hz */ + SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL, + }, +}; + +static int adv7393_setstd(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7393_state *state = to_state(sd); + const struct adv7393_std_info *std_info; + int num_std; + u8 reg; + u32 val; + int err = 0; + int i; + + num_std = ARRAY_SIZE(stdinfo); + + for (i = 0; i < num_std; i++) { + if (stdinfo[i].stdid & std) + break; + } + + if (i == num_std) { + v4l2_dbg(1, debug, sd, + "Invalid std or std is not supported: %llx\n", + (unsigned long long)std); + return -EINVAL; + } + + std_info = &stdinfo[i]; + + /* Set the standard */ + val = state->reg80 & ~SD_STD_MASK; + val |= std_info->standard_val3; + err = adv7393_write(sd, ADV7393_SD_MODE_REG1, val); + if (err < 0) + goto setstd_exit; + + state->reg80 = val; + + /* Configure the input mode register */ + val = state->reg01 & ~INPUT_MODE_MASK; + val |= SD_INPUT_MODE; + err = adv7393_write(sd, ADV7393_MODE_SELECT_REG, val); + if (err < 0) + goto setstd_exit; + + state->reg01 = val; + + /* Program the sub carrier frequency registers */ + val = std_info->fsc_val; + for (reg = ADV7393_FSC_REG0; reg <= ADV7393_FSC_REG3; reg++) { + err = adv7393_write(sd, reg, val); + if (err < 0) + goto setstd_exit; + val >>= 8; + } + + val = state->reg82; + + /* Pedestal settings */ + if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443)) + val |= SD_PEDESTAL_EN; + else + val &= SD_PEDESTAL_DI; + + err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); + if (err < 0) + goto setstd_exit; + + state->reg82 = val; + +setstd_exit: + if (err != 0) + v4l2_err(sd, "Error setting std, write failed\n"); + + return err; +} + +static int adv7393_setoutput(struct v4l2_subdev *sd, u32 output_type) +{ + struct adv7393_state *state = to_state(sd); + u8 val; + int err = 0; + + if (output_type > ADV7393_SVIDEO_ID) { + v4l2_dbg(1, debug, sd, + "Invalid output type or output type not supported:%d\n", + output_type); + return -EINVAL; + } + + /* Enable Appropriate DAC */ + val = state->reg00 & 0x03; + + if (output_type == ADV7393_COMPOSITE_ID) + val |= ADV7393_COMPOSITE_POWER_VALUE; + else if (output_type == ADV7393_COMPONENT_ID) + val |= ADV7393_COMPONENT_POWER_VALUE; + else + val |= ADV7393_SVIDEO_POWER_VALUE; + + err = adv7393_write(sd, ADV7393_POWER_MODE_REG, val); + if (err < 0) + goto setoutput_exit; + + state->reg00 = val; + + /* Enable YUV output */ + val = state->reg02 | YUV_OUTPUT_SELECT; + err = adv7393_write(sd, ADV7393_MODE_REG0, val); + if (err < 0) + goto setoutput_exit; + + state->reg02 = val; + + /* configure SD DAC Output 1 bit */ + val = state->reg82; + if (output_type == ADV7393_COMPONENT_ID) + val &= SD_DAC_OUT1_DI; + else + val |= SD_DAC_OUT1_EN; + err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); + if (err < 0) + goto setoutput_exit; + + state->reg82 = val; + + /* configure ED/HD Color DAC Swap bit to zero */ + val = state->reg35 & HD_DAC_SWAP_DI; + err = adv7393_write(sd, ADV7393_HD_MODE_REG6, val); + if (err < 0) + goto setoutput_exit; + + state->reg35 = val; + +setoutput_exit: + if (err != 0) + v4l2_err(sd, "Error setting output, write failed\n"); + + return err; +} + +static int adv7393_log_status(struct v4l2_subdev *sd) +{ + struct adv7393_state *state = to_state(sd); + + v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std); + v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" : + ((state->output == 1) ? "Component" : "S-Video")); + return 0; +} + +static int adv7393_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return adv7393_write(sd, ADV7393_SD_BRIGHTNESS_WSS, + ctrl->val & SD_BRIGHTNESS_VALUE_MASK); + + case V4L2_CID_HUE: + return adv7393_write(sd, ADV7393_SD_HUE_ADJUST, + ctrl->val - ADV7393_HUE_MIN); + + case V4L2_CID_GAIN: + return adv7393_write(sd, ADV7393_DAC123_OUTPUT_LEVEL, + ctrl->val); + } + return -EINVAL; +} + +static int adv7393_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7393, 0); +} + +static const struct v4l2_ctrl_ops adv7393_ctrl_ops = { + .s_ctrl = adv7393_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops adv7393_core_ops = { + .log_status = adv7393_log_status, + .g_chip_ident = adv7393_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, +}; + +static int adv7393_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7393_state *state = to_state(sd); + int err = 0; + + if (state->std == std) + return 0; + + err = adv7393_setstd(sd, std); + if (!err) + state->std = std; + + return err; +} + +static int adv7393_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7393_state *state = to_state(sd); + int err = 0; + + if (state->output == output) + return 0; + + err = adv7393_setoutput(sd, output); + if (!err) + state->output = output; + + return err; +} + +static const struct v4l2_subdev_video_ops adv7393_video_ops = { + .s_std_output = adv7393_s_std_output, + .s_routing = adv7393_s_routing, +}; + +static const struct v4l2_subdev_ops adv7393_ops = { + .core = &adv7393_core_ops, + .video = &adv7393_video_ops, +}; + +static int adv7393_initialize(struct v4l2_subdev *sd) +{ + struct adv7393_state *state = to_state(sd); + int err = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(adv7393_init_reg_val); i += 2) { + + err = adv7393_write(sd, adv7393_init_reg_val[i], + adv7393_init_reg_val[i+1]); + if (err) { + v4l2_err(sd, "Error initializing\n"); + return err; + } + } + + /* Configure for default video standard */ + err = adv7393_setoutput(sd, state->output); + if (err < 0) { + v4l2_err(sd, "Error setting output during init\n"); + return -EINVAL; + } + + err = adv7393_setstd(sd, state->std); + if (err < 0) { + v4l2_err(sd, "Error setting std during init\n"); + return -EINVAL; + } + + return err; +} + +static int adv7393_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7393_state *state; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct adv7393_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + state->reg00 = ADV7393_POWER_MODE_REG_DEFAULT; + state->reg01 = 0x00; + state->reg02 = 0x20; + state->reg35 = ADV7393_HD_MODE_REG6_DEFAULT; + state->reg80 = ADV7393_SD_MODE_REG1_DEFAULT; + state->reg82 = ADV7393_SD_MODE_REG2_DEFAULT; + + state->output = ADV7393_COMPOSITE_ID; + state->std = V4L2_STD_NTSC; + + v4l2_i2c_subdev_init(&state->sd, client, &adv7393_ops); + + v4l2_ctrl_handler_init(&state->hdl, 3); + v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, + V4L2_CID_BRIGHTNESS, ADV7393_BRIGHTNESS_MIN, + ADV7393_BRIGHTNESS_MAX, 1, + ADV7393_BRIGHTNESS_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, + V4L2_CID_HUE, ADV7393_HUE_MIN, + ADV7393_HUE_MAX, 1, + ADV7393_HUE_DEF); + v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, + V4L2_CID_GAIN, ADV7393_GAIN_MIN, + ADV7393_GAIN_MAX, 1, + ADV7393_GAIN_DEF); + state->sd.ctrl_handler = &state->hdl; + if (state->hdl.error) { + int err = state->hdl.error; + + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + return err; + } + v4l2_ctrl_handler_setup(&state->hdl); + + err = adv7393_initialize(&state->sd); + if (err) { + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + } + return err; +} + +static int adv7393_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7393_state *state = to_state(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + kfree(state); + + return 0; +} + +static const struct i2c_device_id adv7393_id[] = { + {"adv7393", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, adv7393_id); + +static struct i2c_driver adv7393_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7393", + }, + .probe = adv7393_probe, + .remove = adv7393_remove, + .id_table = adv7393_id, +}; +module_i2c_driver(adv7393_driver); diff --git a/drivers/media/video/adv7393_regs.h b/drivers/media/video/adv7393_regs.h new file mode 100644 index 000000000000..78968330f0be --- /dev/null +++ b/drivers/media/video/adv7393_regs.h @@ -0,0 +1,188 @@ +/* + * ADV7393 encoder related structure and register definitions + * + * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ + * Benoît Thébaudeau + * + * Based on ADV7343 driver, + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ADV7393_REGS_H +#define ADV7393_REGS_H + +struct adv7393_std_info { + u32 standard_val3; + u32 fsc_val; + v4l2_std_id stdid; +}; + +/* Register offset macros */ +#define ADV7393_POWER_MODE_REG (0x00) +#define ADV7393_MODE_SELECT_REG (0x01) +#define ADV7393_MODE_REG0 (0x02) + +#define ADV7393_DAC123_OUTPUT_LEVEL (0x0B) + +#define ADV7393_SOFT_RESET (0x17) + +#define ADV7393_HD_MODE_REG1 (0x30) +#define ADV7393_HD_MODE_REG2 (0x31) +#define ADV7393_HD_MODE_REG3 (0x32) +#define ADV7393_HD_MODE_REG4 (0x33) +#define ADV7393_HD_MODE_REG5 (0x34) +#define ADV7393_HD_MODE_REG6 (0x35) + +#define ADV7393_HD_MODE_REG7 (0x39) + +#define ADV7393_SD_MODE_REG1 (0x80) +#define ADV7393_SD_MODE_REG2 (0x82) +#define ADV7393_SD_MODE_REG3 (0x83) +#define ADV7393_SD_MODE_REG4 (0x84) +#define ADV7393_SD_MODE_REG5 (0x86) +#define ADV7393_SD_MODE_REG6 (0x87) +#define ADV7393_SD_MODE_REG7 (0x88) +#define ADV7393_SD_MODE_REG8 (0x89) + +#define ADV7393_SD_TIMING_REG0 (0x8A) + +#define ADV7393_FSC_REG0 (0x8C) +#define ADV7393_FSC_REG1 (0x8D) +#define ADV7393_FSC_REG2 (0x8E) +#define ADV7393_FSC_REG3 (0x8F) + +#define ADV7393_SD_CGMS_WSS0 (0x99) + +#define ADV7393_SD_HUE_ADJUST (0xA0) +#define ADV7393_SD_BRIGHTNESS_WSS (0xA1) + +/* Default values for the registers */ +#define ADV7393_POWER_MODE_REG_DEFAULT (0x10) +#define ADV7393_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default + 720p EAV/SAV code*/ +#define ADV7393_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data + valid */ +#define ADV7393_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */ +#define ADV7393_HD_MODE_REG4_DEFAULT (0xEC) /* Changed */ +#define ADV7393_HD_MODE_REG5_DEFAULT (0x08) +#define ADV7393_HD_MODE_REG6_DEFAULT (0x00) +#define ADV7393_HD_MODE_REG7_DEFAULT (0x00) +#define ADV7393_SOFT_RESET_DEFAULT (0x02) +#define ADV7393_COMPOSITE_POWER_VALUE (0x10) +#define ADV7393_COMPONENT_POWER_VALUE (0x1C) +#define ADV7393_SVIDEO_POWER_VALUE (0x0C) +#define ADV7393_SD_HUE_ADJUST_DEFAULT (0x80) +#define ADV7393_SD_BRIGHTNESS_WSS_DEFAULT (0x00) + +#define ADV7393_SD_CGMS_WSS0_DEFAULT (0x10) + +#define ADV7393_SD_MODE_REG1_DEFAULT (0x10) +#define ADV7393_SD_MODE_REG2_DEFAULT (0xC9) +#define ADV7393_SD_MODE_REG3_DEFAULT (0x00) +#define ADV7393_SD_MODE_REG4_DEFAULT (0x00) +#define ADV7393_SD_MODE_REG5_DEFAULT (0x02) +#define ADV7393_SD_MODE_REG6_DEFAULT (0x8C) +#define ADV7393_SD_MODE_REG7_DEFAULT (0x14) +#define ADV7393_SD_MODE_REG8_DEFAULT (0x00) + +#define ADV7393_SD_TIMING_REG0_DEFAULT (0x0C) + +/* Bit masks for Mode Select Register */ +#define INPUT_MODE_MASK (0x70) +#define SD_INPUT_MODE (0x00) +#define HD_720P_INPUT_MODE (0x10) +#define HD_1080I_INPUT_MODE (0x10) + +/* Bit masks for Mode Register 0 */ +#define TEST_PATTERN_BLACK_BAR_EN (0x04) +#define YUV_OUTPUT_SELECT (0x20) +#define RGB_OUTPUT_SELECT (0xDF) + +/* Bit masks for SD brightness/WSS */ +#define SD_BRIGHTNESS_VALUE_MASK (0x7F) +#define SD_BLANK_WSS_DATA_MASK (0x80) + +/* Bit masks for soft reset register */ +#define SOFT_RESET (0x02) + +/* Bit masks for HD Mode Register 1 */ +#define OUTPUT_STD_MASK (0x03) +#define OUTPUT_STD_SHIFT (0) +#define OUTPUT_STD_EIA0_2 (0x00) +#define OUTPUT_STD_EIA0_1 (0x01) +#define OUTPUT_STD_FULL (0x02) +#define EMBEDDED_SYNC (0x04) +#define EXTERNAL_SYNC (0xFB) +#define STD_MODE_MASK (0x1F) +#define STD_MODE_SHIFT (3) +#define STD_MODE_720P (0x05) +#define STD_MODE_720P_25 (0x08) +#define STD_MODE_720P_30 (0x07) +#define STD_MODE_720P_50 (0x06) +#define STD_MODE_1080I (0x0D) +#define STD_MODE_1080I_25 (0x0E) +#define STD_MODE_1080P_24 (0x11) +#define STD_MODE_1080P_25 (0x10) +#define STD_MODE_1080P_30 (0x0F) +#define STD_MODE_525P (0x00) +#define STD_MODE_625P (0x03) + +/* Bit masks for SD Mode Register 1 */ +#define SD_STD_MASK (0x03) +#define SD_STD_NTSC (0x00) +#define SD_STD_PAL_BDGHI (0x01) +#define SD_STD_PAL_M (0x02) +#define SD_STD_PAL_N (0x03) +#define SD_LUMA_FLTR_MASK (0x07) +#define SD_LUMA_FLTR_SHIFT (2) +#define SD_CHROMA_FLTR_MASK (0x07) +#define SD_CHROMA_FLTR_SHIFT (5) + +/* Bit masks for SD Mode Register 2 */ +#define SD_PRPB_SSAF_EN (0x01) +#define SD_PRPB_SSAF_DI (0xFE) +#define SD_DAC_OUT1_EN (0x02) +#define SD_DAC_OUT1_DI (0xFD) +#define SD_PEDESTAL_EN (0x08) +#define SD_PEDESTAL_DI (0xF7) +#define SD_SQUARE_PIXEL_EN (0x10) +#define SD_SQUARE_PIXEL_DI (0xEF) +#define SD_PIXEL_DATA_VALID (0x40) +#define SD_ACTIVE_EDGE_EN (0x80) +#define SD_ACTIVE_EDGE_DI (0x7F) + +/* Bit masks for HD Mode Register 6 */ +#define HD_PRPB_SYNC_EN (0x04) +#define HD_PRPB_SYNC_DI (0xFB) +#define HD_DAC_SWAP_EN (0x08) +#define HD_DAC_SWAP_DI (0xF7) +#define HD_GAMMA_CURVE_A (0xEF) +#define HD_GAMMA_CURVE_B (0x10) +#define HD_GAMMA_EN (0x20) +#define HD_GAMMA_DI (0xDF) +#define HD_ADPT_FLTR_MODEA (0xBF) +#define HD_ADPT_FLTR_MODEB (0x40) +#define HD_ADPT_FLTR_EN (0x80) +#define HD_ADPT_FLTR_DI (0x7F) + +#define ADV7393_BRIGHTNESS_MAX (63) +#define ADV7393_BRIGHTNESS_MIN (-64) +#define ADV7393_BRIGHTNESS_DEF (0) +#define ADV7393_HUE_MAX (127) +#define ADV7393_HUE_MIN (-128) +#define ADV7393_HUE_DEF (0) +#define ADV7393_GAIN_MAX (64) +#define ADV7393_GAIN_MIN (-64) +#define ADV7393_GAIN_DEF (0) + +#endif diff --git a/include/media/adv7393.h b/include/media/adv7393.h new file mode 100644 index 000000000000..b28edf351842 --- /dev/null +++ b/include/media/adv7393.h @@ -0,0 +1,28 @@ +/* + * ADV7393 header file + * + * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ + * Benoît Thébaudeau + * + * Based on ADV7343 driver, + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ADV7393_H +#define ADV7393_H + +#define ADV7393_COMPOSITE_ID (0) +#define ADV7393_COMPONENT_ID (1) +#define ADV7393_SVIDEO_ID (2) + +#endif /* End of #ifndef ADV7393_H */ diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index 7395c815939d..58f914a40b20 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -180,6 +180,9 @@ enum { /* module adv7343: just ident 7343 */ V4L2_IDENT_ADV7343 = 7343, + /* module adv7393: just ident 7393 */ + V4L2_IDENT_ADV7393 = 7393, + /* module saa7706h: just ident 7706 */ V4L2_IDENT_SAA7706H = 7706, -- cgit v1.2.3 From 4a085168b59ec0fb18eb7fa023dcc47f4db14655 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 22 Jun 2012 06:38:06 -0300 Subject: [media] v4l2-ioctl: remove v4l_(i2c_)print_ioctl v4l_i2c_print_ioctl wasn't used and v4l_print_ioctl could be replaced by v4l_printk_ioctl. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 4 ++-- drivers/media/video/sn9c102/sn9c102.h | 2 +- drivers/media/video/uvc/uvc_v4l2.c | 2 +- drivers/media/video/v4l2-ioctl.c | 34 +++++++----------------------- include/media/v4l2-ioctl.h | 20 +++--------------- 5 files changed, 15 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index cbe40806bd71..f344aed32a93 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -957,7 +957,7 @@ static long pvr2_v4l2_ioctl(struct file *file, long ret = -EINVAL; if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) - v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); + v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); if (!pvr2_hdw_dev_ok(hdw)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, @@ -990,7 +990,7 @@ static long pvr2_v4l2_ioctl(struct file *file, pvr2_trace(PVR2_TRACE_V4LIOCTL, "pvr2_v4l2_do_ioctl failure, ret=%ld" " command was:", ret); - v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), + v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); } } diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h index 22ea211ab54f..2bc153e869be 100644 --- a/drivers/media/video/sn9c102/sn9c102.h +++ b/drivers/media/video/sn9c102/sn9c102.h @@ -182,7 +182,7 @@ do { \ # define V4LDBG(level, name, cmd) \ do { \ if (debug >= (level)) \ - v4l_print_ioctl(name, cmd); \ + v4l_printk_ioctl(name, cmd); \ } while (0) # define KDBG(level, fmt, args...) \ do { \ diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 759bef8897e9..f00db3060e0e 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -1051,7 +1051,7 @@ static long uvc_v4l2_ioctl(struct file *file, { if (uvc_trace_param & UVC_TRACE_IOCTL) { uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl("); - v4l_printk_ioctl(cmd); + v4l_printk_ioctl(NULL, cmd); printk(")\n"); } diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 273c6d7bef65..fd6436edde70 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -28,27 +28,6 @@ #include #include -#define dbgarg(cmd, fmt, arg...) \ - do { \ - if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { \ - printk(KERN_DEBUG "%s: ", vfd->name); \ - v4l_printk_ioctl(cmd); \ - printk(" " fmt, ## arg); \ - } \ - } while (0) - -#define dbgarg2(fmt, arg...) \ - do { \ - if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \ - printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg);\ - } while (0) - -#define dbgarg3(fmt, arg...) \ - do { \ - if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \ - printk(KERN_CONT "%s: " fmt, vfd->name, ## arg);\ - } while (0) - /* Zero out the end of the struct pointed to by p. Everything after, but * not including, the specified field is cleared. */ #define CLEAR_AFTER_FIELD(p, field) \ @@ -1956,10 +1935,13 @@ bool v4l2_is_known_ioctl(unsigned int cmd) /* Common ioctl debug function. This function can be used by external ioctl messages as well as internal V4L ioctl */ -void v4l_printk_ioctl(unsigned int cmd) +void v4l_printk_ioctl(const char *prefix, unsigned int cmd) { const char *dir, *type; + if (prefix) + printk(KERN_DEBUG "%s: ", prefix); + switch (_IOC_TYPE(cmd)) { case 'd': type = "v4l2_int"; @@ -2003,8 +1985,8 @@ static long __video_do_ioctl(struct file *file, long ret = -ENOTTY; if (ops == NULL) { - printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n", - vfd->name); + pr_warn("%s: has no ioctl_ops.\n", + video_device_node_name(vfd)); return ret; } @@ -2034,7 +2016,7 @@ static long __video_do_ioctl(struct file *file, write_only = _IOC_DIR(cmd) == _IOC_WRITE; if (write_only && debug > V4L2_DEBUG_IOCTL) { - v4l_print_ioctl(vfd->name, cmd); + v4l_printk_ioctl(video_device_node_name(vfd), cmd); pr_cont(": "); info->debug(arg, write_only); } @@ -2062,7 +2044,7 @@ done: video_device_node_name(vfd), ret); return ret; } - v4l_print_ioctl(vfd->name, cmd); + v4l_printk_ioctl(video_device_node_name(vfd), cmd); if (ret < 0) pr_cont(": error %ld\n", ret); else if (debug == V4L2_DEBUG_IOCTL) diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index d8b76f7392f8..dfd984f10d42 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -295,28 +295,14 @@ struct v4l2_ioctl_ops { #define V4L2_DEBUG_IOCTL 0x01 #define V4L2_DEBUG_IOCTL_ARG 0x02 -/* Use this macro for non-I2C drivers. Pass the driver name as the first arg. */ -#define v4l_print_ioctl(name, cmd) \ - do { \ - printk(KERN_DEBUG "%s: ", name); \ - v4l_printk_ioctl(cmd); \ - } while (0) - -/* Use this macro in I2C drivers where 'client' is the struct i2c_client - pointer */ -#define v4l_i2c_print_ioctl(client, cmd) \ - do { \ - v4l_client_printk(KERN_DEBUG, client, ""); \ - v4l_printk_ioctl(cmd); \ - } while (0) - /* Video standard functions */ extern const char *v4l2_norm_to_name(v4l2_std_id id); extern void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod); extern int v4l2_video_std_construct(struct v4l2_standard *vs, int id, const char *name); -/* Prints the ioctl in a human-readable format */ -extern void v4l_printk_ioctl(unsigned int cmd); +/* Prints the ioctl in a human-readable format. If prefix != NULL, + then do printk(KERN_DEBUG "%s: ", prefix) first. */ +extern void v4l_printk_ioctl(const char *prefix, unsigned int cmd); /* names for fancy debug output */ extern const char *v4l2_field_names[]; -- cgit v1.2.3 From 5a5adf6b669cf1a3dd2af419cd68a4c491f384a3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 22 Jun 2012 07:29:35 -0300 Subject: [media] v4l2-dev/ioctl.c: add vb2_queue support to video_device This prepares struct video_device for easier integration with vb2. It also introduces a new lock that protects the vb2_queue. It is up to the driver to use it or not. And the driver can associate an owner filehandle with the queue to check whether queuing requests are permitted for the calling filehandle. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-dev.c | 16 +++++----------- drivers/media/video/v4l2-ioctl.c | 31 +++++++++++++++++++++++-------- include/media/v4l2-dev.h | 3 +++ include/media/v4l2-ioctl.h | 5 +++++ include/media/videobuf2-core.h | 13 +++++++++++++ 5 files changed, 49 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index c2122e53f051..b82778174eef 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -348,20 +348,14 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) int ret = -ENODEV; if (vdev->fops->unlocked_ioctl) { - bool locked = false; + struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd); - if (vdev->lock) { - /* always lock unless the cmd is marked as "don't use lock" */ - locked = !v4l2_is_known_ioctl(cmd) || - !test_bit(_IOC_NR(cmd), vdev->disable_locking); - - if (locked && mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; - } + if (lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; if (video_is_registered(vdev)) ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); - if (locked) - mutex_unlock(vdev->lock); + if (lock) + mutex_unlock(lock); } else if (vdev->fops->ioctl) { /* This code path is a replacement for the BKL. It is a major * hack but it will have to do for those drivers that are not diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index fd6436edde70..70e0efb127a6 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -27,6 +27,7 @@ #include #include #include +#include /* Zero out the end of the struct pointed to by p. Everything after, but * not including, the specified field is cleared. */ @@ -1817,6 +1818,8 @@ struct v4l2_ioctl_info { #define INFO_FL_STD (1 << 2) /* This is ioctl has its own function */ #define INFO_FL_FUNC (1 << 3) +/* Queuing ioctl */ +#define INFO_FL_QUEUE (1 << 4) /* Zero struct from after the field to the end */ #define INFO_FL_CLEAR(v4l2_struct, field) \ ((offsetof(struct v4l2_struct, field) + \ @@ -1846,15 +1849,15 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)), IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)), IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_CLEAR(v4l2_buffer, length)), + IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)), IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0), IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_OVERLAY, vidioc_overlay, v4l_print_u32, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, 0), - IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, 0), - IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO), + IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)), IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_G_STD, v4l_g_std, v4l_print_std, 0), @@ -1918,8 +1921,8 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0), IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0), IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0), - IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, 0), + IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE), + IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE), IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0), IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0), IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, 0), @@ -1933,6 +1936,18 @@ bool v4l2_is_known_ioctl(unsigned int cmd) return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd; } +struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd) +{ + if (_IOC_NR(cmd) >= V4L2_IOCTLS) + return vdev->lock; + if (test_bit(_IOC_NR(cmd), vdev->disable_locking)) + return NULL; + if (vdev->queue && vdev->queue->lock && + (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE)) + return vdev->queue->lock; + return vdev->lock; +} + /* Common ioctl debug function. This function can be used by external ioctl messages as well as internal V4L ioctl */ void v4l_printk_ioctl(const char *prefix, unsigned int cmd) diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index a056e6ee1b68..5c416cdc88d5 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -100,6 +100,9 @@ struct video_device /* Control handler associated with this device node. May be NULL. */ struct v4l2_ctrl_handler *ctrl_handler; + /* vb2_queue associated with this device node. May be NULL. */ + struct vb2_queue *queue; + /* Priority state. If NULL, then v4l2_dev->prio will be used. */ struct v4l2_prio_state *prio; diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index dfd984f10d42..19e93523c2d8 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -304,6 +304,11 @@ extern int v4l2_video_std_construct(struct v4l2_standard *vs, then do printk(KERN_DEBUG "%s: ", prefix) first. */ extern void v4l_printk_ioctl(const char *prefix, unsigned int cmd); +/* Internal use only: get the mutex (if any) that we need to lock for the + given command. */ +struct video_device; +extern struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd); + /* names for fancy debug output */ extern const char *v4l2_field_names[]; extern const char *v4l2_type_names[]; diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index a15d1f1b319e..924e95e0a0a6 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -244,12 +244,23 @@ struct vb2_ops { void (*buf_queue)(struct vb2_buffer *vb); }; +struct v4l2_fh; + /** * struct vb2_queue - a videobuf queue * * @type: queue type (see V4L2_BUF_TYPE_* in linux/videodev2.h * @io_modes: supported io methods (see vb2_io_modes enum) * @io_flags: additional io flags (see vb2_fileio_flags enum) + * @lock: pointer to a mutex that protects the vb2_queue struct. The + * driver can set this to a mutex to let the v4l2 core serialize + * the queuing ioctls. If the driver wants to handle locking + * itself, then this should be set to NULL. This lock is not used + * by the videobuf2 core API. + * @owner: The filehandle that 'owns' the buffers, i.e. the filehandle + * that called reqbufs, create_buffers or started fileio. + * This field is not used by the videobuf2 core API, but it allows + * drivers to easily associate an owner filehandle with the queue. * @ops: driver-specific callbacks * @mem_ops: memory allocator specific callbacks * @drv_priv: driver private data @@ -273,6 +284,8 @@ struct vb2_queue { enum v4l2_buf_type type; unsigned int io_modes; unsigned int io_flags; + struct mutex *lock; + struct v4l2_fh *owner; const struct vb2_ops *ops; const struct vb2_mem_ops *mem_ops; -- cgit v1.2.3 From 4c1ffcaad5070ea5bca9b8057bdd7b4925237bc0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Jul 2012 05:59:18 -0300 Subject: [media] videobuf2-core: add helper functions Add helper functions to make it easier to adapt drivers to vb2. These helpers take care of core locking and check if the filehandle is the owner of the queue. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/videobuf2-core.c | 257 +++++++++++++++++++++++++++++++++++ include/media/videobuf2-core.h | 41 ++++++ 2 files changed, 298 insertions(+) (limited to 'include') diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index ed38eb748357..4e0290ab5071 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -2125,6 +2125,263 @@ size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count, } EXPORT_SYMBOL_GPL(vb2_write); + +/* + * The following functions are not part of the vb2 core API, but are helper + * functions that plug into struct v4l2_ioctl_ops, struct v4l2_file_operations + * and struct vb2_ops. + * They contain boilerplate code that most if not all drivers have to do + * and so they simplify the driver code. + */ + +/* The queue is busy if there is a owner and you are not that owner. */ +static inline bool vb2_queue_is_busy(struct video_device *vdev, struct file *file) +{ + return vdev->queue->owner && vdev->queue->owner != file->private_data; +} + +/* vb2 ioctl helpers */ + +int vb2_ioctl_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct video_device *vdev = video_devdata(file); + int res = __verify_memory_type(vdev->queue, p->memory, p->type); + + if (res) + return res; + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + res = __reqbufs(vdev->queue, p); + /* If count == 0, then the owner has released all buffers and he + is no longer owner of the queue. Otherwise we have a new owner. */ + if (res == 0) + vdev->queue->owner = p->count ? file->private_data : NULL; + return res; +} +EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs); + +int vb2_ioctl_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *p) +{ + struct video_device *vdev = video_devdata(file); + int res = __verify_memory_type(vdev->queue, p->memory, p->format.type); + + p->index = vdev->queue->num_buffers; + /* If count == 0, then just check if memory and type are valid. + Any -EBUSY result from __verify_memory_type can be mapped to 0. */ + if (p->count == 0) + return res != -EBUSY ? res : 0; + if (res) + return res; + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + res = __create_bufs(vdev->queue, p); + if (res == 0) + vdev->queue->owner = file->private_data; + return res; +} +EXPORT_SYMBOL_GPL(vb2_ioctl_create_bufs); + +int vb2_ioctl_prepare_buf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_prepare_buf(vdev->queue, p); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_prepare_buf); + +int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + /* No need to call vb2_queue_is_busy(), anyone can query buffers. */ + return vb2_querybuf(vdev->queue, p); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_querybuf); + +int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_qbuf(vdev->queue, p); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf); + +int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_dqbuf); + +int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_streamon(vdev->queue, i); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_streamon); + +int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct video_device *vdev = video_devdata(file); + + if (vb2_queue_is_busy(vdev, file)) + return -EBUSY; + return vb2_streamoff(vdev->queue, i); +} +EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff); + +/* v4l2_file_operations helpers */ + +int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = video_devdata(file); + + return vb2_mmap(vdev->queue, vma); +} +EXPORT_SYMBOL_GPL(vb2_fop_mmap); + +int vb2_fop_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + + if (file->private_data == vdev->queue->owner) { + vb2_queue_release(vdev->queue); + vdev->queue->owner = NULL; + } + return v4l2_fh_release(file); +} +EXPORT_SYMBOL_GPL(vb2_fop_release); + +ssize_t vb2_fop_write(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *vdev = video_devdata(file); + struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; + bool must_lock = !test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && lock; + int err = -EBUSY; + + if (must_lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + if (vb2_queue_is_busy(vdev, file)) + goto exit; + err = vb2_write(vdev->queue, buf, count, ppos, + file->f_flags & O_NONBLOCK); + if (err >= 0) + vdev->queue->owner = file->private_data; +exit: + if (must_lock) + mutex_unlock(lock); + return err; +} +EXPORT_SYMBOL_GPL(vb2_fop_write); + +ssize_t vb2_fop_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *vdev = video_devdata(file); + struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; + bool must_lock = !test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && vdev->lock; + int err = -EBUSY; + + if (must_lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + if (vb2_queue_is_busy(vdev, file)) + goto exit; + err = vb2_read(vdev->queue, buf, count, ppos, + file->f_flags & O_NONBLOCK); + if (err >= 0) + vdev->queue->owner = file->private_data; +exit: + if (must_lock) + mutex_unlock(lock); + return err; +} +EXPORT_SYMBOL_GPL(vb2_fop_read); + +unsigned int vb2_fop_poll(struct file *file, poll_table *wait) +{ + struct video_device *vdev = video_devdata(file); + struct vb2_queue *q = vdev->queue; + struct mutex *lock = q->lock ? q->lock : vdev->lock; + unsigned long req_events = poll_requested_events(wait); + unsigned res; + void *fileio; + /* Yuck. We really need to get rid of this flag asap. If it is + set, then the core took the serialization lock before calling + poll(). This is being phased out, but for now we have to handle + this case. */ + bool locked = test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags); + bool must_lock = false; + + /* Try to be smart: only lock if polling might start fileio, + otherwise locking will only introduce unwanted delays. */ + if (q->num_buffers == 0 && q->fileio == NULL) { + if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && + (req_events & (POLLIN | POLLRDNORM))) + must_lock = true; + else if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) && + (req_events & (POLLOUT | POLLWRNORM))) + must_lock = true; + } + + /* If locking is needed, but this helper doesn't know how, then you + shouldn't be using this helper but you should write your own. */ + WARN_ON(must_lock && !locked && !lock); + + if (must_lock && !locked && lock && mutex_lock_interruptible(lock)) + return POLLERR; + + fileio = q->fileio; + + res = vb2_poll(vdev->queue, file, wait); + + /* If fileio was started, then we have a new queue owner. */ + if (must_lock && !fileio && q->fileio) + q->owner = file->private_data; + if (must_lock && !locked && lock) + mutex_unlock(lock); + return res; +} +EXPORT_SYMBOL_GPL(vb2_fop_poll); + +#ifndef CONFIG_MMU +unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr, + unsigned long len, unsigned long pgoff, unsigned long flags) +{ + struct video_device *vdev = video_devdata(file); + + return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags); +} +EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area); +#endif + +/* vb2_ops helpers. Only use if vq->lock is non-NULL. */ + +void vb2_ops_wait_prepare(struct vb2_queue *vq) +{ + mutex_unlock(vq->lock); +} +EXPORT_SYMBOL_GPL(vb2_ops_wait_prepare); + +void vb2_ops_wait_finish(struct vb2_queue *vq) +{ + mutex_lock(vq->lock); +} +EXPORT_SYMBOL_GPL(vb2_ops_wait_finish); + MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2"); MODULE_AUTHOR("Pawel Osciak , Marek Szyprowski"); MODULE_LICENSE("GPL"); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 924e95e0a0a6..8dd9b6cc296b 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -417,4 +417,45 @@ vb2_plane_size(struct vb2_buffer *vb, unsigned int plane_no) return 0; } +/* + * The following functions are not part of the vb2 core API, but are simple + * helper functions that you can use in your struct v4l2_file_operations, + * struct v4l2_ioctl_ops and struct vb2_ops. They will serialize if vb2_queue->lock + * or video_device->lock is set, and they will set and test vb2_queue->owner + * to check if the calling filehandle is permitted to do the queuing operation. + */ + +/* struct v4l2_ioctl_ops helpers */ + +int vb2_ioctl_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p); +int vb2_ioctl_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *p); +int vb2_ioctl_prepare_buf(struct file *file, void *priv, + struct v4l2_buffer *p); +int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p); +int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p); +int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p); +int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i); +int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i); + +/* struct v4l2_file_operations helpers */ + +int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma); +int vb2_fop_release(struct file *file); +ssize_t vb2_fop_write(struct file *file, char __user *buf, + size_t count, loff_t *ppos); +ssize_t vb2_fop_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos); +unsigned int vb2_fop_poll(struct file *file, poll_table *wait); +#ifndef CONFIG_MMU +unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr, + unsigned long len, unsigned long pgoff, unsigned long flags); +#endif + +/* struct vb2_ops helpers, only use if vq->lock is non-NULL. */ + +void vb2_ops_wait_prepare(struct vb2_queue *vq); +void vb2_ops_wait_finish(struct vb2_queue *vq); + #endif /* _MEDIA_VIDEOBUF2_CORE_H */ -- cgit v1.2.3 From 500c3201e2aed201f2de0468dfeb3ceb98a9f981 Mon Sep 17 00:00:00 2001 From: "Du, Changbin" Date: Tue, 3 Jul 2012 06:27:19 -0300 Subject: [media] media: gpio-ir-recv: add allowed_protos for platform data It's better to give platform code a chance to specify the allowed protocols to use. [mchehab@redhat.com: fix merge conflict with a patch that made half of this change] Signed-off-by: Du, Changbin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/gpio-ir-recv.c | 5 ++++- include/media/gpio-ir-recv.h | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index 59fe60cd1e02..04cb272db16a 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -84,7 +84,6 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev) rcdev->priv = gpio_dev; rcdev->driver_type = RC_DRIVER_IR_RAW; - rcdev->allowed_protos = RC_TYPE_ALL; rcdev->input_name = GPIO_IR_DEVICE_NAME; rcdev->input_phys = GPIO_IR_DEVICE_NAME "/input0"; rcdev->input_id.bustype = BUS_HOST; @@ -93,6 +92,10 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev) rcdev->input_id.version = 0x0100; rcdev->dev.parent = &pdev->dev; rcdev->driver_name = GPIO_IR_DRIVER_NAME; + if (pdata->allowed_protos) + rcdev->allowed_protos = pdata->allowed_protos; + else + rcdev->allowed_protos = RC_TYPE_ALL; rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY; gpio_dev->rcdev = rcdev; diff --git a/include/media/gpio-ir-recv.h b/include/media/gpio-ir-recv.h index 91546f35b7e1..0142736a59db 100644 --- a/include/media/gpio-ir-recv.h +++ b/include/media/gpio-ir-recv.h @@ -14,9 +14,10 @@ #define __GPIO_IR_RECV_H__ struct gpio_ir_recv_platform_data { - int gpio_nr; - bool active_low; - const char *map_name; + int gpio_nr; + bool active_low; + u64 allowed_protos; + const char *map_name; }; #endif /* __GPIO_IR_RECV_H__ */ -- cgit v1.2.3 From c133482300113b3b71fa4a1fd2118531e765b36a Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 20 May 2012 11:17:12 -0300 Subject: [media] V4L: Remove "_ACTIVE" from the selection target name definitions This patch drops the _ACTIVE part from the selection target names as a prerequisite to unify the selection target names across the subdev and regular video node API. The meaning of V4L2_SEL_TGT_*_ACTIVE and V4L2_SUBDEV_SEL_TGT_*_ACTUAL selection targets is logically the same. Different names add to confusion where both APIs are used in a single driver or an application. For some system configurations different names may lead to interoperability issues. For backwards compatibility V4L2_SEL_TGT_CROP_ACTIVE and V4L2_SEL_TGT_COMPOSE_ACTIVE are defined as aliases to V4L2_SEL_TGT_CROP and V4L2_SEL_TGT_COMPOSE. These aliases will be removed after deprecation period, according to Documentation/feature-removal-schedule.txt. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/selection-api.xml | 24 +++++++++++----------- .../DocBook/media/v4l/vidioc-g-selection.xml | 15 +++++++------- drivers/media/video/s5p-fimc/fimc-capture.c | 14 ++++++------- drivers/media/video/s5p-fimc/fimc-lite.c | 4 ++-- drivers/media/video/s5p-jpeg/jpeg-core.c | 4 ++-- drivers/media/video/s5p-tv/mixer_video.c | 8 ++++---- include/linux/videodev2.h | 8 ++++++-- 7 files changed, 41 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/selection-api.xml b/Documentation/DocBook/media/v4l/selection-api.xml index b299e4779354..ac013e50e0bd 100644 --- a/Documentation/DocBook/media/v4l/selection-api.xml +++ b/Documentation/DocBook/media/v4l/selection-api.xml @@ -91,7 +91,7 @@ top/left corner at position (0,0) . The rectangle's coordinates are expressed in pixels. The top left corner, width and height of the source rectangle, that is -the area actually sampled, is given by the V4L2_SEL_TGT_CROP_ACTIVE +the area actually sampled, is given by the V4L2_SEL_TGT_CROP target. It uses the same coordinate system as V4L2_SEL_TGT_CROP_BOUNDS . The active cropping area must lie completely inside the capture boundaries. The driver may further adjust the @@ -111,7 +111,7 @@ height are equal to the image size set by VIDIOC_S_FMT . The part of a buffer into which the image is inserted by the hardware is -controlled by the V4L2_SEL_TGT_COMPOSE_ACTIVE target. +controlled by the V4L2_SEL_TGT_COMPOSE target. The rectangle's coordinates are also expressed in the same coordinate system as the bounds rectangle. The composing rectangle must lie completely inside bounds rectangle. The driver must adjust the composing rectangle to fit to the @@ -125,7 +125,7 @@ bounding rectangle. The part of a buffer that is modified by the hardware is given by V4L2_SEL_TGT_COMPOSE_PADDED . It contains all pixels -defined using V4L2_SEL_TGT_COMPOSE_ACTIVE plus all +defined using V4L2_SEL_TGT_COMPOSE plus all padding data modified by hardware during insertion process. All pixels outside this rectangle must not be changed by the hardware. The content of pixels that lie inside the padded area but outside active area is @@ -153,7 +153,7 @@ specified using VIDIOC_S_FMT ioctl. The top left corner, width and height of the source rectangle, that is the area from which image date are processed by the hardware, is given by the - V4L2_SEL_TGT_CROP_ACTIVE . Its coordinates are expressed + V4L2_SEL_TGT_CROP . Its coordinates are expressed in in the same coordinate system as the bounds rectangle. The active cropping area must lie completely inside the crop boundaries and the driver may further adjust the requested size and/or position according to hardware @@ -165,7 +165,7 @@ bounding rectangle. The part of a video signal or graphics display where the image is inserted by the hardware is controlled by -V4L2_SEL_TGT_COMPOSE_ACTIVE target. The rectangle's coordinates +V4L2_SEL_TGT_COMPOSE target. The rectangle's coordinates are expressed in pixels. The composing rectangle must lie completely inside the bounds rectangle. The driver must adjust the area to fit to the bounding limits. Moreover, the driver can perform other adjustments according to @@ -184,7 +184,7 @@ such a padded area is driver-dependent feature not covered by this document. Driver developers are encouraged to keep padded rectangle equal to active one. The padded target is accessed by the V4L2_SEL_TGT_COMPOSE_PADDED identifier. It must contain all pixels from the -V4L2_SEL_TGT_COMPOSE_ACTIVE target. +V4L2_SEL_TGT_COMPOSE target.
@@ -193,8 +193,8 @@ V4L2_SEL_TGT_COMPOSE_ACTIVE target. Scaling control An application can detect if scaling is performed by comparing the width -and the height of rectangles obtained using V4L2_SEL_TGT_CROP_ACTIVE - and V4L2_SEL_TGT_COMPOSE_ACTIVE targets. If +and the height of rectangles obtained using V4L2_SEL_TGT_CROP + and V4L2_SEL_TGT_COMPOSE targets. If these are not equal then the scaling is applied. The application can compute the scaling ratios using these values. @@ -252,7 +252,7 @@ area) ret = ioctl(fd, &VIDIOC-G-SELECTION;, &sel); if (ret) exit(-1); - sel.target = V4L2_SEL_TGT_CROP_ACTIVE; + sel.target = V4L2_SEL_TGT_CROP; ret = ioctl(fd, &VIDIOC-S-SELECTION;, &sel); if (ret) exit(-1); @@ -281,7 +281,7 @@ area) r.left = sel.r.width / 4; r.top = sel.r.height / 4; sel.r = r; - sel.target = V4L2_SEL_TGT_COMPOSE_ACTIVE; + sel.target = V4L2_SEL_TGT_COMPOSE; sel.flags = V4L2_SEL_FLAG_LE; ret = ioctl(fd, &VIDIOC-S-SELECTION;, &sel); if (ret) @@ -298,11 +298,11 @@ V4L2_BUF_TYPE_VIDEO_OUTPUT for other devices &v4l2-selection; compose = { .type = V4L2_BUF_TYPE_VIDEO_OUTPUT, - .target = V4L2_SEL_TGT_COMPOSE_ACTIVE, + .target = V4L2_SEL_TGT_COMPOSE, }; &v4l2-selection; crop = { .type = V4L2_BUF_TYPE_VIDEO_OUTPUT, - .target = V4L2_SEL_TGT_CROP_ACTIVE, + .target = V4L2_SEL_TGT_CROP, }; double hscale, vscale; diff --git a/Documentation/DocBook/media/v4l/vidioc-g-selection.xml b/Documentation/DocBook/media/v4l/vidioc-g-selection.xml index bb04eff75f45..6376e57ff576 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-selection.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-selection.xml @@ -65,8 +65,8 @@ Do not use multiplanar buffers. Use V4L2_BUF_TYPE_VIDEO_CAPTURE . Use V4L2_BUF_TYPE_VIDEO_OUTPUT instead of V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE . The next step is setting the value of &v4l2-selection; target field -to V4L2_SEL_TGT_CROP_ACTIVE ( -V4L2_SEL_TGT_COMPOSE_ACTIVE ). Please refer to table V4L2_SEL_TGT_CROP ( +V4L2_SEL_TGT_COMPOSE ). Please refer to table or for additional targets. The flags and reserved fields of &v4l2-selection; are ignored and they must be filled @@ -86,8 +86,8 @@ use multiplanar buffers. Use V4L2_BUF_TYPE_VIDEO_CAPTURE . Use V4L2_BUF_TYPE_VIDEO_OUTPUT instead of V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE . The next step is setting the value of &v4l2-selection; target to -V4L2_SEL_TGT_CROP_ACTIVE ( -V4L2_SEL_TGT_COMPOSE_ACTIVE ). Please refer to table V4L2_SEL_TGT_CROP ( +V4L2_SEL_TGT_COMPOSE ). Please refer to table or for additional targets. The &v4l2-rect; r rectangle need to be set to the desired active area. Field &v4l2-selection; reserved @@ -161,7 +161,7 @@ exist no rectangle that satisfies the constraints. &cs-def; - V4L2_SEL_TGT_CROP_ACTIVE + V4L2_SEL_TGT_CROP 0x0000 The area that is currently cropped by hardware. @@ -176,7 +176,7 @@ exist no rectangle that satisfies the constraints. Limits for the cropping rectangle. - V4L2_SEL_TGT_COMPOSE_ACTIVE + V4L2_SEL_TGT_COMPOSE 0x0100 The area to which data is composed by hardware. @@ -193,7 +193,8 @@ exist no rectangle that satisfies the constraints. V4L2_SEL_TGT_COMPOSE_PADDED 0x0103 - The active area and all padding pixels that are inserted or modified by hardware. + The active area and all padding pixels that are inserted or + modified by hardware. diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 62ce5399c4cf..a3cd78d33913 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -655,7 +655,7 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx, r->left = r->top = 0; return; } - if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) { + if (target == V4L2_SEL_TGT_COMPOSE) { if (ctx->rotation != 90 && ctx->rotation != 270) align_h = 1; max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3)); @@ -682,7 +682,7 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx, rotate ? sink->f_height : sink->f_width); max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height); - if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) { + if (target == V4L2_SEL_TGT_COMPOSE) { min_w = min_t(u32, max_w, sink->f_width / max_sc_h); min_h = min_t(u32, max_h, sink->f_height / max_sc_v); if (rotate) { @@ -1147,9 +1147,9 @@ static int fimc_cap_g_selection(struct file *file, void *fh, s->r.height = f->o_height; return 0; - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + case V4L2_SEL_TGT_COMPOSE: f = &ctx->d_frame; - case V4L2_SEL_TGT_CROP_ACTIVE: + case V4L2_SEL_TGT_CROP: s->r.left = f->offs_h; s->r.top = f->offs_v; s->r.width = f->width; @@ -1185,9 +1185,9 @@ static int fimc_cap_s_selection(struct file *file, void *fh, if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) return -EINVAL; - if (s->target == V4L2_SEL_TGT_COMPOSE_ACTIVE) + if (s->target == V4L2_SEL_TGT_COMPOSE) f = &ctx->d_frame; - else if (s->target == V4L2_SEL_TGT_CROP_ACTIVE) + else if (s->target == V4L2_SEL_TGT_CROP) f = &ctx->s_frame; else return -EINVAL; @@ -1483,7 +1483,7 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, return -EINVAL; mutex_lock(&fimc->lock); - fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP_ACTIVE); + fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP); switch (sel->target) { case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: diff --git a/drivers/media/video/s5p-fimc/fimc-lite.c b/drivers/media/video/s5p-fimc/fimc-lite.c index 400d701aef04..52ede56e0758 100644 --- a/drivers/media/video/s5p-fimc/fimc-lite.c +++ b/drivers/media/video/s5p-fimc/fimc-lite.c @@ -871,7 +871,7 @@ static int fimc_lite_g_selection(struct file *file, void *fh, sel->r.height = f->f_height; return 0; - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + case V4L2_SEL_TGT_COMPOSE: sel->r = f->rect; return 0; } @@ -888,7 +888,7 @@ static int fimc_lite_s_selection(struct file *file, void *fh, unsigned long flags; if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || - sel->target != V4L2_SEL_TGT_COMPOSE_ACTIVE) + sel->target != V4L2_SEL_TGT_COMPOSE) return -EINVAL; fimc_lite_try_compose(fimc, &rect); diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c index e40e79b33df6..95f23024b17d 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.c +++ b/drivers/media/video/s5p-jpeg/jpeg-core.c @@ -824,10 +824,10 @@ static int s5p_jpeg_g_selection(struct file *file, void *priv, /* For JPEG blob active == default == bounds */ switch (s->target) { - case V4L2_SEL_TGT_CROP_ACTIVE: + case V4L2_SEL_TGT_CROP: case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_DEFAULT: s->r.width = ctx->out_q.w; s->r.height = ctx->out_q.h; diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index 33fde2a763ec..6c74b05d1f95 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c @@ -367,7 +367,7 @@ static int mxr_g_selection(struct file *file, void *fh, return -EINVAL; switch (s->target) { - case V4L2_SEL_TGT_CROP_ACTIVE: + case V4L2_SEL_TGT_CROP: s->r.left = geo->src.x_offset; s->r.top = geo->src.y_offset; s->r.width = geo->src.width; @@ -380,7 +380,7 @@ static int mxr_g_selection(struct file *file, void *fh, s->r.width = geo->src.full_width; s->r.height = geo->src.full_height; break; - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_PADDED: s->r.left = geo->dst.x_offset; s->r.top = geo->dst.y_offset; @@ -449,11 +449,11 @@ static int mxr_s_selection(struct file *file, void *fh, res.height = geo->dst.full_height; break; - case V4L2_SEL_TGT_CROP_ACTIVE: + case V4L2_SEL_TGT_CROP: target = &geo->src; stage = MXR_GEOMETRY_CROP; break; - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_PADDED: target = &geo->dst; stage = MXR_GEOMETRY_COMPOSE; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index a61edb353273..ac1ad33ba3e0 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -767,13 +767,13 @@ struct v4l2_crop { /* Selection targets */ /* Current cropping area */ -#define V4L2_SEL_TGT_CROP_ACTIVE 0x0000 +#define V4L2_SEL_TGT_CROP 0x0000 /* Default cropping area */ #define V4L2_SEL_TGT_CROP_DEFAULT 0x0001 /* Cropping bounds */ #define V4L2_SEL_TGT_CROP_BOUNDS 0x0002 /* Current composing area */ -#define V4L2_SEL_TGT_COMPOSE_ACTIVE 0x0100 +#define V4L2_SEL_TGT_COMPOSE 0x0100 /* Default composing area */ #define V4L2_SEL_TGT_COMPOSE_DEFAULT 0x0101 /* Composing bounds */ @@ -781,6 +781,10 @@ struct v4l2_crop { /* Current composing area plus all padding pixels */ #define V4L2_SEL_TGT_COMPOSE_PADDED 0x0103 +/* Backward compatibility definitions */ +#define V4L2_SEL_TGT_CROP_ACTIVE V4L2_SEL_TGT_CROP +#define V4L2_SEL_TGT_COMPOSE_ACTIVE V4L2_SEL_TGT_COMPOSE + /** * struct v4l2_selection - selection info * @type: buffer type (do not use *_MPLANE types) -- cgit v1.2.3 From 1ec0ed083988ae433305d7f4158fda8c3a1a23b9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 17 May 2012 17:50:45 -0300 Subject: [media] v4l: Remove "_ACTUAL" from subdev selection API target definition names The string "_ACTUAL" does not say anything more about the target names. Drop it. V4L2 selection API was changed by "V4L: Remove "_ACTIVE" from the selection target name definitions" by Sylwester Nawrocki. This patch does the same for the V4L2 subdev API. Signed-off-by: Sakari Ailus Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/dev-subdev.xml | 28 +++++++++++----------- .../media/v4l/vidioc-subdev-g-selection.xml | 12 +++++----- drivers/media/video/omap3isp/ispccdc.c | 4 ++-- drivers/media/video/omap3isp/isppreview.c | 4 ++-- drivers/media/video/omap3isp/ispresizer.c | 4 ++-- drivers/media/video/smiapp/smiapp-core.c | 22 ++++++++--------- drivers/media/video/v4l2-subdev.c | 4 ++-- include/linux/v4l2-subdev.h | 9 +++++-- 8 files changed, 46 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/dev-subdev.xml b/Documentation/DocBook/media/v4l/dev-subdev.xml index 4afcbbec5eda..e88d5ea8f826 100644 --- a/Documentation/DocBook/media/v4l/dev-subdev.xml +++ b/Documentation/DocBook/media/v4l/dev-subdev.xml @@ -289,9 +289,9 @@ &v4l2-rect; by the coordinates of the top left corner and the rectangle size. Both the coordinates and sizes are expressed in pixels. - As for pad formats, drivers store try and active - rectangles for the selection targets of ACTUAL type . + As for pad formats, drivers store try and active rectangles for + the selection targets . On sink pads, cropping is applied relative to the current pad format. The pad format represents the image size as @@ -308,7 +308,7 @@ Scaling support is optional. When supported by a subdev, the crop rectangle on the subdev's sink pad is scaled to the size configured using the &VIDIOC-SUBDEV-S-SELECTION; IOCTL - using V4L2_SUBDEV_SEL_COMPOSE_ACTUAL + using V4L2_SUBDEV_SEL_TGT_COMPOSE selection target on the same pad. If the subdev supports scaling but not composing, the top and left values are not used and must always be set to zero. @@ -333,22 +333,22 @@ Types of selection targets
- ACTUAL targets + Actual targets - ACTUAL targets reflect the actual hardware configuration - at any point of time. There is a BOUNDS target - corresponding to every ACTUAL. + Actual targets (without a postfix) reflect the actual + hardware configuration at any point of time. There is a BOUNDS + target corresponding to every actual target.
BOUNDS targets - BOUNDS targets is the smallest rectangle that contains - all valid ACTUAL rectangles. It may not be possible to set the - ACTUAL rectangle as large as the BOUNDS rectangle, however. - This may be because e.g. a sensor's pixel array is not - rectangular but cross-shaped or round. The maximum size may - also be smaller than the BOUNDS rectangle. + BOUNDS targets is the smallest rectangle that contains all + valid actual rectangles. It may not be possible to set the actual + rectangle as large as the BOUNDS rectangle, however. This may be + because e.g. a sensor's pixel array is not rectangular but + cross-shaped or round. The maximum size may also be smaller than the + BOUNDS rectangle.
diff --git a/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml index 208e9f0da3f3..4c44808ab25c 100644 --- a/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml +++ b/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml @@ -72,10 +72,10 @@
Types of selection targets - There are two types of selection targets: actual and bounds. - The ACTUAL targets are the targets which configure the hardware. - The BOUNDS target will return a rectangle that contain all - possible ACTUAL rectangles. + There are two types of selection targets: actual and bounds. The + actual targets are the targets which configure the hardware. The BOUNDS + target will return a rectangle that contain all possible actual + rectangles.
@@ -93,7 +93,7 @@ &cs-def; - V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL + V4L2_SUBDEV_SEL_TGT_CROP 0x0000 Actual crop. Defines the cropping performed by the processing step. @@ -104,7 +104,7 @@ Bounds of the crop rectangle. - V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL + V4L2_SUBDEV_SEL_TGT_COMPOSE 0x0100 Actual compose rectangle. Used to configure scaling on sink pads and composition on source pads. diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index 7e32331b60fb..f19774f8396a 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -2024,7 +2024,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ccdc_try_crop(ccdc, format, &sel->r); break; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP: sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); break; @@ -2052,7 +2052,7 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP || sel->pad != CCDC_PAD_SOURCE_OF) return -EINVAL; diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index 8a4935ecc655..1086f6a9ff76 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -1960,7 +1960,7 @@ static int preview_get_selection(struct v4l2_subdev *sd, preview_try_crop(prev, format, &sel->r); break; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP: sel->r = *__preview_get_crop(prev, fh, sel->which); break; @@ -1988,7 +1988,7 @@ static int preview_set_selection(struct v4l2_subdev *sd, struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP || sel->pad != PREV_PAD_SINK) return -EINVAL; diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c index 14041c9c8643..945665295571 100644 --- a/drivers/media/video/omap3isp/ispresizer.c +++ b/drivers/media/video/omap3isp/ispresizer.c @@ -1259,7 +1259,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, resizer_calc_ratios(res, &sel->r, format_source, &ratio); break; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP: sel->r = *__resizer_get_crop(res, fh, sel->which); resizer_calc_ratios(res, &sel->r, format_source, &ratio); break; @@ -1293,7 +1293,7 @@ static int resizer_set_selection(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format_sink, *format_source; struct resizer_ratio ratio; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP || sel->pad != RESZ_PAD_SINK) return -EINVAL; diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index e8c93c89265a..37622bb6c667 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -1630,7 +1630,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev, smiapp_get_crop_compose(subdev, fh, crops, &comp, which); switch (target) { - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP: comp->width = crops[SMIAPP_PAD_SINK]->width; comp->height = crops[SMIAPP_PAD_SINK]->height; if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { @@ -1646,7 +1646,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev, } } /* Fall through */ - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_COMPOSE: *crops[SMIAPP_PAD_SRC] = *comp; break; default: @@ -1722,7 +1722,7 @@ static int smiapp_set_format(struct v4l2_subdev *subdev, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) ssd->sink_fmt = *crops[ssd->sink_pad]; smiapp_propagate(subdev, fh, fmt->which, - V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL); + V4L2_SUBDEV_SEL_TGT_CROP); mutex_unlock(&sensor->mutex); @@ -1957,7 +1957,7 @@ static int smiapp_set_compose(struct v4l2_subdev *subdev, *comp = sel->r; smiapp_propagate(subdev, fh, sel->which, - V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL); + V4L2_SUBDEV_SEL_TGT_COMPOSE); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) return smiapp_update_mode(sensor); @@ -1973,7 +1973,7 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev, /* We only implement crop in three places. */ switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP: case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: if (ssd == sensor->pixel_array && sel->pad == SMIAPP_PA_PAD_SRC) @@ -1987,7 +1987,7 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev, == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) return 0; return -EINVAL; - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_COMPOSE: case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: if (sel->pad == ssd->source_pad) return -EINVAL; @@ -2050,7 +2050,7 @@ static int smiapp_set_crop(struct v4l2_subdev *subdev, if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) smiapp_propagate(subdev, fh, sel->which, - V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL); + V4L2_SUBDEV_SEL_TGT_CROP); return 0; } @@ -2096,11 +2096,11 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev, sel->r = *comp; } break; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP: case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: sel->r = *crops[sel->pad]; break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_COMPOSE: sel->r = *comp; break; } @@ -2147,10 +2147,10 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev, sel->r.height); switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP: ret = smiapp_set_crop(subdev, fh, sel); break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_COMPOSE: ret = smiapp_set_compose(subdev, fh, sel); break; default: diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index db6e859b93d4..cd86f0c3ec74 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -245,7 +245,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; - sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; + sel.target = V4L2_SUBDEV_SEL_TGT_CROP; rval = v4l2_subdev_call( sd, pad, get_selection, subdev_fh, &sel); @@ -274,7 +274,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; - sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; + sel.target = V4L2_SUBDEV_SEL_TGT_CROP; sel.r = crop->rect; rval = v4l2_subdev_call( diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h index 812019ee1e06..3cbe6889fcb5 100644 --- a/include/linux/v4l2-subdev.h +++ b/include/linux/v4l2-subdev.h @@ -128,14 +128,19 @@ struct v4l2_subdev_frame_interval_enum { #define V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG (1 << 2) /* active cropping area */ -#define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL 0x0000 +#define V4L2_SUBDEV_SEL_TGT_CROP 0x0000 /* cropping bounds */ #define V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS 0x0002 /* current composing area */ -#define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL 0x0100 +#define V4L2_SUBDEV_SEL_TGT_COMPOSE 0x0100 /* composing bounds */ #define V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS 0x0102 +/* backward compatibility definitions */ +#define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL \ + V4L2_SUBDEV_SEL_TGT_CROP +#define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL \ + V4L2_SUBDEV_SEL_TGT_COMPOSE /** * struct v4l2_subdev_selection - selection info -- cgit v1.2.3 From 5689b28890f4a7c4e12290dbf2c29a9d23047335 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 18 May 2012 09:31:18 -0300 Subject: [media] v4l: Unify selection targets across V4L2 and V4L2 subdev interfaces Change the users of V4L2_SUBDEV_SEL_TGT_* targets to use V4L2_SEL_TGT_* instead. The common definitions are moved to a new header file, include/linux/v4l2-common.h. Signed-off-by: Sakari Ailus Signed-off-by: Sylwester Nawrocki Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispccdc.c | 6 +-- drivers/media/video/omap3isp/isppreview.c | 6 +-- drivers/media/video/omap3isp/ispresizer.c | 6 +-- drivers/media/video/s5p-fimc/fimc-capture.c | 18 ++++----- drivers/media/video/s5p-fimc/fimc-lite.c | 11 +++--- drivers/media/video/smiapp/smiapp-core.c | 30 +++++++-------- drivers/media/video/v4l2-subdev.c | 4 +- include/linux/v4l2-common.h | 57 +++++++++++++++++++++++++++++ include/linux/v4l2-subdev.h | 19 ++-------- include/linux/videodev2.h | 25 ++----------- 10 files changed, 103 insertions(+), 79 deletions(-) create mode 100644 include/linux/v4l2-common.h (limited to 'include') diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index f19774f8396a..82df7a06dc36 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -2014,7 +2014,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return -EINVAL; switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: sel->r.left = 0; sel->r.top = 0; sel->r.width = INT_MAX; @@ -2024,7 +2024,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ccdc_try_crop(ccdc, format, &sel->r); break; - case V4L2_SUBDEV_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP: sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); break; @@ -2052,7 +2052,7 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP || + if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != CCDC_PAD_SOURCE_OF) return -EINVAL; diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index 1086f6a9ff76..6fa70f4e8ea4 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -1949,7 +1949,7 @@ static int preview_get_selection(struct v4l2_subdev *sd, return -EINVAL; switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: sel->r.left = 0; sel->r.top = 0; sel->r.width = INT_MAX; @@ -1960,7 +1960,7 @@ static int preview_get_selection(struct v4l2_subdev *sd, preview_try_crop(prev, format, &sel->r); break; - case V4L2_SUBDEV_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP: sel->r = *__preview_get_crop(prev, fh, sel->which); break; @@ -1988,7 +1988,7 @@ static int preview_set_selection(struct v4l2_subdev *sd, struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP || + if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != PREV_PAD_SINK) return -EINVAL; diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c index 945665295571..ae17d917f77b 100644 --- a/drivers/media/video/omap3isp/ispresizer.c +++ b/drivers/media/video/omap3isp/ispresizer.c @@ -1249,7 +1249,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, sel->which); switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: sel->r.left = 0; sel->r.top = 0; sel->r.width = INT_MAX; @@ -1259,7 +1259,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd, resizer_calc_ratios(res, &sel->r, format_source, &ratio); break; - case V4L2_SUBDEV_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP: sel->r = *__resizer_get_crop(res, fh, sel->which); resizer_calc_ratios(res, &sel->r, format_source, &ratio); break; @@ -1293,7 +1293,7 @@ static int resizer_set_selection(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format_sink, *format_source; struct resizer_ratio ratio; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP || + if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != RESZ_PAD_SINK) return -EINVAL; diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index a3cd78d33913..521e3715b9ee 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -1429,9 +1429,9 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, mutex_lock(&fimc->lock); switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: f = &ctx->d_frame; - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: r->width = f->o_width; r->height = f->o_height; r->left = 0; @@ -1439,10 +1439,10 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, mutex_unlock(&fimc->lock); return 0; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SEL_TGT_CROP: try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SEL_TGT_COMPOSE: try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); f = &ctx->d_frame; break; @@ -1486,9 +1486,9 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP); switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: f = &ctx->d_frame; - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: r->width = f->o_width; r->height = f->o_height; r->left = 0; @@ -1496,10 +1496,10 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, mutex_unlock(&fimc->lock); return 0; - case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SEL_TGT_CROP: try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SEL_TGT_COMPOSE: try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); f = &ctx->d_frame; break; @@ -1515,7 +1515,7 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, set_frame_crop(f, r->left, r->top, r->width, r->height); set_bit(ST_CAPT_APPLY_CFG, &fimc->state); spin_unlock_irqrestore(&fimc->slock, flags); - if (sel->target == V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL) + if (sel->target == V4L2_SEL_TGT_COMPOSE) ctx->state |= FIMC_COMPOSE; } diff --git a/drivers/media/video/s5p-fimc/fimc-lite.c b/drivers/media/video/s5p-fimc/fimc-lite.c index 52ede56e0758..8785089c4460 100644 --- a/drivers/media/video/s5p-fimc/fimc-lite.c +++ b/drivers/media/video/s5p-fimc/fimc-lite.c @@ -1086,9 +1086,9 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, struct fimc_lite *fimc = v4l2_get_subdevdata(sd); struct flite_frame *f = &fimc->inp_frame; - if ((sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL && - sel->target != V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS) || - sel->pad != FLITE_SD_PAD_SINK) + if ((sel->target != V4L2_SEL_TGT_CROP && + sel->target != V4L2_SEL_TGT_CROP_BOUNDS) || + sel->pad != FLITE_SD_PAD_SINK) return -EINVAL; if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { @@ -1097,7 +1097,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, } mutex_lock(&fimc->lock); - if (sel->target == V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL) { + if (sel->target == V4L2_SEL_TGT_CROP) { sel->r = f->rect; } else { sel->r.left = 0; @@ -1122,8 +1122,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, struct flite_frame *f = &fimc->inp_frame; int ret = 0; - if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || - sel->pad != FLITE_SD_PAD_SINK) + if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != FLITE_SD_PAD_SINK) return -EINVAL; mutex_lock(&fimc->lock); diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index 37622bb6c667..9bbb5d3f003b 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -1630,7 +1630,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev, smiapp_get_crop_compose(subdev, fh, crops, &comp, which); switch (target) { - case V4L2_SUBDEV_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP: comp->width = crops[SMIAPP_PAD_SINK]->width; comp->height = crops[SMIAPP_PAD_SINK]->height; if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { @@ -1646,7 +1646,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev, } } /* Fall through */ - case V4L2_SUBDEV_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE: *crops[SMIAPP_PAD_SRC] = *comp; break; default: @@ -1722,7 +1722,7 @@ static int smiapp_set_format(struct v4l2_subdev *subdev, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) ssd->sink_fmt = *crops[ssd->sink_pad]; smiapp_propagate(subdev, fh, fmt->which, - V4L2_SUBDEV_SEL_TGT_CROP); + V4L2_SEL_TGT_CROP); mutex_unlock(&sensor->mutex); @@ -1957,7 +1957,7 @@ static int smiapp_set_compose(struct v4l2_subdev *subdev, *comp = sel->r; smiapp_propagate(subdev, fh, sel->which, - V4L2_SUBDEV_SEL_TGT_COMPOSE); + V4L2_SEL_TGT_COMPOSE); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) return smiapp_update_mode(sensor); @@ -1973,8 +1973,8 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev, /* We only implement crop in three places. */ switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP: - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: if (ssd == sensor->pixel_array && sel->pad == SMIAPP_PA_PAD_SRC) return 0; @@ -1987,8 +1987,8 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev, == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) return 0; return -EINVAL; - case V4L2_SUBDEV_SEL_TGT_COMPOSE: - case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: if (sel->pad == ssd->source_pad) return -EINVAL; if (ssd == sensor->binner) @@ -2050,7 +2050,7 @@ static int smiapp_set_crop(struct v4l2_subdev *subdev, if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) smiapp_propagate(subdev, fh, sel->which, - V4L2_SUBDEV_SEL_TGT_CROP); + V4L2_SEL_TGT_CROP); return 0; } @@ -2084,7 +2084,7 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev, } switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_BOUNDS: if (ssd == sensor->pixel_array) { sel->r.width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; @@ -2096,11 +2096,11 @@ static int __smiapp_get_selection(struct v4l2_subdev *subdev, sel->r = *comp; } break; - case V4L2_SUBDEV_SEL_TGT_CROP: - case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: sel->r = *crops[sel->pad]; break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE: sel->r = *comp; break; } @@ -2147,10 +2147,10 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev, sel->r.height); switch (sel->target) { - case V4L2_SUBDEV_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP: ret = smiapp_set_crop(subdev, fh, sel); break; - case V4L2_SUBDEV_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE: ret = smiapp_set_compose(subdev, fh, sel); break; default: diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index cd86f0c3ec74..9182f81deb5b 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -245,7 +245,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; - sel.target = V4L2_SUBDEV_SEL_TGT_CROP; + sel.target = V4L2_SEL_TGT_CROP; rval = v4l2_subdev_call( sd, pad, get_selection, subdev_fh, &sel); @@ -274,7 +274,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; - sel.target = V4L2_SUBDEV_SEL_TGT_CROP; + sel.target = V4L2_SEL_TGT_CROP; sel.r = crop->rect; rval = v4l2_subdev_call( diff --git a/include/linux/v4l2-common.h b/include/linux/v4l2-common.h new file mode 100644 index 000000000000..e85bf15b5994 --- /dev/null +++ b/include/linux/v4l2-common.h @@ -0,0 +1,57 @@ +/* + * include/linux/v4l2-common.h + * + * Common V4L2 and V4L2 subdev definitions. + * + * Users are advised to #include this file either through videodev2.h + * (V4L2) or through v4l2-subdev.h (V4L2 subdev) rather than to refer + * to this file directly. + * + * Copyright (C) 2012 Nokia Corporation + * Contact: Sakari Ailus + * + * 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __V4L2_COMMON__ +#define __V4L2_COMMON__ + +/* Selection target definitions */ + +/* Current cropping area */ +#define V4L2_SEL_TGT_CROP 0x0000 +/* Default cropping area */ +#define V4L2_SEL_TGT_CROP_DEFAULT 0x0001 +/* Cropping bounds */ +#define V4L2_SEL_TGT_CROP_BOUNDS 0x0002 +/* Current composing area */ +#define V4L2_SEL_TGT_COMPOSE 0x0100 +/* Default composing area */ +#define V4L2_SEL_TGT_COMPOSE_DEFAULT 0x0101 +/* Composing bounds */ +#define V4L2_SEL_TGT_COMPOSE_BOUNDS 0x0102 +/* Current composing area plus all padding pixels */ +#define V4L2_SEL_TGT_COMPOSE_PADDED 0x0103 + +/* Backward compatibility definitions */ +#define V4L2_SEL_TGT_CROP_ACTIVE V4L2_SEL_TGT_CROP +#define V4L2_SEL_TGT_COMPOSE_ACTIVE V4L2_SEL_TGT_COMPOSE +#define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL \ + V4L2_SEL_TGT_CROP +#define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL \ + V4L2_SEL_TGT_COMPOSE + +#endif /* __V4L2_COMMON__ */ diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h index 3cbe6889fcb5..1d7d45739260 100644 --- a/include/linux/v4l2-subdev.h +++ b/include/linux/v4l2-subdev.h @@ -25,6 +25,7 @@ #include #include +#include #include /** @@ -127,27 +128,13 @@ struct v4l2_subdev_frame_interval_enum { #define V4L2_SUBDEV_SEL_FLAG_SIZE_LE (1 << 1) #define V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG (1 << 2) -/* active cropping area */ -#define V4L2_SUBDEV_SEL_TGT_CROP 0x0000 -/* cropping bounds */ -#define V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS 0x0002 -/* current composing area */ -#define V4L2_SUBDEV_SEL_TGT_COMPOSE 0x0100 -/* composing bounds */ -#define V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS 0x0102 - -/* backward compatibility definitions */ -#define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL \ - V4L2_SUBDEV_SEL_TGT_CROP -#define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL \ - V4L2_SUBDEV_SEL_TGT_COMPOSE - /** * struct v4l2_subdev_selection - selection info * * @which: either V4L2_SUBDEV_FORMAT_ACTIVE or V4L2_SUBDEV_FORMAT_TRY * @pad: pad number, as reported by the media API - * @target: selection target, used to choose one of possible rectangles + * @target: Selection target, used to choose one of possible rectangles, + * defined in v4l2-common.h; V4L2_SEL_TGT_* . * @flags: constraint flags * @r: coordinates of the selection window * @reserved: for future use, set to zero for now diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index ac1ad33ba3e0..7fdb8710c831 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -64,6 +64,7 @@ #include #include #include +#include /* * Common stuff for both V4L1 and V4L2 @@ -764,31 +765,11 @@ struct v4l2_crop { #define V4L2_SEL_FLAG_GE 0x00000001 #define V4L2_SEL_FLAG_LE 0x00000002 -/* Selection targets */ - -/* Current cropping area */ -#define V4L2_SEL_TGT_CROP 0x0000 -/* Default cropping area */ -#define V4L2_SEL_TGT_CROP_DEFAULT 0x0001 -/* Cropping bounds */ -#define V4L2_SEL_TGT_CROP_BOUNDS 0x0002 -/* Current composing area */ -#define V4L2_SEL_TGT_COMPOSE 0x0100 -/* Default composing area */ -#define V4L2_SEL_TGT_COMPOSE_DEFAULT 0x0101 -/* Composing bounds */ -#define V4L2_SEL_TGT_COMPOSE_BOUNDS 0x0102 -/* Current composing area plus all padding pixels */ -#define V4L2_SEL_TGT_COMPOSE_PADDED 0x0103 - -/* Backward compatibility definitions */ -#define V4L2_SEL_TGT_CROP_ACTIVE V4L2_SEL_TGT_CROP -#define V4L2_SEL_TGT_COMPOSE_ACTIVE V4L2_SEL_TGT_COMPOSE - /** * struct v4l2_selection - selection info * @type: buffer type (do not use *_MPLANE types) - * @target: selection target, used to choose one of possible rectangles + * @target: Selection target, used to choose one of possible rectangles; + * defined in v4l2-common.h; V4L2_SEL_TGT_* . * @flags: constraints flags * @r: coordinates of selection window * @reserved: for future use, rounds structure size to 64 bytes, set to zero -- cgit v1.2.3 From 563df3d0bc2ca103e5ddb76c8b7b3386ed2da0d6 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 13 Jun 2012 16:01:10 -0300 Subject: [media] v4l: Unify selection flags Unify flags on the selection interfaces on V4L2 and V4L2 subdev. Flags are very similar to targets in this case: there are more similarities than differences between the two interfaces. Signed-off-by: Sakari Ailus Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/ispccdc.c | 2 +- drivers/media/video/omap3isp/isppreview.c | 2 +- drivers/media/video/smiapp/smiapp-core.c | 10 +++++----- include/linux/v4l2-common.h | 20 +++++++++++++++++--- include/linux/v4l2-subdev.h | 6 +----- include/linux/videodev2.h | 6 +----- 6 files changed, 26 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index 82df7a06dc36..f1220d3d4970 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -2064,7 +2064,7 @@ static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, * pad. If the KEEP_CONFIG flag is set, just return the current crop * rectangle. */ - if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) { + if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); return 0; } diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index 6fa70f4e8ea4..99d5cc4fd623 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -2000,7 +2000,7 @@ static int preview_set_selection(struct v4l2_subdev *sd, * pad. If the KEEP_CONFIG flag is set, just return the current crop * rectangle. */ - if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) { + if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { sel->r = *__preview_get_crop(prev, fh, sel->which); return 0; } diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index 9bbb5d3f003b..8a5d4f7932a0 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -38,9 +38,9 @@ #include "smiapp.h" -#define SMIAPP_ALIGN_DIM(dim, flags) \ - ((flags) & V4L2_SUBDEV_SEL_FLAG_SIZE_GE \ - ? ALIGN((dim), 2) \ +#define SMIAPP_ALIGN_DIM(dim, flags) \ + ((flags) & V4L2_SEL_FLAG_GE \ + ? ALIGN((dim), 2) \ : (dim) & ~1) /* @@ -1747,14 +1747,14 @@ static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, h &= ~1; ask_h &= ~1; - if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_GE) { + if (flags & V4L2_SEL_FLAG_GE) { if (w < ask_w) val -= SCALING_GOODNESS; if (h < ask_h) val -= SCALING_GOODNESS; } - if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_LE) { + if (flags & V4L2_SEL_FLAG_LE) { if (w > ask_w) val -= SCALING_GOODNESS; if (h > ask_h) diff --git a/include/linux/v4l2-common.h b/include/linux/v4l2-common.h index e85bf15b5994..0fa8b64c3cdb 100644 --- a/include/linux/v4l2-common.h +++ b/include/linux/v4l2-common.h @@ -29,7 +29,11 @@ #ifndef __V4L2_COMMON__ #define __V4L2_COMMON__ -/* Selection target definitions */ +/* + * + * Selection interface definitions + * + */ /* Current cropping area */ #define V4L2_SEL_TGT_CROP 0x0000 @@ -46,7 +50,7 @@ /* Current composing area plus all padding pixels */ #define V4L2_SEL_TGT_COMPOSE_PADDED 0x0103 -/* Backward compatibility definitions */ +/* Backward compatibility target definitions --- to be removed. */ #define V4L2_SEL_TGT_CROP_ACTIVE V4L2_SEL_TGT_CROP #define V4L2_SEL_TGT_COMPOSE_ACTIVE V4L2_SEL_TGT_COMPOSE #define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL \ @@ -54,4 +58,14 @@ #define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL \ V4L2_SEL_TGT_COMPOSE -#endif /* __V4L2_COMMON__ */ +/* Selection flags */ +#define V4L2_SEL_FLAG_GE (1 << 0) +#define V4L2_SEL_FLAG_LE (1 << 1) +#define V4L2_SEL_FLAG_KEEP_CONFIG (1 << 2) + +/* Backward compatibility flag definitions --- to be removed. */ +#define V4L2_SUBDEV_SEL_FLAG_SIZE_GE V4L2_SEL_FLAG_GE +#define V4L2_SUBDEV_SEL_FLAG_SIZE_LE V4L2_SEL_FLAG_LE +#define V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG V4L2_SEL_FLAG_KEEP_CONFIG + +#endif /* __V4L2_COMMON__ */ diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h index 1d7d45739260..8c57ee9872bb 100644 --- a/include/linux/v4l2-subdev.h +++ b/include/linux/v4l2-subdev.h @@ -124,10 +124,6 @@ struct v4l2_subdev_frame_interval_enum { __u32 reserved[9]; }; -#define V4L2_SUBDEV_SEL_FLAG_SIZE_GE (1 << 0) -#define V4L2_SUBDEV_SEL_FLAG_SIZE_LE (1 << 1) -#define V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG (1 << 2) - /** * struct v4l2_subdev_selection - selection info * @@ -135,7 +131,7 @@ struct v4l2_subdev_frame_interval_enum { * @pad: pad number, as reported by the media API * @target: Selection target, used to choose one of possible rectangles, * defined in v4l2-common.h; V4L2_SEL_TGT_* . - * @flags: constraint flags + * @flags: constraint flags, defined in v4l2-common.h; V4L2_SEL_FLAG_*. * @r: coordinates of the selection window * @reserved: for future use, set to zero for now * diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 7fdb8710c831..5d78910f926c 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -761,16 +761,12 @@ struct v4l2_crop { struct v4l2_rect c; }; -/* Hints for adjustments of selection rectangle */ -#define V4L2_SEL_FLAG_GE 0x00000001 -#define V4L2_SEL_FLAG_LE 0x00000002 - /** * struct v4l2_selection - selection info * @type: buffer type (do not use *_MPLANE types) * @target: Selection target, used to choose one of possible rectangles; * defined in v4l2-common.h; V4L2_SEL_TGT_* . - * @flags: constraints flags + * @flags: constraints flags, defined in v4l2-common.h; V4L2_SEL_FLAG_*. * @r: coordinates of selection window * @reserved: for future use, rounds structure size to 64 bytes, set to zero * -- cgit v1.2.3 From 6680884a44207efe8ef6bc56b1c932cce2b89994 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Fri, 6 Jul 2012 10:44:19 -0700 Subject: Input: ad7879 - add option to correct xy axis Sebastian Zenker reported that driver swaps x and y samples when the touchscreen leads are connected in accordance with the datasheet specification. Transposed axis can be typically corrected by touch screen calibration however this bug also negatively influences touch pressure measurements. Add an option to correct x and y axis. Signed-off-by: Michael Hennerich Reported-and-tested-by: Sebastian Zenker Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7879.c | 5 +++++ include/linux/spi/ad7879.h | 2 ++ 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index e2482b40da51..e60709261951 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -118,6 +118,7 @@ struct ad7879 { unsigned int irq; bool disabled; /* P: input->mutex */ bool suspended; /* P: input->mutex */ + bool swap_xy; u16 conversion_data[AD7879_NR_SENSE]; char phys[32]; u8 first_conversion_delay; @@ -161,6 +162,9 @@ static int ad7879_report(struct ad7879 *ts) z1 = ts->conversion_data[AD7879_SEQ_Z1] & MAX_12BIT; z2 = ts->conversion_data[AD7879_SEQ_Z2] & MAX_12BIT; + if (ts->swap_xy) + swap(x, y); + /* * The samples processed here are already preprocessed by the AD7879. * The preprocessing function consists of a median and an averaging @@ -520,6 +524,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, ts->dev = dev; ts->input = input_dev; ts->irq = irq; + ts->swap_xy = pdata->swap_xy; setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); diff --git a/include/linux/spi/ad7879.h b/include/linux/spi/ad7879.h index 6334cee1a3be..58368be0b4c0 100644 --- a/include/linux/spi/ad7879.h +++ b/include/linux/spi/ad7879.h @@ -12,6 +12,8 @@ struct ad7879_platform_data { u16 y_min, y_max; u16 pressure_min, pressure_max; + bool swap_xy; /* swap x and y axes */ + /* [0..255] 0=OFF Starts at 1=550us and goes * all the way to 9.440ms in steps of 35us. */ -- cgit v1.2.3 From 3bdff937827da04b487f0a0ac6e1f3a9a1296878 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Sun, 1 Jul 2012 00:47:43 +0200 Subject: iio: cleanup buffer.h comments Signed-off-by: Peter Meerwald Signed-off-by: Jonathan Cameron --- include/linux/iio/buffer.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index ad4fb1af0f7d..2a2b6b4d8d05 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -85,7 +85,7 @@ struct iio_buffer { /** * iio_buffer_init() - Initialize the buffer structure - * @buffer: buffer to be initialized + * @buffer: buffer to be initialized **/ void iio_buffer_init(struct iio_buffer *buffer); @@ -107,8 +107,9 @@ int iio_scan_mask_query(struct iio_dev *indio_dev, /** * iio_scan_mask_set() - set particular bit in the scan mask - * @buffer: the buffer whose scan mask we are interested in - * @bit: the bit to be set. + * @indio_dev IIO device structure + * @buffer: the buffer whose scan mask we are interested in + * @bit: the bit to be set. **/ int iio_scan_mask_set(struct iio_dev *indio_dev, struct iio_buffer *buffer, int bit); @@ -116,8 +117,8 @@ int iio_scan_mask_set(struct iio_dev *indio_dev, /** * iio_push_to_buffer() - push to a registered buffer. * @buffer: IIO buffer structure for device - * @scan: Full scan. - * @timestamp: + * @data: the data to push to the buffer + * @timestamp: timestamp to associate with the data */ int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data, s64 timestamp); @@ -126,7 +127,9 @@ int iio_update_demux(struct iio_dev *indio_dev); /** * iio_buffer_register() - register the buffer with IIO core - * @indio_dev: device with the buffer to be registered + * @indio_dev: device with the buffer to be registered + * @channels: the channel descriptions used to construct buffer + * @num_channels: the number of channels **/ int iio_buffer_register(struct iio_dev *indio_dev, const struct iio_chan_spec *channels, @@ -134,7 +137,7 @@ int iio_buffer_register(struct iio_dev *indio_dev, /** * iio_buffer_unregister() - unregister the buffer from IIO core - * @indio_dev: the device with the buffer to be unregistered + * @indio_dev: the device with the buffer to be unregistered **/ void iio_buffer_unregister(struct iio_dev *indio_dev); -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 From dae8a969d512ee15e08fbec7837b9dab1777896d Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Mon, 25 Jun 2012 10:34:36 +0200 Subject: mfd: Add Maxim 77686 driver This patch is device driver for MAX77686 chip. MAX77686 is PMIC and includes regulator and rtc on it. This driver is core of MAX77686 chip, so provides common support for accessing on-chip devices. It uses irq_domain to manage irq and regmap to read/write data to its register with i2c bus. Signed-off-by: Chiwoong Byun Signed-off-by: Jonghwa Lee Signed-off-by: Myungjoo Ham Signed-off-by: Kyungmin Park Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 13 ++ drivers/mfd/Makefile | 1 + drivers/mfd/max77686-irq.c | 309 +++++++++++++++++++++++++++++++++++ drivers/mfd/max77686.c | 156 ++++++++++++++++++ include/linux/mfd/max77686-private.h | 247 ++++++++++++++++++++++++++++ include/linux/mfd/max77686.h | 117 +++++++++++++ 6 files changed, 843 insertions(+) create mode 100644 drivers/mfd/max77686-irq.c create mode 100644 drivers/mfd/max77686.c create mode 100644 include/linux/mfd/max77686-private.h create mode 100644 include/linux/mfd/max77686.h (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index e129c820df7d..a7d0c851afc5 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -422,6 +422,19 @@ config PMIC_ADP5520 individual components like LCD backlight, LEDs, GPIOs and Kepad under the corresponding menus. +config MFD_MAX77686 + bool "Maxim Semiconductor MAX77686 PMIC Support" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + select REGMAP_I2C + select IRQ_DOMAIN + help + Say yes here to support for Maxim Semiconductor MAX77686. + This is a Power Management IC with RTC on chip. + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the functionality + of the device. + config MFD_MAX77693 bool "Maxim Semiconductor MAX77693 PMIC Support" depends on I2C=y && GENERIC_HARDIRQS diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 75f6ed68a4b9..8ee7a3bf595b 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-core.o obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o +obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o diff --git a/drivers/mfd/max77686-irq.c b/drivers/mfd/max77686-irq.c new file mode 100644 index 000000000000..fc101220f990 --- /dev/null +++ b/drivers/mfd/max77686-irq.c @@ -0,0 +1,309 @@ +/* + * max77686-irq.c - Interrupt controller support for MAX77686 + * + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Chiwoong Byun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max8997-irq.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + MAX77686_DEBUG_IRQ_INFO = 1 << 0, + MAX77686_DEBUG_IRQ_MASK = 1 << 1, + MAX77686_DEBUG_IRQ_INT = 1 << 2, +}; + +static int debug_mask = 0; +module_param(debug_mask, int, 0); +MODULE_PARM_DESC(debug_mask, "Set debug_mask : 0x0=off 0x1=IRQ_INFO 0x2=IRQ_MASK 0x4=IRQ_INI)"); + +static const u8 max77686_mask_reg[] = { + [PMIC_INT1] = MAX77686_REG_INT1MSK, + [PMIC_INT2] = MAX77686_REG_INT2MSK, + [RTC_INT] = MAX77686_RTC_INTM, +}; + +static struct regmap *max77686_get_regmap(struct max77686_dev *max77686, + enum max77686_irq_source src) +{ + switch (src) { + case PMIC_INT1 ... PMIC_INT2: + return max77686->regmap; + case RTC_INT: + return max77686->rtc_regmap; + default: + return ERR_PTR(-EINVAL); + } +} + +struct max77686_irq_data { + int mask; + enum max77686_irq_source group; +}; + +#define DECLARE_IRQ(idx, _group, _mask) \ + [(idx)] = { .group = (_group), .mask = (_mask) } +static const struct max77686_irq_data max77686_irqs[] = { + DECLARE_IRQ(MAX77686_PMICIRQ_PWRONF, PMIC_INT1, 1 << 0), + DECLARE_IRQ(MAX77686_PMICIRQ_PWRONR, PMIC_INT1, 1 << 1), + DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBF, PMIC_INT1, 1 << 2), + DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBR, PMIC_INT1, 1 << 3), + DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBF, PMIC_INT1, 1 << 4), + DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBR, PMIC_INT1, 1 << 5), + DECLARE_IRQ(MAX77686_PMICIRQ_ONKEY1S, PMIC_INT1, 1 << 6), + DECLARE_IRQ(MAX77686_PMICIRQ_MRSTB, PMIC_INT1, 1 << 7), + DECLARE_IRQ(MAX77686_PMICIRQ_140C, PMIC_INT2, 1 << 0), + DECLARE_IRQ(MAX77686_PMICIRQ_120C, PMIC_INT2, 1 << 1), + DECLARE_IRQ(MAX77686_RTCIRQ_RTC60S, RTC_INT, 1 << 0), + DECLARE_IRQ(MAX77686_RTCIRQ_RTCA1, RTC_INT, 1 << 1), + DECLARE_IRQ(MAX77686_RTCIRQ_RTCA2, RTC_INT, 1 << 2), + DECLARE_IRQ(MAX77686_RTCIRQ_SMPL, RTC_INT, 1 << 3), + DECLARE_IRQ(MAX77686_RTCIRQ_RTC1S, RTC_INT, 1 << 4), + DECLARE_IRQ(MAX77686_RTCIRQ_WTSR, RTC_INT, 1 << 5), +}; + +static void max77686_irq_lock(struct irq_data *data) +{ + struct max77686_dev *max77686 = irq_get_chip_data(data->irq); + + if (debug_mask & MAX77686_DEBUG_IRQ_MASK) + pr_info("%s\n", __func__); + + mutex_lock(&max77686->irqlock); +} + +static void max77686_irq_sync_unlock(struct irq_data *data) +{ + struct max77686_dev *max77686 = irq_get_chip_data(data->irq); + int i; + + for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) { + u8 mask_reg = max77686_mask_reg[i]; + struct regmap *map = max77686_get_regmap(max77686, i); + + if (debug_mask & MAX77686_DEBUG_IRQ_MASK) + pr_debug("%s: mask_reg[%d]=0x%x, cur=0x%x\n", + __func__, i, mask_reg, max77686->irq_masks_cur[i]); + + if (mask_reg == MAX77686_REG_INVALID || + IS_ERR_OR_NULL(map)) + continue; + + max77686->irq_masks_cache[i] = max77686->irq_masks_cur[i]; + + regmap_write(map, max77686_mask_reg[i], + max77686->irq_masks_cur[i]); + } + + mutex_unlock(&max77686->irqlock); +} + +static const inline struct max77686_irq_data *to_max77686_irq(int irq) +{ + struct irq_data *data = irq_get_irq_data(irq); + return &max77686_irqs[data->hwirq]; +} + +static void max77686_irq_mask(struct irq_data *data) +{ + struct max77686_dev *max77686 = irq_get_chip_data(data->irq); + const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq); + + max77686->irq_masks_cur[irq_data->group] |= irq_data->mask; + + if (debug_mask & MAX77686_DEBUG_IRQ_MASK) + pr_info("%s: group=%d, cur=0x%x\n", + __func__, irq_data->group, + max77686->irq_masks_cur[irq_data->group]); +} + +static void max77686_irq_unmask(struct irq_data *data) +{ + struct max77686_dev *max77686 = irq_get_chip_data(data->irq); + const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq); + + max77686->irq_masks_cur[irq_data->group] &= ~irq_data->mask; + + if (debug_mask & MAX77686_DEBUG_IRQ_MASK) + pr_info("%s: group=%d, cur=0x%x\n", + __func__, irq_data->group, + max77686->irq_masks_cur[irq_data->group]); +} + +static struct irq_chip max77686_irq_chip = { + .name = "max77686", + .irq_bus_lock = max77686_irq_lock, + .irq_bus_sync_unlock = max77686_irq_sync_unlock, + .irq_mask = max77686_irq_mask, + .irq_unmask = max77686_irq_unmask, +}; + +static irqreturn_t max77686_irq_thread(int irq, void *data) +{ + struct max77686_dev *max77686 = data; + unsigned int irq_reg[MAX77686_IRQ_GROUP_NR] = {}; + unsigned int irq_src; + int ret; + int i, cur_irq; + + ret = regmap_read(max77686->regmap, MAX77686_REG_INTSRC, &irq_src); + if (ret < 0) { + dev_err(max77686->dev, "Failed to read interrupt source: %d\n", + ret); + return IRQ_NONE; + } + + if (debug_mask & MAX77686_DEBUG_IRQ_INT) + pr_info("%s: irq_src=0x%x\n", __func__, irq_src); + + if (irq_src == MAX77686_IRQSRC_PMIC) { + ret = regmap_bulk_read(max77686->rtc_regmap, + MAX77686_REG_INT1, irq_reg, 2); + if (ret < 0) { + dev_err(max77686->dev, "Failed to read interrupt source: %d\n", + ret); + return IRQ_NONE; + } + + if (debug_mask & MAX77686_DEBUG_IRQ_INT) + pr_info("%s: int1=0x%x, int2=0x%x\n", __func__, + irq_reg[PMIC_INT1], irq_reg[PMIC_INT2]); + } + + if (irq_src & MAX77686_IRQSRC_RTC) { + ret = regmap_read(max77686->rtc_regmap, + MAX77686_RTC_INT, &irq_reg[RTC_INT]); + if (ret < 0) { + dev_err(max77686->dev, "Failed to read interrupt source: %d\n", + ret); + return IRQ_NONE; + } + + if (debug_mask & MAX77686_DEBUG_IRQ_INT) + pr_info("%s: rtc int=0x%x\n", __func__, + irq_reg[RTC_INT]); + + } + + for (i = 0; i < MAX77686_IRQ_NR; i++) { + if (irq_reg[max77686_irqs[i].group] & max77686_irqs[i].mask) { + cur_irq = irq_find_mapping(max77686->irq_domain, i); + if (cur_irq) + handle_nested_irq(cur_irq); + } + } + + return IRQ_HANDLED; +} + +static int max77686_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct max77686_dev *max77686 = d->host_data; + + irq_set_chip_data(irq, max77686); + irq_set_chip_and_handler(irq, &max77686_irq_chip, handle_edge_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + return 0; +} + +static struct irq_domain_ops max77686_irq_domain_ops = { + .map = max77686_irq_domain_map, +}; + +int max77686_irq_init(struct max77686_dev *max77686) +{ + struct irq_domain *domain; + int i; + int ret; + int val; + struct regmap *map; + + mutex_init(&max77686->irqlock); + + max77686->irq = gpio_to_irq(max77686->irq_gpio); + + if (debug_mask & MAX77686_DEBUG_IRQ_INT) { + ret = gpio_request(max77686->irq_gpio, "pmic_irq"); + if (ret < 0) { + dev_err(max77686->dev, + "Failed to request gpio %d with ret: %d\n", + max77686->irq_gpio, ret); + return IRQ_NONE; + } + + gpio_direction_input(max77686->irq_gpio); + val = gpio_get_value(max77686->irq_gpio); + gpio_free(max77686->irq_gpio); + pr_info("%s: gpio_irq=%x\n", __func__, val); + } + + /* Mask individual interrupt sources */ + for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) { + max77686->irq_masks_cur[i] = 0xff; + max77686->irq_masks_cache[i] = 0xff; + map = max77686_get_regmap(max77686, i); + + if (IS_ERR_OR_NULL(map)) + continue; + if (max77686_mask_reg[i] == MAX77686_REG_INVALID) + continue; + + regmap_write(map, max77686_mask_reg[i], 0xff); + } + domain = irq_domain_add_linear(NULL, MAX77686_IRQ_NR, + &max77686_irq_domain_ops, max77686); + if (!domain) { + dev_err(max77686->dev, "could not create irq domain\n"); + return -ENODEV; + } + max77686->irq_domain = domain; + + ret = request_threaded_irq(max77686->irq, NULL, max77686_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "max77686-irq", max77686); + + if (ret) + dev_err(max77686->dev, "Failed to request IRQ %d: %d\n", + max77686->irq, ret); + + + if (debug_mask & MAX77686_DEBUG_IRQ_INFO) + pr_info("%s-\n", __func__); + + return 0; +} + +void max77686_irq_exit(struct max77686_dev *max77686) +{ + if (max77686->irq) + free_irq(max77686->irq, max77686); +} diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c new file mode 100644 index 000000000000..11e56017e0b0 --- /dev/null +++ b/drivers/mfd/max77686.c @@ -0,0 +1,156 @@ +/* + * max77686.c - mfd core driver for the Maxim 77686 + * + * Copyright (C) 2012 Samsung Electronics + * Chiwoong Byun + * Jonghwa Lee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max8997.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define I2C_ADDR_RTC (0x0C >> 1) + +static struct mfd_cell max77686_devs[] = { + { .name = "max77686-pmic", }, + { .name = "max77686-rtc", }, +}; + +static struct regmap_config max77686_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int max77686_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max77686_dev *max77686; + struct max77686_platform_data *pdata = i2c->dev.platform_data; + unsigned int data; + int ret = 0; + + max77686 = kzalloc(sizeof(struct max77686_dev), GFP_KERNEL); + if (max77686 == NULL) + return -ENOMEM; + + max77686->regmap = regmap_init_i2c(i2c, &max77686_regmap_config); + if (IS_ERR(max77686->regmap)) { + ret = PTR_ERR(max77686->regmap); + dev_err(max77686->dev, "Failed to allocate register map: %d\n", + ret); + kfree(max77686); + return ret; + } + + i2c_set_clientdata(i2c, max77686); + max77686->dev = &i2c->dev; + max77686->i2c = i2c; + max77686->type = id->driver_data; + + if (!pdata) { + ret = -EIO; + goto err; + } + + max77686->wakeup = pdata->wakeup; + max77686->irq_gpio = pdata->irq_gpio; + + mutex_init(&max77686->iolock); + + if (regmap_read(max77686->regmap, + MAX77686_REG_DEVICE_ID, &data) < 0) { + dev_err(max77686->dev, + "device not found on this channel (this is not an error)\n"); + ret = -ENODEV; + goto err; + } else + dev_info(max77686->dev, "device found\n"); + + max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC); + i2c_set_clientdata(max77686->rtc, max77686); + + max77686_irq_init(max77686); + + ret = mfd_add_devices(max77686->dev, -1, max77686_devs, + ARRAY_SIZE(max77686_devs), NULL, 0); + + if (ret < 0) + goto err_mfd; + + return ret; + +err_mfd: + mfd_remove_devices(max77686->dev); + i2c_unregister_device(max77686->rtc); +err: + kfree(max77686); + return ret; +} + +static int max77686_i2c_remove(struct i2c_client *i2c) +{ + struct max77686_dev *max77686 = i2c_get_clientdata(i2c); + + mfd_remove_devices(max77686->dev); + i2c_unregister_device(max77686->rtc); + kfree(max77686); + + return 0; +} + +static const struct i2c_device_id max77686_i2c_id[] = { + { "max77686", TYPE_MAX77686 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max77686_i2c_id); + +static struct i2c_driver max77686_i2c_driver = { + .driver = { + .name = "max77686", + .owner = THIS_MODULE, + }, + .probe = max77686_i2c_probe, + .remove = max77686_i2c_remove, + .id_table = max77686_i2c_id, +}; + +static int __init max77686_i2c_init(void) +{ + return i2c_add_driver(&max77686_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(max77686_i2c_init); + +static void __exit max77686_i2c_exit(void) +{ + i2c_del_driver(&max77686_i2c_driver); +} +module_exit(max77686_i2c_exit); + +MODULE_DESCRIPTION("MAXIM 77686 multi-function core driver"); +MODULE_AUTHOR("Chiwoong Byun "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/max77686-private.h b/include/linux/mfd/max77686-private.h new file mode 100644 index 000000000000..962f65e72b71 --- /dev/null +++ b/include/linux/mfd/max77686-private.h @@ -0,0 +1,247 @@ +/* + * max77686.h - Voltage regulator driver for the Maxim 77686 + * + * Copyright (C) 2012 Samsung Electrnoics + * Chiwoong Byun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __LINUX_MFD_MAX77686_PRIV_H +#define __LINUX_MFD_MAX77686_PRIV_H + +#include +#include +#include + +#define MAX77686_REG_INVALID (0xff) + +enum max77686_pmic_reg { + MAX77686_REG_DEVICE_ID = 0x00, + MAX77686_REG_INTSRC = 0x01, + MAX77686_REG_INT1 = 0x02, + MAX77686_REG_INT2 = 0x03, + + MAX77686_REG_INT1MSK = 0x04, + MAX77686_REG_INT2MSK = 0x05, + + MAX77686_REG_STATUS1 = 0x06, + MAX77686_REG_STATUS2 = 0x07, + + MAX77686_REG_PWRON = 0x08, + MAX77686_REG_ONOFF_DELAY = 0x09, + MAX77686_REG_MRSTB = 0x0A, + /* Reserved: 0x0B-0x0F */ + + MAX77686_REG_BUCK1CTRL = 0x10, + MAX77686_REG_BUCK1OUT = 0x11, + MAX77686_REG_BUCK2CTRL1 = 0x12, + MAX77686_REG_BUCK234FREQ = 0x13, + MAX77686_REG_BUCK2DVS1 = 0x14, + MAX77686_REG_BUCK2DVS2 = 0x15, + MAX77686_REG_BUCK2DVS3 = 0x16, + MAX77686_REG_BUCK2DVS4 = 0x17, + MAX77686_REG_BUCK2DVS5 = 0x18, + MAX77686_REG_BUCK2DVS6 = 0x19, + MAX77686_REG_BUCK2DVS7 = 0x1A, + MAX77686_REG_BUCK2DVS8 = 0x1B, + MAX77686_REG_BUCK3CTRL1 = 0x1C, + /* Reserved: 0x1D */ + MAX77686_REG_BUCK3DVS1 = 0x1E, + MAX77686_REG_BUCK3DVS2 = 0x1F, + MAX77686_REG_BUCK3DVS3 = 0x20, + MAX77686_REG_BUCK3DVS4 = 0x21, + MAX77686_REG_BUCK3DVS5 = 0x22, + MAX77686_REG_BUCK3DVS6 = 0x23, + MAX77686_REG_BUCK3DVS7 = 0x24, + MAX77686_REG_BUCK3DVS8 = 0x25, + MAX77686_REG_BUCK4CTRL1 = 0x26, + /* Reserved: 0x27 */ + MAX77686_REG_BUCK4DVS1 = 0x28, + MAX77686_REG_BUCK4DVS2 = 0x29, + MAX77686_REG_BUCK4DVS3 = 0x2A, + MAX77686_REG_BUCK4DVS4 = 0x2B, + MAX77686_REG_BUCK4DVS5 = 0x2C, + MAX77686_REG_BUCK4DVS6 = 0x2D, + MAX77686_REG_BUCK4DVS7 = 0x2E, + MAX77686_REG_BUCK4DVS8 = 0x2F, + MAX77686_REG_BUCK5CTRL = 0x30, + MAX77686_REG_BUCK5OUT = 0x31, + MAX77686_REG_BUCK6CTRL = 0x32, + MAX77686_REG_BUCK6OUT = 0x33, + MAX77686_REG_BUCK7CTRL = 0x34, + MAX77686_REG_BUCK7OUT = 0x35, + MAX77686_REG_BUCK8CTRL = 0x36, + MAX77686_REG_BUCK8OUT = 0x37, + MAX77686_REG_BUCK9CTRL = 0x38, + MAX77686_REG_BUCK9OUT = 0x39, + /* Reserved: 0x3A-0x3F */ + + MAX77686_REG_LDO1CTRL1 = 0x40, + MAX77686_REG_LDO2CTRL1 = 0x41, + MAX77686_REG_LDO3CTRL1 = 0x42, + MAX77686_REG_LDO4CTRL1 = 0x43, + MAX77686_REG_LDO5CTRL1 = 0x44, + MAX77686_REG_LDO6CTRL1 = 0x45, + MAX77686_REG_LDO7CTRL1 = 0x46, + MAX77686_REG_LDO8CTRL1 = 0x47, + MAX77686_REG_LDO9CTRL1 = 0x48, + MAX77686_REG_LDO10CTRL1 = 0x49, + MAX77686_REG_LDO11CTRL1 = 0x4A, + MAX77686_REG_LDO12CTRL1 = 0x4B, + MAX77686_REG_LDO13CTRL1 = 0x4C, + MAX77686_REG_LDO14CTRL1 = 0x4D, + MAX77686_REG_LDO15CTRL1 = 0x4E, + MAX77686_REG_LDO16CTRL1 = 0x4F, + MAX77686_REG_LDO17CTRL1 = 0x50, + MAX77686_REG_LDO18CTRL1 = 0x51, + MAX77686_REG_LDO19CTRL1 = 0x52, + MAX77686_REG_LDO20CTRL1 = 0x53, + MAX77686_REG_LDO21CTRL1 = 0x54, + MAX77686_REG_LDO22CTRL1 = 0x55, + MAX77686_REG_LDO23CTRL1 = 0x56, + MAX77686_REG_LDO24CTRL1 = 0x57, + MAX77686_REG_LDO25CTRL1 = 0x58, + MAX77686_REG_LDO26CTRL1 = 0x59, + /* Reserved: 0x5A-0x5F */ + MAX77686_REG_LDO1CTRL2 = 0x60, + MAX77686_REG_LDO2CTRL2 = 0x61, + MAX77686_REG_LDO3CTRL2 = 0x62, + MAX77686_REG_LDO4CTRL2 = 0x63, + MAX77686_REG_LDO5CTRL2 = 0x64, + MAX77686_REG_LDO6CTRL2 = 0x65, + MAX77686_REG_LDO7CTRL2 = 0x66, + MAX77686_REG_LDO8CTRL2 = 0x67, + MAX77686_REG_LDO9CTRL2 = 0x68, + MAX77686_REG_LDO10CTRL2 = 0x69, + MAX77686_REG_LDO11CTRL2 = 0x6A, + MAX77686_REG_LDO12CTRL2 = 0x6B, + MAX77686_REG_LDO13CTRL2 = 0x6C, + MAX77686_REG_LDO14CTRL2 = 0x6D, + MAX77686_REG_LDO15CTRL2 = 0x6E, + MAX77686_REG_LDO16CTRL2 = 0x6F, + MAX77686_REG_LDO17CTRL2 = 0x70, + MAX77686_REG_LDO18CTRL2 = 0x71, + MAX77686_REG_LDO19CTRL2 = 0x72, + MAX77686_REG_LDO20CTRL2 = 0x73, + MAX77686_REG_LDO21CTRL2 = 0x74, + MAX77686_REG_LDO22CTRL2 = 0x75, + MAX77686_REG_LDO23CTRL2 = 0x76, + MAX77686_REG_LDO24CTRL2 = 0x77, + MAX77686_REG_LDO25CTRL2 = 0x78, + MAX77686_REG_LDO26CTRL2 = 0x79, + /* Reserved: 0x7A-0x7D */ + + MAX77686_REG_BBAT_CHG = 0x7E, + MAX77686_REG_32KHZ = 0x7F, + + MAX77686_REG_PMIC_END = 0x80, +}; + +enum max77686_rtc_reg { + MAX77686_RTC_INT = 0x00, + MAX77686_RTC_INTM = 0x01, + MAX77686_RTC_CONTROLM = 0x02, + MAX77686_RTC_CONTROL = 0x03, + MAX77686_RTC_UPDATE0 = 0x04, + /* Reserved: 0x5 */ + MAX77686_WTSR_SMPL_CNTL = 0x06, + MAX77686_RTC_SEC = 0x07, + MAX77686_RTC_MIN = 0x08, + MAX77686_RTC_HOUR = 0x09, + MAX77686_RTC_WEEKDAY = 0x0A, + MAX77686_RTC_MONTH = 0x0B, + MAX77686_RTC_YEAR = 0x0C, + MAX77686_RTC_DATE = 0x0D, + MAX77686_ALARM1_SEC = 0x0E, + MAX77686_ALARM1_MIN = 0x0F, + MAX77686_ALARM1_HOUR = 0x10, + MAX77686_ALARM1_WEEKDAY = 0x11, + MAX77686_ALARM1_MONTH = 0x12, + MAX77686_ALARM1_YEAR = 0x13, + MAX77686_ALARM1_DATE = 0x14, + MAX77686_ALARM2_SEC = 0x15, + MAX77686_ALARM2_MIN = 0x16, + MAX77686_ALARM2_HOUR = 0x17, + MAX77686_ALARM2_WEEKDAY = 0x18, + MAX77686_ALARM2_MONTH = 0x19, + MAX77686_ALARM2_YEAR = 0x1A, + MAX77686_ALARM2_DATE = 0x1B, +}; + +#define MAX77686_IRQSRC_PMIC (0) +#define MAX77686_IRQSRC_RTC (1 << 0) + +enum max77686_irq_source { + PMIC_INT1 = 0, + PMIC_INT2, + RTC_INT, + + MAX77686_IRQ_GROUP_NR, +}; + +enum max77686_irq { + MAX77686_PMICIRQ_PWRONF, + MAX77686_PMICIRQ_PWRONR, + MAX77686_PMICIRQ_JIGONBF, + MAX77686_PMICIRQ_JIGONBR, + MAX77686_PMICIRQ_ACOKBF, + MAX77686_PMICIRQ_ACOKBR, + MAX77686_PMICIRQ_ONKEY1S, + MAX77686_PMICIRQ_MRSTB, + + MAX77686_PMICIRQ_140C, + MAX77686_PMICIRQ_120C, + + MAX77686_RTCIRQ_RTC60S, + MAX77686_RTCIRQ_RTCA1, + MAX77686_RTCIRQ_RTCA2, + MAX77686_RTCIRQ_SMPL, + MAX77686_RTCIRQ_RTC1S, + MAX77686_RTCIRQ_WTSR, + + MAX77686_IRQ_NR, +}; + +struct max77686_dev { + struct device *dev; + struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */ + struct i2c_client *rtc; /* slave addr 0x0c */ + struct mutex iolock; + + int type; + + struct regmap *regmap; /* regmap for mfd */ + struct regmap *rtc_regmap; /* regmap for rtc */ + + struct irq_domain *irq_domain; + + int irq; + int irq_gpio; + bool wakeup; + struct mutex irqlock; + int irq_masks_cur[MAX77686_IRQ_GROUP_NR]; + int irq_masks_cache[MAX77686_IRQ_GROUP_NR]; +}; + +enum max77686_types { + TYPE_MAX77686, +}; + +extern int max77686_irq_init(struct max77686_dev *max77686); +extern void max77686_irq_exit(struct max77686_dev *max77686); +extern int max77686_irq_resume(struct max77686_dev *max77686); + +#endif /* __LINUX_MFD_MAX77686_PRIV_H */ diff --git a/include/linux/mfd/max77686.h b/include/linux/mfd/max77686.h new file mode 100644 index 000000000000..fcf312678cac --- /dev/null +++ b/include/linux/mfd/max77686.h @@ -0,0 +1,117 @@ +/* + * max77686.h - Driver for the Maxim 77686 + * + * Copyright (C) 2012 Samsung Electrnoics + * Chiwoong Byun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max8997.h + * + * MAX77686 has PMIC, RTC devices. + * The devices share the same I2C bus and included in + * this mfd driver. + */ + +#ifndef __LINUX_MFD_MAX77686_H +#define __LINUX_MFD_MAX77686_H + +#include + +/* MAX77686 regulator IDs */ +enum max77686_regulators { + MAX77686_LDO1 = 0, + MAX77686_LDO2, + MAX77686_LDO3, + MAX77686_LDO4, + MAX77686_LDO5, + MAX77686_LDO6, + MAX77686_LDO7, + MAX77686_LDO8, + MAX77686_LDO9, + MAX77686_LDO10, + MAX77686_LDO11, + MAX77686_LDO12, + MAX77686_LDO13, + MAX77686_LDO14, + MAX77686_LDO15, + MAX77686_LDO16, + MAX77686_LDO17, + MAX77686_LDO18, + MAX77686_LDO19, + MAX77686_LDO20, + MAX77686_LDO21, + MAX77686_LDO22, + MAX77686_LDO23, + MAX77686_LDO24, + MAX77686_LDO25, + MAX77686_LDO26, + MAX77686_BUCK1, + MAX77686_BUCK2, + MAX77686_BUCK3, + MAX77686_BUCK4, + MAX77686_BUCK5, + MAX77686_BUCK6, + MAX77686_BUCK7, + MAX77686_BUCK8, + MAX77686_BUCK9, + MAX77686_EN32KHZ_AP, + MAX77686_EN32KHZ_CP, + MAX77686_P32KH, + + MAX77686_REG_MAX, +}; + +struct max77686_regulator_data { + int id; + struct regulator_init_data *initdata; +}; + +enum max77686_opmode { + MAX77686_OPMODE_NORMAL, + MAX77686_OPMODE_LP, + MAX77686_OPMODE_STANDBY, +}; + +struct max77686_opmode_data { + int id; + int mode; +}; + +struct max77686_platform_data { + /* IRQ */ + int irq_gpio; + int ono; + int wakeup; + + /* ---- PMIC ---- */ + struct max77686_regulator_data *regulators; + int num_regulators; + + struct max77686_opmode_data *opmode_data; + + /* + * GPIO-DVS feature is not enabled with the current version of + * MAX77686 driver. Buck2/3/4_voltages[0] is used as the default + * voltage at probe. DVS/SELB gpios are set as OUTPUT-LOW. + */ + int buck234_gpio_dvs[3]; /* GPIO of [0]DVS1, [1]DVS2, [2]DVS3 */ + int buck234_gpio_selb[3]; /* [0]SELB2, [1]SELB3, [2]SELB4 */ + unsigned int buck2_voltage[8]; /* buckx_voltage in uV */ + unsigned int buck3_voltage[8]; + unsigned int buck4_voltage[8]; +}; + +#endif /* __LINUX_MFD_MAX77686_H */ -- cgit v1.2.3 From a7cc37a49876319b2f848290eefe3388dd82286b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 24 May 2012 16:57:46 +0800 Subject: mfd: Remove unused max77693 iolock mutex Now this driver is using regmap APIs, the iolock mutex is not used and can be removed. Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/max77693.c | 2 -- include/linux/mfd/max77693-private.h | 1 - 2 files changed, 3 deletions(-) (limited to 'include') diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c index e9e4278722f3..4055bc2d36fb 100644 --- a/drivers/mfd/max77693.c +++ b/drivers/mfd/max77693.c @@ -138,8 +138,6 @@ static int max77693_i2c_probe(struct i2c_client *i2c, max77693->wakeup = pdata->wakeup; - mutex_init(&max77693->iolock); - if (max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_PMIC_ID2, ®_data) < 0) { dev_err(max77693->dev, "device not found on this channel\n"); diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h index 68263c5fa53c..1eeae5c07915 100644 --- a/include/linux/mfd/max77693-private.h +++ b/include/linux/mfd/max77693-private.h @@ -190,7 +190,6 @@ struct max77693_dev { struct i2c_client *i2c; /* 0xCC , PMIC, Charger, Flash LED */ struct i2c_client *muic; /* 0x4A , MUIC */ struct i2c_client *haptic; /* 0x90 , Haptic */ - struct mutex iolock; int type; -- cgit v1.2.3 From 06e589efa5b75e6a38a8e8b9c6cd774b5f679cdc Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 20 Jun 2012 13:56:37 +0100 Subject: mfd: Add IRQ domain support for the AB8500 As the AB8500 is an IRQ controller in its own right, here we provide the AB8500 driver with IRQ domain support. This is required if we wish to reference any of its IRQs from a platform's Device Tree. Cc: Naga Radheshy Cc: Mattias Wallin Cc: Daniel Willerud Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 1 + drivers/mfd/ab8500-core.c | 130 ++++++++++++++++++++------------------ include/linux/mfd/abx500/ab8500.h | 5 ++ 3 files changed, 76 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ed488edb2dee..8b56c1998ab2 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -709,6 +709,7 @@ config AB8500_CORE bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU select MFD_CORE + select IRQ_DOMAIN help Select this option to enable access to AB8500 power management chip. This connects to U8500 either on the SSP/SPI bus (deprecated diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index dac0e2998603..f3af34596439 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -361,7 +362,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data) static void ab8500_irq_mask(struct irq_data *data) { struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); - int offset = data->irq - ab8500->irq_base; + int offset = data->hwirq; int index = offset / 8; int mask = 1 << (offset % 8); @@ -371,7 +372,7 @@ static void ab8500_irq_mask(struct irq_data *data) static void ab8500_irq_unmask(struct irq_data *data) { struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); - int offset = data->irq - ab8500->irq_base; + int offset = data->hwirq; int index = offset / 8; int mask = 1 << (offset % 8); @@ -510,38 +511,51 @@ static irqreturn_t ab8500_irq(int irq, void *dev) return IRQ_HANDLED; } -static int ab8500_irq_init(struct ab8500 *ab8500) +/** + * ab8500_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ + * + * @ab8500: ab8500_irq controller to operate on. + * @irq: index of the interrupt requested in the chip IRQs + * + * Useful for drivers to request their own IRQs. + */ +int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq) { - int base = ab8500->irq_base; - int irq; - int num_irqs; + if (!ab8500) + return -EINVAL; - if (is_ab9540(ab8500)) - num_irqs = AB9540_NR_IRQS; - else if (is_ab8505(ab8500)) - num_irqs = AB8505_NR_IRQS; - else - num_irqs = AB8500_NR_IRQS; + return irq_create_mapping(ab8500->domain, irq); +} +EXPORT_SYMBOL_GPL(ab8500_irq_get_virq); - for (irq = base; irq < base + num_irqs; irq++) { - irq_set_chip_data(irq, ab8500); - irq_set_chip_and_handler(irq, &ab8500_irq_chip, - handle_simple_irq); - irq_set_nested_thread(irq, 1); +static int ab8500_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hwirq) +{ + struct ab8500 *ab8500 = d->host_data; + + if (!ab8500) + return -EINVAL; + + irq_set_chip_data(virq, ab8500); + irq_set_chip_and_handler(virq, &ab8500_irq_chip, + handle_simple_irq); + irq_set_nested_thread(virq, 1); #ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); + set_irq_flags(virq, IRQF_VALID); #else - irq_set_noprobe(irq); + irq_set_noprobe(virq); #endif - } return 0; } -static void ab8500_irq_remove(struct ab8500 *ab8500) +static struct irq_domain_ops ab8500_irq_ops = { + .map = ab8500_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np) { - int base = ab8500->irq_base; - int irq; int num_irqs; if (is_ab9540(ab8500)) @@ -551,13 +565,22 @@ static void ab8500_irq_remove(struct ab8500 *ab8500) else num_irqs = AB8500_NR_IRQS; - for (irq = base; irq < base + num_irqs; irq++) { -#ifdef CONFIG_ARM - set_irq_flags(irq, 0); -#endif - irq_set_chip_and_handler(irq, NULL, NULL); - irq_set_chip_data(irq, NULL); + if (ab8500->irq_base) { + ab8500->domain = irq_domain_add_legacy( + NULL, num_irqs, ab8500->irq_base, + 0, &ab8500_irq_ops, ab8500); + } + else { + ab8500->domain = irq_domain_add_linear( + np, num_irqs, &ab8500_irq_ops, ab8500); } + + if (!ab8500->domain) { + dev_err(ab8500->dev, "Failed to create irqdomain\n"); + return -ENOSYS; + } + + return 0; } int ab8500_suspend(struct ab8500 *ab8500) @@ -1233,14 +1256,6 @@ static int __devinit ab8500_probe(struct platform_device *pdev) if (plat) ab8500->irq_base = plat->irq_base; - else if (np) - ret = of_property_read_u32(np, "stericsson,irq-base", &ab8500->irq_base); - - if (!ab8500->irq_base) { - dev_info(&pdev->dev, "couldn't find irq-base\n"); - ret = -EINVAL; - goto out_free_ab8500; - } ab8500->dev = &pdev->dev; @@ -1323,7 +1338,7 @@ static int __devinit ab8500_probe(struct platform_device *pdev) AB8500_SWITCH_OFF_STATUS, &value); if (ret < 0) return ret; - dev_info(ab8500->dev, "switch off status: %#x", value); + dev_info(ab8500->dev, "switch off status: %#x\n", value); if (plat && plat->init) plat->init(ab8500); @@ -1352,25 +1367,25 @@ static int __devinit ab8500_probe(struct platform_device *pdev) for (i = 0; i < ab8500->mask_size; i++) ab8500->mask[i] = ab8500->oldmask[i] = 0xff; - if (ab8500->irq_base) { - ret = ab8500_irq_init(ab8500); - if (ret) - goto out_freeoldmask; + ret = ab8500_irq_init(ab8500, np); + if (ret) + goto out_freeoldmask; - /* Activate this feature only in ab9540 */ - /* till tests are done on ab8500 1p2 or later*/ - if (is_ab9540(ab8500)) - ret = request_threaded_irq(ab8500->irq, NULL, + /* Activate this feature only in ab9540 */ + /* till tests are done on ab8500 1p2 or later*/ + if (is_ab9540(ab8500)) { + ret = request_threaded_irq(ab8500->irq, NULL, ab8500_hierarchical_irq, IRQF_ONESHOT | IRQF_NO_SUSPEND, "ab8500", ab8500); - else - ret = request_threaded_irq(ab8500->irq, NULL, + } + else { + ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq, IRQF_ONESHOT | IRQF_NO_SUSPEND, "ab8500", ab8500); if (ret) - goto out_removeirq; + goto out_freeoldmask; } if (!np) { @@ -1417,15 +1432,11 @@ static int __devinit ab8500_probe(struct platform_device *pdev) &ab8500_attr_group); if (ret) dev_err(ab8500->dev, "error creating sysfs entries\n"); - else - return ret; + + return ret; out_freeirq: - if (ab8500->irq_base) - free_irq(ab8500->irq, ab8500); -out_removeirq: - if (ab8500->irq_base) - ab8500_irq_remove(ab8500); + free_irq(ab8500->irq, ab8500); out_freeoldmask: kfree(ab8500->oldmask); out_freemask: @@ -1444,11 +1455,10 @@ static int __devexit ab8500_remove(struct platform_device *pdev) sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group); else sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group); + mfd_remove_devices(ab8500->dev); - if (ab8500->irq_base) { - free_irq(ab8500->irq, ab8500); - ab8500_irq_remove(ab8500); - } + free_irq(ab8500->irq, ab8500); + kfree(ab8500->oldmask); kfree(ab8500->mask); kfree(ab8500); diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 91dd3ef63e99..4ae2cd9584fb 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -9,6 +9,7 @@ #include #include +#include struct device; @@ -227,6 +228,7 @@ enum ab8500_version { * @irq_lock: genirq bus lock * @transfer_ongoing: 0 if no transfer ongoing * @irq: irq line + * @irq_domain: irq domain * @version: chip version id (e.g. ab8500 or ab9540) * @chip_id: chip revision id * @write: register write @@ -247,6 +249,7 @@ struct ab8500 { atomic_t transfer_ongoing; int irq_base; int irq; + struct irq_domain *domain; enum ab8500_version version; u8 chip_id; @@ -336,4 +339,6 @@ static inline int is_ab8500_2p0(struct ab8500 *ab) return (is_ab8500(ab) && (ab->chip_id == AB8500_CUT2P0)); } +int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq); + #endif /* MFD_AB8500_H */ -- cgit v1.2.3 From c94bb233a9fee3314dc5d9c7de9fa702e91283f2 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 29 Jun 2012 19:01:03 +0200 Subject: mfd: Make MFD core code Device Tree and IRQ domain aware During Device Tree enablement of the ab8500 and db8500-prcmu drivers, a decision was made to omit registration through the MFD API and use Device Tree directly. However, because MFD devices have a different address space and the ab8500 and db8500 both use I2C to communicate, this causes issues with address translation during execution of of_platform_populate(). So the solution is to make the MFD core aware of Device Tree and have it assign the correct node pointers instead. To make this work the MFD core also needs to be awere of IRQ domains, as Device Tree insists on IRQ domain compatibility. So, instead of providing an irq-base via platform code, in the DT case we simply look up the IRQ domain and map to the correct virtual IRQ. Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 1 + drivers/mfd/mfd-core.c | 30 ++++++++++++++++++++++++++---- include/linux/mfd/core.h | 1 + 3 files changed, 28 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 8b56c1998ab2..d43876c6da23 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -7,6 +7,7 @@ menu "Multifunction device drivers" config MFD_CORE tristate + select IRQ_DOMAIN default n config MFD_88PM860X diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index ffc3d48676ae..0c3a01cde2f7 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include int mfd_cell_enable(struct platform_device *pdev) { @@ -76,6 +78,8 @@ static int mfd_add_device(struct device *parent, int id, { struct resource *res; struct platform_device *pdev; + struct device_node *np = NULL; + struct irq_domain *domain = NULL; int ret = -ENOMEM; int r; @@ -89,6 +93,16 @@ static int mfd_add_device(struct device *parent, int id, pdev->dev.parent = parent; + if (parent->of_node && cell->of_compatible) { + for_each_child_of_node(parent->of_node, np) { + if (of_device_is_compatible(np, cell->of_compatible)) { + pdev->dev.of_node = np; + domain = irq_find_host(parent->of_node); + break; + } + } + } + if (cell->pdata_size) { ret = platform_device_add_data(pdev, cell->platform_data, cell->pdata_size); @@ -112,10 +126,18 @@ static int mfd_add_device(struct device *parent, int id, res[r].end = mem_base->start + cell->resources[r].end; } else if (cell->resources[r].flags & IORESOURCE_IRQ) { - res[r].start = irq_base + - cell->resources[r].start; - res[r].end = irq_base + - cell->resources[r].end; + if (domain) { + /* Unable to create mappings for IRQ ranges. */ + WARN_ON(cell->resources[r].start != + cell->resources[r].end); + res[r].start = res[r].end = irq_create_mapping( + domain, cell->resources[r].start); + } else { + res[r].start = irq_base + + cell->resources[r].start; + res[r].end = irq_base + + cell->resources[r].end; + } } else { res[r].parent = cell->resources[r].parent; res[r].start = cell->resources[r].start; diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 4e76163dd862..99b7eb1961b6 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -36,6 +36,7 @@ struct mfd_cell { /* platform data passed to the sub devices drivers */ void *platform_data; size_t pdata_size; + const char *of_compatible; /* * These resources can be specified relative to the parent device. -- cgit v1.2.3 From b0ab907d325f99054eb2700a8f8c50776ebfeaf9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 1 Jun 2012 16:33:19 +0100 Subject: mfd: Support for user defined wm8994 irq flags Signed-off-by: Chris Rattray Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-irq.c | 10 +++++++++- include/linux/mfd/wm8994/pdata.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c index f1837f669755..0aac4aff17a5 100644 --- a/drivers/mfd/wm8994-irq.c +++ b/drivers/mfd/wm8994-irq.c @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -139,6 +140,8 @@ static struct regmap_irq_chip wm8994_irq_chip = { int wm8994_irq_init(struct wm8994 *wm8994) { int ret; + unsigned long irqflags; + struct wm8994_pdata *pdata = wm8994->dev->platform_data; if (!wm8994->irq) { dev_warn(wm8994->dev, @@ -147,8 +150,13 @@ int wm8994_irq_init(struct wm8994 *wm8994) return 0; } + /* select user or default irq flags */ + irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; + if (pdata->irq_flags) + irqflags = pdata->irq_flags; + ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + irqflags, wm8994->irq_base, &wm8994_irq_chip, &wm8994->irq_data); if (ret != 0) { diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index 893267bb6229..f0361c031927 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -141,6 +141,7 @@ struct wm8994_pdata { struct wm8994_ldo_pdata ldo[WM8994_NUM_LDO]; int irq_base; /** Base IRQ number for WM8994, required for IRQs */ + unsigned long irq_flags; /** user irq flags */ int num_drc_cfgs; struct wm8994_drc_cfg *drc_cfgs; -- cgit v1.2.3 From 52b461b86a9f6c7a86bdcb858e1bbef089fbe6a0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 3 Jun 2012 13:37:22 +0100 Subject: mfd: Add regmap cache support for wm8350 Use the most simple possible transformation on the existing code so keep the table sitting around, further patches in this series will delete the existing cache code - the main purpose of this patch is to ensure that we always have a cache for bisection. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8350-core.c | 29 +++++++++++++-------- drivers/mfd/wm8350-i2c.c | 5 ---- drivers/mfd/wm8350-regmap.c | 56 +++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/wm8350/core.h | 7 +++++- 4 files changed, 80 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index 8a9b11ca076a..fadcbbe9e2ba 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -32,9 +32,6 @@ #include #include -#define WM8350_UNLOCK_KEY 0x0013 -#define WM8350_LOCK_KEY 0x0000 - #define WM8350_CLOCK_CONTROL_1 0x28 #define WM8350_AIF_TEST 0x74 @@ -295,15 +292,20 @@ EXPORT_SYMBOL_GPL(wm8350_block_write); */ int wm8350_reg_lock(struct wm8350 *wm8350) { - u16 key = WM8350_LOCK_KEY; int ret; + mutex_lock(®_lock_mutex); + ldbg(__func__); - mutex_lock(&io_mutex); - ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key); + + ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_LOCK_KEY); if (ret) dev_err(wm8350->dev, "lock failed\n"); - mutex_unlock(&io_mutex); + + wm8350->unlocked = false; + + mutex_unlock(®_lock_mutex); + return ret; } EXPORT_SYMBOL_GPL(wm8350_reg_lock); @@ -319,15 +321,20 @@ EXPORT_SYMBOL_GPL(wm8350_reg_lock); */ int wm8350_reg_unlock(struct wm8350 *wm8350) { - u16 key = WM8350_UNLOCK_KEY; int ret; + mutex_lock(®_lock_mutex); + ldbg(__func__); - mutex_lock(&io_mutex); - ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key); + + ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_UNLOCK_KEY); if (ret) dev_err(wm8350->dev, "unlock failed\n"); - mutex_unlock(&io_mutex); + + wm8350->unlocked = true; + + mutex_unlock(®_lock_mutex); + return ret; } EXPORT_SYMBOL_GPL(wm8350_reg_unlock); diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c index a68aceb4e48c..2e57101c8d3d 100644 --- a/drivers/mfd/wm8350-i2c.c +++ b/drivers/mfd/wm8350-i2c.c @@ -23,11 +23,6 @@ #include #include -static const struct regmap_config wm8350_regmap = { - .reg_bits = 8, - .val_bits = 16, -}; - static int wm8350_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { diff --git a/drivers/mfd/wm8350-regmap.c b/drivers/mfd/wm8350-regmap.c index e965139e5cd5..7974cadaa422 100644 --- a/drivers/mfd/wm8350-regmap.c +++ b/drivers/mfd/wm8350-regmap.c @@ -3433,3 +3433,59 @@ const struct wm8350_reg_access wm8350_reg_io_map[] = { { 0x0000, 0x0000, 0x0000 }, /* R254 */ { 0x0000, 0x0000, 0x0000 }, /* R255 */ }; + +static bool wm8350_readable(struct device *dev, unsigned int reg) +{ + return wm8350_reg_io_map[reg].readable; +} + +static bool wm8350_writeable(struct device *dev, unsigned int reg) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + + if (!wm8350->unlocked) { + if ((reg >= WM8350_GPIO_FUNCTION_SELECT_1 && + reg <= WM8350_GPIO_FUNCTION_SELECT_4) || + (reg >= WM8350_BATTERY_CHARGER_CONTROL_1 && + reg <= WM8350_BATTERY_CHARGER_CONTROL_3)) + return false; + } + + return wm8350_reg_io_map[reg].writable; +} + +static bool wm8350_volatile(struct device *dev, unsigned int reg) +{ + return wm8350_reg_io_map[reg].vol; +} + +static bool wm8350_precious(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8350_SYSTEM_INTERRUPTS: + case WM8350_INT_STATUS_1: + case WM8350_INT_STATUS_2: + case WM8350_POWER_UP_INT_STATUS: + case WM8350_UNDER_VOLTAGE_INT_STATUS: + case WM8350_OVER_CURRENT_INT_STATUS: + case WM8350_GPIO_INT_STATUS: + case WM8350_COMPARATOR_INT_STATUS: + return true; + + default: + return false; + } +} + +const struct regmap_config wm8350_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .cache_type = REGCACHE_RBTREE, + + .max_register = WM8350_MAX_REGISTER, + .readable_reg = wm8350_readable, + .writeable_reg = wm8350_writeable, + .volatile_reg = wm8350_volatile, + .precious_reg = wm8350_precious, +}; diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h index 9192b6404a73..cba9bc8f947b 100644 --- a/include/linux/mfd/wm8350/core.h +++ b/include/linux/mfd/wm8350/core.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -66,6 +67,9 @@ #define WM8350_MAX_REGISTER 0xFF +#define WM8350_UNLOCK_KEY 0x0013 +#define WM8350_LOCK_KEY 0x0000 + /* * Field Definitions. */ @@ -582,6 +586,7 @@ #define WM8350_NUM_IRQ_REGS 7 +extern const struct regmap_config wm8350_regmap; struct wm8350_reg_access { u16 readable; /* Mask of readable bits */ u16 writable; /* Mask of writable bits */ @@ -602,7 +607,6 @@ extern const u16 wm8352_mode2_defaults[]; extern const u16 wm8352_mode3_defaults[]; struct wm8350; -struct regmap; struct wm8350_hwmon { struct platform_device *pdev; @@ -615,6 +619,7 @@ struct wm8350 { /* device IO */ struct regmap *regmap; u16 *reg_cache; + bool unlocked; struct mutex auxadc_mutex; struct completion auxadc_done; -- cgit v1.2.3 From 19d57ed5a308472a02e773f33c03ad4cb2ec6a9d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 3 Jun 2012 13:37:24 +0100 Subject: mfd: Remove custom wm8350 cache implementation Since none of the users now reference the cache directly we can happily remove the custom cache code and rely on the regmap cache. For simplicity we don't bother with the register defaults tables but instead read the defaults from the device - regmap is capable of doing this, unlike our old cache infrastructure. This saves a lot of code and allows us to cache the device revision information too. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8350-core.c | 325 +--- drivers/mfd/wm8350-regmap.c | 3166 +-------------------------------------- include/linux/mfd/wm8350/core.h | 19 - 3 files changed, 18 insertions(+), 3492 deletions(-) (limited to 'include') diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index fadcbbe9e2ba..7c1ae24605d9 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -60,181 +60,32 @@ /* * WM8350 Device IO */ -static DEFINE_MUTEX(io_mutex); static DEFINE_MUTEX(reg_lock_mutex); -/* Perform a physical read from the device. - */ -static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs, - u16 *dest) -{ - int i, ret; - int bytes = num_regs * 2; - - dev_dbg(wm8350->dev, "volatile read\n"); - ret = regmap_raw_read(wm8350->regmap, reg, dest, bytes); - - for (i = reg; i < reg + num_regs; i++) { - /* Cache is CPU endian */ - dest[i - reg] = be16_to_cpu(dest[i - reg]); - - /* Mask out non-readable bits */ - dest[i - reg] &= wm8350_reg_io_map[i].readable; - } - - dump(num_regs, dest); - - return ret; -} - -static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest) -{ - int i; - int end = reg + num_regs; - int ret = 0; - int bytes = num_regs * 2; - - if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) { - dev_err(wm8350->dev, "invalid reg %x\n", - reg + num_regs - 1); - return -EINVAL; - } - - dev_dbg(wm8350->dev, - "%s R%d(0x%2.2x) %d regs\n", __func__, reg, reg, num_regs); - -#if WM8350_BUS_DEBUG - /* we can _safely_ read any register, but warn if read not supported */ - for (i = reg; i < end; i++) { - if (!wm8350_reg_io_map[i].readable) - dev_warn(wm8350->dev, - "reg R%d is not readable\n", i); - } -#endif - - /* if any volatile registers are required, then read back all */ - for (i = reg; i < end; i++) - if (wm8350_reg_io_map[i].vol) - return wm8350_phys_read(wm8350, reg, num_regs, dest); - - /* no volatiles, then cache is good */ - dev_dbg(wm8350->dev, "cache read\n"); - memcpy(dest, &wm8350->reg_cache[reg], bytes); - dump(num_regs, dest); - return ret; -} - -static inline int is_reg_locked(struct wm8350 *wm8350, u8 reg) -{ - if (reg == WM8350_SECURITY || - wm8350->reg_cache[WM8350_SECURITY] == WM8350_UNLOCK_KEY) - return 0; - - if ((reg >= WM8350_GPIO_FUNCTION_SELECT_1 && - reg <= WM8350_GPIO_FUNCTION_SELECT_4) || - (reg >= WM8350_BATTERY_CHARGER_CONTROL_1 && - reg <= WM8350_BATTERY_CHARGER_CONTROL_3)) - return 1; - return 0; -} - -static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src) -{ - int i; - int end = reg + num_regs; - int bytes = num_regs * 2; - - if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) { - dev_err(wm8350->dev, "invalid reg %x\n", - reg + num_regs - 1); - return -EINVAL; - } - - /* it's generally not a good idea to write to RO or locked registers */ - for (i = reg; i < end; i++) { - if (!wm8350_reg_io_map[i].writable) { - dev_err(wm8350->dev, - "attempted write to read only reg R%d\n", i); - return -EINVAL; - } - - if (is_reg_locked(wm8350, i)) { - dev_err(wm8350->dev, - "attempted write to locked reg R%d\n", i); - return -EINVAL; - } - - src[i - reg] &= wm8350_reg_io_map[i].writable; - - wm8350->reg_cache[i] = - (wm8350->reg_cache[i] & ~wm8350_reg_io_map[i].writable) - | src[i - reg]; - - src[i - reg] = cpu_to_be16(src[i - reg]); - } - - /* Actually write it out */ - return regmap_raw_write(wm8350->regmap, reg, src, bytes); -} - /* * Safe read, modify, write methods */ int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask) { - u16 data; - int err; - - mutex_lock(&io_mutex); - err = wm8350_read(wm8350, reg, 1, &data); - if (err) { - dev_err(wm8350->dev, "read from reg R%d failed\n", reg); - goto out; - } - - data &= ~mask; - err = wm8350_write(wm8350, reg, 1, &data); - if (err) - dev_err(wm8350->dev, "write to reg R%d failed\n", reg); -out: - mutex_unlock(&io_mutex); - return err; + return regmap_update_bits(wm8350->regmap, reg, mask, 0); } EXPORT_SYMBOL_GPL(wm8350_clear_bits); int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask) { - u16 data; - int err; - - mutex_lock(&io_mutex); - err = wm8350_read(wm8350, reg, 1, &data); - if (err) { - dev_err(wm8350->dev, "read from reg R%d failed\n", reg); - goto out; - } - - data |= mask; - err = wm8350_write(wm8350, reg, 1, &data); - if (err) - dev_err(wm8350->dev, "write to reg R%d failed\n", reg); -out: - mutex_unlock(&io_mutex); - return err; + return regmap_update_bits(wm8350->regmap, reg, mask, mask); } EXPORT_SYMBOL_GPL(wm8350_set_bits); u16 wm8350_reg_read(struct wm8350 *wm8350, int reg) { - u16 data; + unsigned int data; int err; - mutex_lock(&io_mutex); - err = wm8350_read(wm8350, reg, 1, &data); + err = regmap_read(wm8350->regmap, reg, &data); if (err) dev_err(wm8350->dev, "read from reg R%d failed\n", reg); - mutex_unlock(&io_mutex); return data; } EXPORT_SYMBOL_GPL(wm8350_reg_read); @@ -242,13 +93,11 @@ EXPORT_SYMBOL_GPL(wm8350_reg_read); int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val) { int ret; - u16 data = val; - mutex_lock(&io_mutex); - ret = wm8350_write(wm8350, reg, 1, &data); + ret = regmap_write(wm8350->regmap, reg, val); + if (ret) dev_err(wm8350->dev, "write to reg R%d failed\n", reg); - mutex_unlock(&io_mutex); return ret; } EXPORT_SYMBOL_GPL(wm8350_reg_write); @@ -258,12 +107,11 @@ int wm8350_block_read(struct wm8350 *wm8350, int start_reg, int regs, { int err = 0; - mutex_lock(&io_mutex); - err = wm8350_read(wm8350, start_reg, regs, dest); + err = regmap_bulk_read(wm8350->regmap, start_reg, dest, regs); if (err) dev_err(wm8350->dev, "block read starting from R%d failed\n", start_reg); - mutex_unlock(&io_mutex); + return err; } EXPORT_SYMBOL_GPL(wm8350_block_read); @@ -273,12 +121,11 @@ int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs, { int ret = 0; - mutex_lock(&io_mutex); - ret = wm8350_write(wm8350, start_reg, regs, src); + ret = regmap_bulk_write(wm8350->regmap, start_reg, src, regs); if (ret) dev_err(wm8350->dev, "block write starting at R%d failed\n", start_reg); - mutex_unlock(&io_mutex); + return ret; } EXPORT_SYMBOL_GPL(wm8350_block_write); @@ -401,146 +248,6 @@ static irqreturn_t wm8350_auxadc_irq(int irq, void *irq_data) return IRQ_HANDLED; } -/* - * Cache is always host endian. - */ -static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode) -{ - int i, ret = 0; - u16 value; - const u16 *reg_map; - - switch (type) { - case 0: - switch (mode) { -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0 - case 0: - reg_map = wm8350_mode0_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1 - case 1: - reg_map = wm8350_mode1_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2 - case 2: - reg_map = wm8350_mode2_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3 - case 3: - reg_map = wm8350_mode3_defaults; - break; -#endif - default: - dev_err(wm8350->dev, - "WM8350 configuration mode %d not supported\n", - mode); - return -EINVAL; - } - break; - - case 1: - switch (mode) { -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0 - case 0: - reg_map = wm8351_mode0_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1 - case 1: - reg_map = wm8351_mode1_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2 - case 2: - reg_map = wm8351_mode2_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3 - case 3: - reg_map = wm8351_mode3_defaults; - break; -#endif - default: - dev_err(wm8350->dev, - "WM8351 configuration mode %d not supported\n", - mode); - return -EINVAL; - } - break; - - case 2: - switch (mode) { -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0 - case 0: - reg_map = wm8352_mode0_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1 - case 1: - reg_map = wm8352_mode1_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2 - case 2: - reg_map = wm8352_mode2_defaults; - break; -#endif -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3 - case 3: - reg_map = wm8352_mode3_defaults; - break; -#endif - default: - dev_err(wm8350->dev, - "WM8352 configuration mode %d not supported\n", - mode); - return -EINVAL; - } - break; - - default: - dev_err(wm8350->dev, - "WM835x configuration mode %d not supported\n", - mode); - return -EINVAL; - } - - wm8350->reg_cache = - kmalloc(sizeof(u16) * (WM8350_MAX_REGISTER + 1), GFP_KERNEL); - if (wm8350->reg_cache == NULL) - return -ENOMEM; - - /* Read the initial cache state back from the device - this is - * a PMIC so the device many not be in a virgin state and we - * can't rely on the silicon values. - */ - ret = regmap_raw_read(wm8350->regmap, 0, wm8350->reg_cache, - sizeof(u16) * (WM8350_MAX_REGISTER + 1)); - if (ret < 0) { - dev_err(wm8350->dev, - "failed to read initial cache values\n"); - goto out; - } - - /* Mask out uncacheable/unreadable bits and the audio. */ - for (i = 0; i < WM8350_MAX_REGISTER; i++) { - if (wm8350_reg_io_map[i].readable && - (i < WM8350_CLOCK_CONTROL_1 || i > WM8350_AIF_TEST)) { - value = be16_to_cpu(wm8350->reg_cache[i]); - value &= wm8350_reg_io_map[i].readable; - wm8350->reg_cache[i] = value; - } else - wm8350->reg_cache[i] = reg_map[i]; - } - -out: - kfree(wm8350->reg_cache); - return ret; -} - /* * Register a client device. This is non-fatal since there is no need to * fail the entire device init due to a single platform device failing. @@ -688,18 +395,12 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, goto err; } - ret = wm8350_create_cache(wm8350, mask_rev, mode); - if (ret < 0) { - dev_err(wm8350->dev, "Failed to create register cache\n"); - return ret; - } - mutex_init(&wm8350->auxadc_mutex); init_completion(&wm8350->auxadc_done); ret = wm8350_irq_init(wm8350, irq, pdata); if (ret < 0) - goto err_free; + goto err; if (wm8350->irq_base) { ret = request_threaded_irq(wm8350->irq_base + @@ -737,8 +438,6 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, err_irq: wm8350_irq_exit(wm8350); -err_free: - kfree(wm8350->reg_cache); err: return ret; } @@ -765,8 +464,6 @@ void wm8350_device_exit(struct wm8350 *wm8350) free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350); wm8350_irq_exit(wm8350); - - kfree(wm8350->reg_cache); } EXPORT_SYMBOL_GPL(wm8350_device_exit); diff --git a/drivers/mfd/wm8350-regmap.c b/drivers/mfd/wm8350-regmap.c index 7974cadaa422..9efc64750fb6 100644 --- a/drivers/mfd/wm8350-regmap.c +++ b/drivers/mfd/wm8350-regmap.c @@ -14,3170 +14,18 @@ #include -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8350_mode0_defaults[] = { - 0x17FF, /* R0 - Reset/ID */ - 0x1000, /* R1 - ID */ - 0x0000, /* R2 */ - 0x1002, /* R3 - System Control 1 */ - 0x0004, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 - Power Up Interrupt Status */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 - Power Up Interrupt Status Mask */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3B00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - LOUT1 Volume */ - 0x00E4, /* R105 - ROUT1 Volume */ - 0x00E4, /* R106 - LOUT2 Volume */ - 0x02E4, /* R107 - ROUT2 Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 - AIF Test */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x03FC, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0FFC, /* R134 - GPIO Configuration (i/o) */ - 0x0FFC, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0013, /* R140 - GPIO Function Select 1 */ - 0x0000, /* R141 - GPIO Function Select 2 */ - 0x0000, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x002D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0000, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0000, /* R186 - DCDC3 Control */ - 0x0000, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0000, /* R189 - DCDC4 Control */ - 0x0000, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0000, /* R195 - DCDC6 Control */ - 0x0000, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x001B, /* R203 - LDO2 Control */ - 0x0000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001B, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001B, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 */ - 0x4000, /* R220 - RAM BIST 1 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 */ - 0x0000, /* R227 */ - 0x0000, /* R228 */ - 0x0000, /* R229 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 */ - 0x0000, /* R232 */ - 0x0000, /* R233 */ - 0x0000, /* R234 */ - 0x0000, /* R235 */ - 0x0000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0000, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0000, /* R243 */ - 0x0000, /* R244 */ - 0x0000, /* R245 */ - 0x0000, /* R246 */ - 0x0000, /* R247 */ - 0x0000, /* R248 */ - 0x0000, /* R249 */ - 0x0000, /* R250 */ - 0x0000, /* R251 */ - 0x0000, /* R252 */ - 0x0000, /* R253 */ - 0x0000, /* R254 */ - 0x0000, /* R255 */ -}; -#endif - -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8350_mode1_defaults[] = { - 0x17FF, /* R0 - Reset/ID */ - 0x1000, /* R1 - ID */ - 0x0000, /* R2 */ - 0x1002, /* R3 - System Control 1 */ - 0x0014, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 - Power Up Interrupt Status */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 - Power Up Interrupt Status Mask */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3B00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - LOUT1 Volume */ - 0x00E4, /* R105 - ROUT1 Volume */ - 0x00E4, /* R106 - LOUT2 Volume */ - 0x02E4, /* R107 - ROUT2 Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 - AIF Test */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x03FC, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x00FB, /* R134 - GPIO Configuration (i/o) */ - 0x04FE, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0312, /* R140 - GPIO Function Select 1 */ - 0x1003, /* R141 - GPIO Function Select 2 */ - 0x1331, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x002D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x0062, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0026, /* R186 - DCDC3 Control */ - 0x0400, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0062, /* R189 - DCDC4 Control */ - 0x0400, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0026, /* R195 - DCDC6 Control */ - 0x0800, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x0006, /* R200 - LDO1 Control */ - 0x0400, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0006, /* R203 - LDO2 Control */ - 0x0400, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001B, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001B, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 */ - 0x4000, /* R220 - RAM BIST 1 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 */ - 0x0000, /* R227 */ - 0x0000, /* R228 */ - 0x0000, /* R229 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 */ - 0x0000, /* R232 */ - 0x0000, /* R233 */ - 0x0000, /* R234 */ - 0x0000, /* R235 */ - 0x0000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0000, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0000, /* R243 */ - 0x0000, /* R244 */ - 0x0000, /* R245 */ - 0x0000, /* R246 */ - 0x0000, /* R247 */ - 0x0000, /* R248 */ - 0x0000, /* R249 */ - 0x0000, /* R250 */ - 0x0000, /* R251 */ - 0x0000, /* R252 */ - 0x0000, /* R253 */ - 0x0000, /* R254 */ - 0x0000, /* R255 */ -}; -#endif - -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8350_mode2_defaults[] = { - 0x17FF, /* R0 - Reset/ID */ - 0x1000, /* R1 - ID */ - 0x0000, /* R2 */ - 0x1002, /* R3 - System Control 1 */ - 0x0014, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 - Power Up Interrupt Status */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 - Power Up Interrupt Status Mask */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3B00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - LOUT1 Volume */ - 0x00E4, /* R105 - ROUT1 Volume */ - 0x00E4, /* R106 - LOUT2 Volume */ - 0x02E4, /* R107 - ROUT2 Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 - AIF Test */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x03FC, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x08FB, /* R134 - GPIO Configuration (i/o) */ - 0x0CFE, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0312, /* R140 - GPIO Function Select 1 */ - 0x0003, /* R141 - GPIO Function Select 2 */ - 0x2331, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x002D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x002E, /* R186 - DCDC3 Control */ - 0x0800, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x000E, /* R189 - DCDC4 Control */ - 0x0800, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0026, /* R195 - DCDC6 Control */ - 0x0C00, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001A, /* R200 - LDO1 Control */ - 0x0800, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0010, /* R203 - LDO2 Control */ - 0x0800, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x000A, /* R206 - LDO3 Control */ - 0x0C00, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001A, /* R209 - LDO4 Control */ - 0x0800, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 */ - 0x4000, /* R220 - RAM BIST 1 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 */ - 0x0000, /* R227 */ - 0x0000, /* R228 */ - 0x0000, /* R229 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 */ - 0x0000, /* R232 */ - 0x0000, /* R233 */ - 0x0000, /* R234 */ - 0x0000, /* R235 */ - 0x0000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0000, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0000, /* R243 */ - 0x0000, /* R244 */ - 0x0000, /* R245 */ - 0x0000, /* R246 */ - 0x0000, /* R247 */ - 0x0000, /* R248 */ - 0x0000, /* R249 */ - 0x0000, /* R250 */ - 0x0000, /* R251 */ - 0x0000, /* R252 */ - 0x0000, /* R253 */ - 0x0000, /* R254 */ - 0x0000, /* R255 */ -}; -#endif - -#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8350_mode3_defaults[] = { - 0x17FF, /* R0 - Reset/ID */ - 0x1000, /* R1 - ID */ - 0x0000, /* R2 */ - 0x1000, /* R3 - System Control 1 */ - 0x0004, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 - Power Up Interrupt Status */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 - Power Up Interrupt Status Mask */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3B00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - LOUT1 Volume */ - 0x00E4, /* R105 - ROUT1 Volume */ - 0x00E4, /* R106 - LOUT2 Volume */ - 0x02E4, /* R107 - ROUT2 Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 - AIF Test */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x03FC, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0A7B, /* R134 - GPIO Configuration (i/o) */ - 0x06FE, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x1312, /* R140 - GPIO Function Select 1 */ - 0x1030, /* R141 - GPIO Function Select 2 */ - 0x2231, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x002D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x000E, /* R186 - DCDC3 Control */ - 0x0400, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0026, /* R189 - DCDC4 Control */ - 0x0400, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0026, /* R195 - DCDC6 Control */ - 0x0400, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x001C, /* R203 - LDO2 Control */ - 0x0400, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001C, /* R206 - LDO3 Control */ - 0x0400, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001F, /* R209 - LDO4 Control */ - 0x0400, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 */ - 0x4000, /* R220 - RAM BIST 1 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 */ - 0x0000, /* R227 */ - 0x0000, /* R228 */ - 0x0000, /* R229 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 */ - 0x0000, /* R232 */ - 0x0000, /* R233 */ - 0x0000, /* R234 */ - 0x0000, /* R235 */ - 0x0000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0000, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0000, /* R243 */ - 0x0000, /* R244 */ - 0x0000, /* R245 */ - 0x0000, /* R246 */ - 0x0000, /* R247 */ - 0x0000, /* R248 */ - 0x0000, /* R249 */ - 0x0000, /* R250 */ - 0x0000, /* R251 */ - 0x0000, /* R252 */ - 0x0000, /* R253 */ - 0x0000, /* R254 */ - 0x0000, /* R255 */ -}; -#endif - -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8351_mode0_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0001, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0004, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0FFC, /* R134 - GPIO Configuration (i/o) */ - 0x0FFC, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0013, /* R140 - GPIO Function Select 1 */ - 0x0000, /* R141 - GPIO Function Select 2 */ - 0x0000, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 */ - 0x0000, /* R175 */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0000, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0000, /* R186 - DCDC3 Control */ - 0x0000, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0000, /* R189 - DCDC4 Control */ - 0x0000, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 */ - 0x0000, /* R193 */ - 0x0000, /* R194 */ - 0x0000, /* R195 */ - 0x0000, /* R196 */ - 0x0006, /* R197 */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x001B, /* R203 - LDO2 Control */ - 0x0000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001B, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001B, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 - FLL Test 1 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x1000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8351_mode1_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0001, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0204, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0CFB, /* R134 - GPIO Configuration (i/o) */ - 0x0C1F, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0300, /* R140 - GPIO Function Select 1 */ - 0x1110, /* R141 - GPIO Function Select 2 */ - 0x0013, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 */ - 0x0000, /* R175 */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0C00, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0026, /* R186 - DCDC3 Control */ - 0x0400, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0062, /* R189 - DCDC4 Control */ - 0x0800, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 */ - 0x0000, /* R193 */ - 0x0000, /* R194 */ - 0x000A, /* R195 */ - 0x1000, /* R196 */ - 0x0006, /* R197 */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x0006, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0010, /* R203 - LDO2 Control */ - 0x0C00, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001F, /* R206 - LDO3 Control */ - 0x0800, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x000A, /* R209 - LDO4 Control */ - 0x0800, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 - FLL Test 1 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x1000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x1000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8351_mode2_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0001, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0214, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0110, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x09FA, /* R134 - GPIO Configuration (i/o) */ - 0x0DF6, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x1310, /* R140 - GPIO Function Select 1 */ - 0x0003, /* R141 - GPIO Function Select 2 */ - 0x2000, /* R142 - GPIO Function Select 3 */ - 0x0000, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 */ - 0x0000, /* R175 */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x001A, /* R180 - DCDC1 Control */ - 0x0800, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0056, /* R186 - DCDC3 Control */ - 0x0400, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0026, /* R189 - DCDC4 Control */ - 0x0C00, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 */ - 0x0000, /* R193 */ - 0x0000, /* R194 */ - 0x0026, /* R195 */ - 0x0C00, /* R196 */ - 0x0006, /* R197 */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0400, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0010, /* R203 - LDO2 Control */ - 0x0C00, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x0015, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001A, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 - FLL Test 1 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x1000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8351_mode3_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0001, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0204, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0010, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0BFB, /* R134 - GPIO Configuration (i/o) */ - 0x0FFD, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0310, /* R140 - GPIO Function Select 1 */ - 0x0001, /* R141 - GPIO Function Select 2 */ - 0x2300, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 */ - 0x0000, /* R175 */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0026, /* R186 - DCDC3 Control */ - 0x0800, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0062, /* R189 - DCDC4 Control */ - 0x1400, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 */ - 0x0000, /* R193 */ - 0x0000, /* R194 */ - 0x0026, /* R195 */ - 0x0400, /* R196 */ - 0x0006, /* R197 */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x0006, /* R200 - LDO1 Control */ - 0x0C00, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0016, /* R203 - LDO2 Control */ - 0x0000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x0019, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001A, /* R209 - LDO4 Control */ - 0x1000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 - FLL Test 1 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x1000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8352_mode0_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0002, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0004, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0FFC, /* R134 - GPIO Configuration (i/o) */ - 0x0FFC, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0013, /* R140 - GPIO Function Select 1 */ - 0x0000, /* R141 - GPIO Function Select 2 */ - 0x0000, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0000, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0000, /* R186 - DCDC3 Control */ - 0x0000, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0000, /* R189 - DCDC4 Control */ - 0x0000, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0000, /* R195 - DCDC6 Control */ - 0x0000, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x001B, /* R203 - LDO2 Control */ - 0x0000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001B, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001B, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x5000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ - 0x5100, /* R252 */ - 0x1000, /* R253 - DCDC6 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8352_mode1_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0002, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0204, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0BFB, /* R134 - GPIO Configuration (i/o) */ - 0x0FFF, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0300, /* R140 - GPIO Function Select 1 */ - 0x0000, /* R141 - GPIO Function Select 2 */ - 0x2300, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x0062, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0006, /* R186 - DCDC3 Control */ - 0x0800, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x0006, /* R189 - DCDC4 Control */ - 0x0C00, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0026, /* R195 - DCDC6 Control */ - 0x1000, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x0002, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x001A, /* R203 - LDO2 Control */ - 0x0000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001F, /* R206 - LDO3 Control */ - 0x0000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001F, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x5000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ - 0x5100, /* R252 */ - 0x1000, /* R253 - DCDC6 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8352_mode2_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0002, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0204, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0000, /* R129 - GPIO Pin pull up Control */ - 0x0110, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x09DA, /* R134 - GPIO Configuration (i/o) */ - 0x0DD6, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x1310, /* R140 - GPIO Function Select 1 */ - 0x0033, /* R141 - GPIO Function Select 2 */ - 0x2000, /* R142 - GPIO Function Select 3 */ - 0x0000, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x000E, /* R180 - DCDC1 Control */ - 0x0800, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0056, /* R186 - DCDC3 Control */ - 0x1800, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x000E, /* R189 - DCDC4 Control */ - 0x1000, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0026, /* R195 - DCDC6 Control */ - 0x0C00, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001C, /* R200 - LDO1 Control */ - 0x0000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0006, /* R203 - LDO2 Control */ - 0x0400, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x001C, /* R206 - LDO3 Control */ - 0x1400, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x001A, /* R209 - LDO4 Control */ - 0x0000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x5000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ - 0x5100, /* R252 */ - 0x1000, /* R253 - DCDC6 Test Controls */ -}; -#endif - -#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3 - -#undef WM8350_HAVE_CONFIG_MODE -#define WM8350_HAVE_CONFIG_MODE - -const u16 wm8352_mode3_defaults[] = { - 0x6143, /* R0 - Reset/ID */ - 0x0000, /* R1 - ID */ - 0x0002, /* R2 - Revision */ - 0x1C02, /* R3 - System Control 1 */ - 0x0204, /* R4 - System Control 2 */ - 0x0000, /* R5 - System Hibernate */ - 0x8A00, /* R6 - Interface Control */ - 0x0000, /* R7 */ - 0x8000, /* R8 - Power mgmt (1) */ - 0x0000, /* R9 - Power mgmt (2) */ - 0x0000, /* R10 - Power mgmt (3) */ - 0x2000, /* R11 - Power mgmt (4) */ - 0x0E00, /* R12 - Power mgmt (5) */ - 0x0000, /* R13 - Power mgmt (6) */ - 0x0000, /* R14 - Power mgmt (7) */ - 0x0000, /* R15 */ - 0x0000, /* R16 - RTC Seconds/Minutes */ - 0x0100, /* R17 - RTC Hours/Day */ - 0x0101, /* R18 - RTC Date/Month */ - 0x1400, /* R19 - RTC Year */ - 0x0000, /* R20 - Alarm Seconds/Minutes */ - 0x0000, /* R21 - Alarm Hours/Day */ - 0x0000, /* R22 - Alarm Date/Month */ - 0x0320, /* R23 - RTC Time Control */ - 0x0000, /* R24 - System Interrupts */ - 0x0000, /* R25 - Interrupt Status 1 */ - 0x0000, /* R26 - Interrupt Status 2 */ - 0x0000, /* R27 */ - 0x0000, /* R28 - Under Voltage Interrupt status */ - 0x0000, /* R29 - Over Current Interrupt status */ - 0x0000, /* R30 - GPIO Interrupt Status */ - 0x0000, /* R31 - Comparator Interrupt Status */ - 0x3FFF, /* R32 - System Interrupts Mask */ - 0x0000, /* R33 - Interrupt Status 1 Mask */ - 0x0000, /* R34 - Interrupt Status 2 Mask */ - 0x0000, /* R35 */ - 0x0000, /* R36 - Under Voltage Interrupt status Mask */ - 0x0000, /* R37 - Over Current Interrupt status Mask */ - 0x0000, /* R38 - GPIO Interrupt Status Mask */ - 0x0000, /* R39 - Comparator Interrupt Status Mask */ - 0x0040, /* R40 - Clock Control 1 */ - 0x0000, /* R41 - Clock Control 2 */ - 0x3A00, /* R42 - FLL Control 1 */ - 0x7086, /* R43 - FLL Control 2 */ - 0xC226, /* R44 - FLL Control 3 */ - 0x0000, /* R45 - FLL Control 4 */ - 0x0000, /* R46 */ - 0x0000, /* R47 */ - 0x0000, /* R48 - DAC Control */ - 0x0000, /* R49 */ - 0x00C0, /* R50 - DAC Digital Volume L */ - 0x00C0, /* R51 - DAC Digital Volume R */ - 0x0000, /* R52 */ - 0x0040, /* R53 - DAC LR Rate */ - 0x0000, /* R54 - DAC Clock Control */ - 0x0000, /* R55 */ - 0x0000, /* R56 */ - 0x0000, /* R57 */ - 0x4000, /* R58 - DAC Mute */ - 0x0000, /* R59 - DAC Mute Volume */ - 0x0000, /* R60 - DAC Side */ - 0x0000, /* R61 */ - 0x0000, /* R62 */ - 0x0000, /* R63 */ - 0x8000, /* R64 - ADC Control */ - 0x0000, /* R65 */ - 0x00C0, /* R66 - ADC Digital Volume L */ - 0x00C0, /* R67 - ADC Digital Volume R */ - 0x0000, /* R68 - ADC Divider */ - 0x0000, /* R69 */ - 0x0040, /* R70 - ADC LR Rate */ - 0x0000, /* R71 */ - 0x0303, /* R72 - Input Control */ - 0x0000, /* R73 - IN3 Input Control */ - 0x0000, /* R74 - Mic Bias Control */ - 0x0000, /* R75 */ - 0x0000, /* R76 - Output Control */ - 0x0000, /* R77 - Jack Detect */ - 0x0000, /* R78 - Anti Pop Control */ - 0x0000, /* R79 */ - 0x0040, /* R80 - Left Input Volume */ - 0x0040, /* R81 - Right Input Volume */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0000, /* R87 */ - 0x0800, /* R88 - Left Mixer Control */ - 0x1000, /* R89 - Right Mixer Control */ - 0x0000, /* R90 */ - 0x0000, /* R91 */ - 0x0000, /* R92 - OUT3 Mixer Control */ - 0x0000, /* R93 - OUT4 Mixer Control */ - 0x0000, /* R94 */ - 0x0000, /* R95 */ - 0x0000, /* R96 - Output Left Mixer Volume */ - 0x0000, /* R97 - Output Right Mixer Volume */ - 0x0000, /* R98 - Input Mixer Volume L */ - 0x0000, /* R99 - Input Mixer Volume R */ - 0x0000, /* R100 - Input Mixer Volume */ - 0x0000, /* R101 */ - 0x0000, /* R102 */ - 0x0000, /* R103 */ - 0x00E4, /* R104 - OUT1L Volume */ - 0x00E4, /* R105 - OUT1R Volume */ - 0x00E4, /* R106 - OUT2L Volume */ - 0x02E4, /* R107 - OUT2R Volume */ - 0x0000, /* R108 */ - 0x0000, /* R109 */ - 0x0000, /* R110 */ - 0x0000, /* R111 - BEEP Volume */ - 0x0A00, /* R112 - AI Formating */ - 0x0000, /* R113 - ADC DAC COMP */ - 0x0020, /* R114 - AI ADC Control */ - 0x0020, /* R115 - AI DAC Control */ - 0x0000, /* R116 */ - 0x0000, /* R117 */ - 0x0000, /* R118 */ - 0x0000, /* R119 */ - 0x0000, /* R120 */ - 0x0000, /* R121 */ - 0x0000, /* R122 */ - 0x0000, /* R123 */ - 0x0000, /* R124 */ - 0x0000, /* R125 */ - 0x0000, /* R126 */ - 0x0000, /* R127 */ - 0x1FFF, /* R128 - GPIO Debounce */ - 0x0010, /* R129 - GPIO Pin pull up Control */ - 0x0000, /* R130 - GPIO Pull down Control */ - 0x0000, /* R131 - GPIO Interrupt Mode */ - 0x0000, /* R132 */ - 0x0000, /* R133 - GPIO Control */ - 0x0BFB, /* R134 - GPIO Configuration (i/o) */ - 0x0FFD, /* R135 - GPIO Pin Polarity / Type */ - 0x0000, /* R136 */ - 0x0000, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0310, /* R140 - GPIO Function Select 1 */ - 0x0001, /* R141 - GPIO Function Select 2 */ - 0x2300, /* R142 - GPIO Function Select 3 */ - 0x0003, /* R143 - GPIO Function Select 4 */ - 0x0000, /* R144 - Digitiser Control (1) */ - 0x0002, /* R145 - Digitiser Control (2) */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x0000, /* R148 */ - 0x0000, /* R149 */ - 0x0000, /* R150 */ - 0x0000, /* R151 */ - 0x7000, /* R152 - AUX1 Readback */ - 0x7000, /* R153 - AUX2 Readback */ - 0x7000, /* R154 - AUX3 Readback */ - 0x7000, /* R155 - AUX4 Readback */ - 0x0000, /* R156 - USB Voltage Readback */ - 0x0000, /* R157 - LINE Voltage Readback */ - 0x0000, /* R158 - BATT Voltage Readback */ - 0x0000, /* R159 - Chip Temp Readback */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 - Generic Comparator Control */ - 0x0000, /* R164 - Generic comparator 1 */ - 0x0000, /* R165 - Generic comparator 2 */ - 0x0000, /* R166 - Generic comparator 3 */ - 0x0000, /* R167 - Generic comparator 4 */ - 0xA00F, /* R168 - Battery Charger Control 1 */ - 0x0B06, /* R169 - Battery Charger Control 2 */ - 0x0000, /* R170 - Battery Charger Control 3 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Current Sink Driver A */ - 0x0000, /* R173 - CSA Flash control */ - 0x0000, /* R174 - Current Sink Driver B */ - 0x0000, /* R175 - CSB Flash control */ - 0x0000, /* R176 - DCDC/LDO requested */ - 0x032D, /* R177 - DCDC Active options */ - 0x0000, /* R178 - DCDC Sleep options */ - 0x0025, /* R179 - Power-check comparator */ - 0x0006, /* R180 - DCDC1 Control */ - 0x0400, /* R181 - DCDC1 Timeouts */ - 0x1006, /* R182 - DCDC1 Low Power */ - 0x0018, /* R183 - DCDC2 Control */ - 0x0000, /* R184 - DCDC2 Timeouts */ - 0x0000, /* R185 */ - 0x0050, /* R186 - DCDC3 Control */ - 0x0C00, /* R187 - DCDC3 Timeouts */ - 0x0006, /* R188 - DCDC3 Low Power */ - 0x000E, /* R189 - DCDC4 Control */ - 0x0400, /* R190 - DCDC4 Timeouts */ - 0x0006, /* R191 - DCDC4 Low Power */ - 0x0008, /* R192 - DCDC5 Control */ - 0x0000, /* R193 - DCDC5 Timeouts */ - 0x0000, /* R194 */ - 0x0029, /* R195 - DCDC6 Control */ - 0x0800, /* R196 - DCDC6 Timeouts */ - 0x0006, /* R197 - DCDC6 Low Power */ - 0x0000, /* R198 */ - 0x0003, /* R199 - Limit Switch Control */ - 0x001D, /* R200 - LDO1 Control */ - 0x1000, /* R201 - LDO1 Timeouts */ - 0x001C, /* R202 - LDO1 Low Power */ - 0x0017, /* R203 - LDO2 Control */ - 0x1000, /* R204 - LDO2 Timeouts */ - 0x001C, /* R205 - LDO2 Low Power */ - 0x0006, /* R206 - LDO3 Control */ - 0x1000, /* R207 - LDO3 Timeouts */ - 0x001C, /* R208 - LDO3 Low Power */ - 0x0010, /* R209 - LDO4 Control */ - 0x1000, /* R210 - LDO4 Timeouts */ - 0x001C, /* R211 - LDO4 Low Power */ - 0x0000, /* R212 */ - 0x0000, /* R213 */ - 0x0000, /* R214 */ - 0x0000, /* R215 - VCC_FAULT Masks */ - 0x001F, /* R216 - Main Bandgap Control */ - 0x0000, /* R217 - OSC Control */ - 0x9000, /* R218 - RTC Tick Control */ - 0x0000, /* R219 - Security1 */ - 0x4000, /* R220 */ - 0x0000, /* R221 */ - 0x0000, /* R222 */ - 0x0000, /* R223 */ - 0x0000, /* R224 - Signal overrides */ - 0x0000, /* R225 - DCDC/LDO status */ - 0x0000, /* R226 - Charger Overides/status */ - 0x0000, /* R227 - misc overrides */ - 0x0000, /* R228 - Supply overrides/status 1 */ - 0x0000, /* R229 - Supply overrides/status 2 */ - 0xE000, /* R230 - GPIO Pin Status */ - 0x0000, /* R231 - comparotor overrides */ - 0x0000, /* R232 */ - 0x0000, /* R233 - State Machine status */ - 0x1200, /* R234 */ - 0x0000, /* R235 */ - 0x8000, /* R236 */ - 0x0000, /* R237 */ - 0x0000, /* R238 */ - 0x0000, /* R239 */ - 0x0003, /* R240 */ - 0x0000, /* R241 */ - 0x0000, /* R242 */ - 0x0004, /* R243 */ - 0x0300, /* R244 */ - 0x0000, /* R245 */ - 0x0200, /* R246 */ - 0x0000, /* R247 */ - 0x1000, /* R248 - DCDC1 Test Controls */ - 0x5000, /* R249 */ - 0x1000, /* R250 - DCDC3 Test Controls */ - 0x1000, /* R251 - DCDC4 Test Controls */ - 0x5100, /* R252 */ - 0x1000, /* R253 - DCDC6 Test Controls */ -}; -#endif - /* * Access masks. */ -const struct wm8350_reg_access wm8350_reg_io_map[] = { +static const struct wm8350_reg_access { + u16 readable; /* Mask of readable bits */ + u16 writable; /* Mask of writable bits */ + u16 vol; /* Mask of volatile bits */ +} wm8350_reg_io_map[] = { /* read write volatile */ - { 0xFFFF, 0xFFFF, 0xFFFF }, /* R0 - Reset/ID */ - { 0x7CFF, 0x0C00, 0x7FFF }, /* R1 - ID */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R0 - Reset/ID */ + { 0x7CFF, 0x0C00, 0x0000 }, /* R1 - ID */ { 0x007F, 0x0000, 0x0000 }, /* R2 - ROM Mask ID */ { 0xBE3B, 0xBE3B, 0x8000 }, /* R3 - System Control 1 */ { 0xFEF7, 0xFEF7, 0xF800 }, /* R4 - System Control 2 */ diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h index cba9bc8f947b..509481d9cf19 100644 --- a/include/linux/mfd/wm8350/core.h +++ b/include/linux/mfd/wm8350/core.h @@ -587,24 +587,6 @@ #define WM8350_NUM_IRQ_REGS 7 extern const struct regmap_config wm8350_regmap; -struct wm8350_reg_access { - u16 readable; /* Mask of readable bits */ - u16 writable; /* Mask of writable bits */ - u16 vol; /* Mask of volatile bits */ -}; -extern const struct wm8350_reg_access wm8350_reg_io_map[]; -extern const u16 wm8350_mode0_defaults[]; -extern const u16 wm8350_mode1_defaults[]; -extern const u16 wm8350_mode2_defaults[]; -extern const u16 wm8350_mode3_defaults[]; -extern const u16 wm8351_mode0_defaults[]; -extern const u16 wm8351_mode1_defaults[]; -extern const u16 wm8351_mode2_defaults[]; -extern const u16 wm8351_mode3_defaults[]; -extern const u16 wm8352_mode0_defaults[]; -extern const u16 wm8352_mode1_defaults[]; -extern const u16 wm8352_mode2_defaults[]; -extern const u16 wm8352_mode3_defaults[]; struct wm8350; @@ -618,7 +600,6 @@ struct wm8350 { /* device IO */ struct regmap *regmap; - u16 *reg_cache; bool unlocked; struct mutex auxadc_mutex; -- cgit v1.2.3 From 5261e101198e7ef31a60d3aa97815a49c8b8fa20 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Mon, 21 May 2012 14:28:21 +0530 Subject: mfd: Update db8500-prmcu hostport_access enable Force the Modem wakeup by asserting the CaWakeReq signal before the hostaccess_req/ack ping-pong sequence. The Awake_req signal is de-asserted asserted at the same time than the hostaccess_req. Return error on failure case so that the client using this can take appropiate steps. Signed-off-by: Arun Murthy Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 45 +++++++++++++++++----------------------- drivers/mfd/dbx500-prcmu-regs.h | 1 + include/linux/mfd/db8500-prcmu.h | 7 +++++-- include/linux/mfd/dbx500-prcmu.h | 7 +++++-- 4 files changed, 30 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index bf5a054a2b91..f4adcabb2a51 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2269,10 +2269,10 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) /** * prcmu_ac_wake_req - should be called whenever ARM wants to wakeup Modem */ -void prcmu_ac_wake_req(void) +int prcmu_ac_wake_req(void) { u32 val; - u32 status; + int ret = 0; mutex_lock(&mb0_transfer.ac_wake_lock); @@ -2282,39 +2282,32 @@ void prcmu_ac_wake_req(void) atomic_set(&ac_wake_req_state, 1); -retry: - writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ), PRCM_HOSTACCESS_REQ); + /* + * Force Modem Wake-up before hostaccess_req ping-pong. + * It prevents Modem to enter in Sleep while acking the hostaccess + * request. The 31us delay has been calculated by HWI. + */ + val |= PRCM_HOSTACCESS_REQ_WAKE_REQ; + writel(val, PRCM_HOSTACCESS_REQ); + + udelay(31); + + val |= PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ; + writel(val, PRCM_HOSTACCESS_REQ); if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work, msecs_to_jiffies(5000))) { +#if defined(CONFIG_DBX500_PRCMU_DEBUG) + db8500_prcmu_debug_dump(__func__, true, true); +#endif pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n", __func__); - goto unlock_and_return; - } - - /* - * The modem can generate an AC_WAKE_ACK, and then still go to sleep. - * As a workaround, we wait, and then check that the modem is indeed - * awake (in terms of the value of the PRCM_MOD_AWAKE_STATUS - * register, which may not be the whole truth). - */ - udelay(400); - status = (readl(PRCM_MOD_AWAKE_STATUS) & BITS(0, 2)); - if (status != (PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE | - PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE)) { - pr_err("prcmu: %s received ack, but modem not awake (0x%X).\n", - __func__, status); - udelay(1200); - writel(val, PRCM_HOSTACCESS_REQ); - if (wait_for_completion_timeout(&mb0_transfer.ac_wake_work, - msecs_to_jiffies(5000))) - goto retry; - pr_crit("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n", - __func__); + ret = -EFAULT; } unlock_and_return: mutex_unlock(&mb0_transfer.ac_wake_lock); + return ret; } /** diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h index 3a0bf91d7780..23108a6e3167 100644 --- a/drivers/mfd/dbx500-prcmu-regs.h +++ b/drivers/mfd/dbx500-prcmu-regs.h @@ -106,6 +106,7 @@ #define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334) #define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1 +#define PRCM_HOSTACCESS_REQ_WAKE_REQ BIT(16) #define ARM_WAKEUP_MODEM 0x1 #define PRCM_ARM_IT1_CLR (_PRCMU_BASE + 0x48C) diff --git a/include/linux/mfd/db8500-prcmu.h b/include/linux/mfd/db8500-prcmu.h index b3a43b1263fe..b82f6ee66a0b 100644 --- a/include/linux/mfd/db8500-prcmu.h +++ b/include/linux/mfd/db8500-prcmu.h @@ -530,7 +530,7 @@ int db8500_prcmu_stop_temp_sense(void); int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size); int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size); -void prcmu_ac_wake_req(void); +int prcmu_ac_wake_req(void); void prcmu_ac_sleep_req(void); void db8500_prcmu_modem_reset(void); @@ -680,7 +680,10 @@ static inline int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) return -ENOSYS; } -static inline void prcmu_ac_wake_req(void) {} +static inline int prcmu_ac_wake_req(void) +{ + return 0; +} static inline void prcmu_ac_sleep_req(void) {} diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index 5a13f93d8f1c..5b90e94399e1 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h @@ -345,7 +345,7 @@ static inline u16 prcmu_get_reset_code(void) return db8500_prcmu_get_reset_code(); } -void prcmu_ac_wake_req(void); +int prcmu_ac_wake_req(void); void prcmu_ac_sleep_req(void); static inline void prcmu_modem_reset(void) { @@ -533,7 +533,10 @@ static inline u16 prcmu_get_reset_code(void) return 0; } -static inline void prcmu_ac_wake_req(void) {} +static inline int prcmu_ac_wake_req(void) +{ + return 0; +} static inline void prcmu_ac_sleep_req(void) {} -- cgit v1.2.3 From 2968ab133ec790134d4347aa4264c2eb064b42e7 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 2 Jul 2012 10:50:19 +0100 Subject: mfd: Attaching a node to new 'struct mfd_cell' of_compatible variable Applying a succinct description to the of_compatible variable recently added to the mfd_cell struct. Also link to the documentation page where more information can be found about compatible properties. Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- include/linux/mfd/core.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 99b7eb1961b6..3a8435a8058f 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -36,6 +36,10 @@ struct mfd_cell { /* platform data passed to the sub devices drivers */ void *platform_data; size_t pdata_size; + /* + * Device Tree compatible string + * See: Documentation/devicetree/usage-model.txt Chapter 2.2 for details + */ const char *of_compatible; /* -- cgit v1.2.3 From b41511f713ccaef666e450fae8cb18909897fe4e Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Mon, 2 Jul 2012 09:02:55 +0900 Subject: mfd: Add irq domain support for max8997 interrupts Add irq domain support for max8997 interrupts. The reverse mapping method used is linear mapping since the sub-drivers of max8997 such as regulator and charger drivers can use the max8997 irq_domain to get the linux irq number for max8997 interrupts. All uses of irq_base in platform data and max8997 driver private data are removed. Reviwed-by: Mark Brown Acked-by: MyungJoo Ham Acked-by: Grant Likely Signed-off-by: Thomas Abraham Signed-off-by: Chanwoo Choi Signed-off-by: Kyungmin Park Signed-off-by: Samuel Ortiz --- arch/arm/mach-exynos/mach-nuri.c | 4 --- arch/arm/mach-exynos/mach-origen.c | 1 - drivers/mfd/Kconfig | 1 + drivers/mfd/max8997-irq.c | 62 ++++++++++++++++++++++--------------- drivers/mfd/max8997.c | 1 - include/linux/mfd/max8997-private.h | 4 ++- include/linux/mfd/max8997.h | 1 - 7 files changed, 41 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c index 656f8fc9addd..acb58f5cee8d 100644 --- a/arch/arm/mach-exynos/mach-nuri.c +++ b/arch/arm/mach-exynos/mach-nuri.c @@ -1067,12 +1067,8 @@ static struct platform_device nuri_max8903_device = { static void __init nuri_power_init(void) { int gpio; - int irq_base = IRQ_GPIO_END + 1; int ta_en = 0; - nuri_max8997_pdata.irq_base = irq_base; - irq_base += MAX8997_IRQ_NR; - gpio = EXYNOS4_GPX0(7); gpio_request(gpio, "AP_PMIC_IRQ"); s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0xf)); diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c index f5572be9d7bf..3ce403d7cf1c 100644 --- a/arch/arm/mach-exynos/mach-origen.c +++ b/arch/arm/mach-exynos/mach-origen.c @@ -425,7 +425,6 @@ static struct max8997_platform_data __initdata origen_max8997_pdata = { .buck1_gpiodvs = false, .buck2_gpiodvs = false, .buck5_gpiodvs = false, - .irq_base = IRQ_GPIO_END + 1, .ignore_gpiodvs_side_effect = true, .buck125_default_idx = 0x0, diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b3dee92fd3a8..aaa048a437c0 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -464,6 +464,7 @@ config MFD_MAX8997 bool "Maxim Semiconductor MAX8997/8966 PMIC Support" depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE + select IRQ_DOMAIN help Say yes here to support for Maxim Semiconductor MAX8997/8966. This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic, diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c index 09274cf7c33b..43fa61413e93 100644 --- a/drivers/mfd/max8997-irq.c +++ b/drivers/mfd/max8997-irq.c @@ -142,7 +142,8 @@ static void max8997_irq_sync_unlock(struct irq_data *data) static const inline struct max8997_irq_data * irq_to_max8997_irq(struct max8997_dev *max8997, int irq) { - return &max8997_irqs[irq - max8997->irq_base]; + struct irq_data *data = irq_get_irq_data(irq); + return &max8997_irqs[data->hwirq]; } static void max8997_irq_mask(struct irq_data *data) @@ -182,7 +183,7 @@ static irqreturn_t max8997_irq_thread(int irq, void *data) u8 irq_reg[MAX8997_IRQ_GROUP_NR] = {}; u8 irq_src; int ret; - int i; + int i, cur_irq; ret = max8997_read_reg(max8997->i2c, MAX8997_REG_INTSRC, &irq_src); if (ret < 0) { @@ -269,8 +270,11 @@ static irqreturn_t max8997_irq_thread(int irq, void *data) /* Report */ for (i = 0; i < MAX8997_IRQ_NR; i++) { - if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) - handle_nested_irq(max8997->irq_base + i); + if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) { + cur_irq = irq_find_mapping(max8997->irq_domain, i); + if (cur_irq) + handle_nested_irq(cur_irq); + } } return IRQ_HANDLED; @@ -278,26 +282,40 @@ static irqreturn_t max8997_irq_thread(int irq, void *data) int max8997_irq_resume(struct max8997_dev *max8997) { - if (max8997->irq && max8997->irq_base) - max8997_irq_thread(max8997->irq_base, max8997); + if (max8997->irq && max8997->irq_domain) + max8997_irq_thread(0, max8997); + return 0; +} + +static int max8997_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct max8997_dev *max8997 = d->host_data; + + irq_set_chip_data(irq, max8997); + irq_set_chip_and_handler(irq, &max8997_irq_chip, handle_edge_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif return 0; } +static struct irq_domain_ops max8997_irq_domain_ops = { + .map = max8997_irq_domain_map, +}; + int max8997_irq_init(struct max8997_dev *max8997) { + struct irq_domain *domain; int i; - int cur_irq; int ret; u8 val; if (!max8997->irq) { dev_warn(max8997->dev, "No interrupt specified.\n"); - max8997->irq_base = 0; - return 0; - } - - if (!max8997->irq_base) { - dev_err(max8997->dev, "No interrupt base specified.\n"); return 0; } @@ -327,19 +345,13 @@ int max8997_irq_init(struct max8997_dev *max8997) true : false; } - /* Register with genirq */ - for (i = 0; i < MAX8997_IRQ_NR; i++) { - cur_irq = i + max8997->irq_base; - irq_set_chip_data(cur_irq, max8997); - irq_set_chip_and_handler(cur_irq, &max8997_irq_chip, - handle_edge_irq); - irq_set_nested_thread(cur_irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(cur_irq, IRQF_VALID); -#else - irq_set_noprobe(cur_irq); -#endif + domain = irq_domain_add_linear(NULL, MAX8997_IRQ_NR, + &max8997_irq_domain_ops, max8997); + if (!domain) { + dev_err(max8997->dev, "could not create irq domain\n"); + return -ENODEV; } + max8997->irq_domain = domain; ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index 454f4992cfc3..10b629c245b6 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c @@ -143,7 +143,6 @@ static int max8997_i2c_probe(struct i2c_client *i2c, if (!pdata) goto err; - max8997->irq_base = pdata->irq_base; max8997->ono = pdata->ono; mutex_init(&max8997->iolock); diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h index 3f4deb62d6b0..830152cfae33 100644 --- a/include/linux/mfd/max8997-private.h +++ b/include/linux/mfd/max8997-private.h @@ -23,6 +23,8 @@ #define __LINUX_MFD_MAX8997_PRIV_H #include +#include +#include #define MAX8997_REG_INVALID (0xff) @@ -325,7 +327,7 @@ struct max8997_dev { int irq; int ono; - int irq_base; + struct irq_domain *irq_domain; struct mutex irqlock; int irq_masks_cur[MAX8997_IRQ_GROUP_NR]; int irq_masks_cache[MAX8997_IRQ_GROUP_NR]; diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h index b40c08cd30bc..328d8e24b533 100644 --- a/include/linux/mfd/max8997.h +++ b/include/linux/mfd/max8997.h @@ -181,7 +181,6 @@ struct max8997_led_platform_data { struct max8997_platform_data { /* IRQ */ - int irq_base; int ono; int wakeup; -- cgit v1.2.3 From 712db99df155eeef7bbab8677d8a02d0eff50d11 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 28 Jun 2012 12:20:21 +0200 Subject: mfd: Add support for enabling tps65910 external 32-kHz oscillator Add flag to platform data to enable external 32-kHz crystal oscillator (or square wave) input. The tps6591x can use either an internal 32-kHz RC oscillator or an external crystal (or square wave) to generate the 32-kHz clock. The default setting depends on the selected boot mode. In boot mode 00 the internal RC oscillator is used at power-on, but the external crystal oscillator (or square wave) can be enabled by clearing the ck32k_ctrl flag in the device control register. Note that there is no way to switch from the external crystal oscillator to the internal RC oscillator. Signed-off-by: Johan Hovold Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65910.c | 21 ++++++++++++++++++++- include/linux/mfd/tps65910.h | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index be9e07b77325..b0526b7d6550 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -68,6 +68,25 @@ static const struct regmap_config tps65910_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +static int __devinit tps65910_misc_init(struct tps65910 *tps65910, + struct tps65910_board *pmic_pdata) +{ + struct device *dev = tps65910->dev; + int ret; + + if (pmic_pdata->en_ck32k_xtal) { + ret = tps65910_reg_clear_bits(tps65910, + TPS65910_DEVCTRL, + DEVCTRL_CK32K_CTRL_MASK); + if (ret < 0) { + dev_err(dev, "clear ck32k_ctrl failed: %d\n", ret); + return ret; + } + } + + return 0; +} + static int __devinit tps65910_sleepinit(struct tps65910 *tps65910, struct tps65910_board *pmic_pdata) { @@ -243,7 +262,7 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c, init_data->irq_base = pmic_plat_data->irq_base; tps65910_irq_init(tps65910, init_data->irq, init_data); - + tps65910_misc_init(tps65910, pmic_plat_data); tps65910_sleepinit(tps65910, pmic_plat_data); return ret; diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h index dd8dc0a6c462..a989d2b066be 100644 --- a/include/linux/mfd/tps65910.h +++ b/include/linux/mfd/tps65910.h @@ -807,6 +807,7 @@ struct tps65910_board { int irq_base; int vmbch_threshold; int vmbch2_threshold; + bool en_ck32k_xtal; bool en_dev_slp; struct tps65910_sleep_keepon_data *slp_keepon; bool en_gpio_sleep[TPS6591X_MAX_NUM_GPIO]; -- cgit v1.2.3 From 2573f6d36e73e080fc1d9d9ac7dfaf2253a61434 Mon Sep 17 00:00:00 2001 From: "Jett.Zhou" Date: Fri, 6 Jul 2012 10:59:58 +0800 Subject: mfd: Add pre-regulator device for 88pm860x Pre-regulator of 88pm8606 is mainly for support charging based on vbus, it needs to be enabled for charging battery, and will be disabled in some exception condition like over-temp. Add the pre-regulator device init data and resource for mfd subdev. Signed-off-by: Jett.Zhou Signed-off-by: Samuel Ortiz --- drivers/mfd/88pm860x-core.c | 23 +++++++++++++++++++++++ include/linux/mfd/88pm860x.h | 1 + 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 87bd5ba38d5b..d09918cf1b15 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -90,6 +90,10 @@ static struct resource charger_resources[] __devinitdata = { {PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage", IORESOURCE_IRQ,}, }; +static struct resource preg_resources[] __devinitdata = { + {PM8606_ID_PREG, PM8606_ID_PREG, "preg", IORESOURCE_IO,}, +}; + static struct resource rtc_resources[] __devinitdata = { {PM8607_IRQ_RTC, PM8607_IRQ_RTC, "rtc", IORESOURCE_IRQ,}, }; @@ -142,9 +146,19 @@ static struct mfd_cell codec_devs[] = { {"88pm860x-codec", -1,}, }; +static struct regulator_consumer_supply preg_supply[] = { + REGULATOR_SUPPLY("preg", "charger-manager"), +}; + +static struct regulator_init_data preg_init_data = { + .num_consumer_supplies = ARRAY_SIZE(preg_supply), + .consumer_supplies = &preg_supply[0], +}; + static struct mfd_cell power_devs[] = { {"88pm860x-battery", -1,}, {"88pm860x-charger", -1,}, + {"88pm860x-preg", -1,}, }; static struct mfd_cell rtc_devs[] = { @@ -768,6 +782,15 @@ static void __devinit device_power_init(struct pm860x_chip *chip, &charger_resources[0], chip->irq_base); if (ret < 0) dev_err(chip->dev, "Failed to add charger subdev\n"); + + power_devs[2].platform_data = &preg_init_data; + power_devs[2].pdata_size = sizeof(struct regulator_init_data); + power_devs[2].num_resources = ARRAY_SIZE(preg_resources); + power_devs[2].resources = &preg_resources[0], + ret = mfd_add_devices(chip->dev, 0, &power_devs[2], 1, + &preg_resources[0], chip->irq_base); + if (ret < 0) + dev_err(chip->dev, "Failed to add preg subdev\n"); } static void __devinit device_onkey_init(struct pm860x_chip *chip, diff --git a/include/linux/mfd/88pm860x.h b/include/linux/mfd/88pm860x.h index 84d071ade1d8..7b24943779fa 100644 --- a/include/linux/mfd/88pm860x.h +++ b/include/linux/mfd/88pm860x.h @@ -136,6 +136,7 @@ enum { PM8607_ID_LDO13, PM8607_ID_LDO14, PM8607_ID_LDO15, + PM8606_ID_PREG, PM8607_ID_RG_MAX, }; -- cgit v1.2.3 From b8748096111b483de8a544cc220510dff17bbff9 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 6 Jul 2012 15:32:20 +0800 Subject: mfd: Remove unused max77686 iolock mutex Now this driver is using regmap API, the iolock mutex is not used and can be removed. Signed-off-by: Axel Lin Signed-off-by: Samuel Ortiz --- drivers/mfd/max77686.c | 3 --- include/linux/mfd/max77686-private.h | 1 - 2 files changed, 4 deletions(-) (limited to 'include') diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index 11e56017e0b0..9e7e1d30f25f 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -79,8 +78,6 @@ static int max77686_i2c_probe(struct i2c_client *i2c, max77686->wakeup = pdata->wakeup; max77686->irq_gpio = pdata->irq_gpio; - mutex_init(&max77686->iolock); - if (regmap_read(max77686->regmap, MAX77686_REG_DEVICE_ID, &data) < 0) { dev_err(max77686->dev, diff --git a/include/linux/mfd/max77686-private.h b/include/linux/mfd/max77686-private.h index 962f65e72b71..d327d4971e4f 100644 --- a/include/linux/mfd/max77686-private.h +++ b/include/linux/mfd/max77686-private.h @@ -219,7 +219,6 @@ struct max77686_dev { struct device *dev; struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */ struct i2c_client *rtc; /* slave addr 0x0c */ - struct mutex iolock; int type; -- cgit v1.2.3 From 59db96913c9d94fe74002df494eb80e4a5ca4087 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 9 Jul 2012 00:31:36 +0200 Subject: mfd: Move arizona digital core supply management to the regulator API Rather than open coding the enable GPIO control in the MFD core use the API to push the management on to the regulator driver. The immediate advantage is slight for most systems but this will in future allow device configurations where an external regulator is used for DCVDD. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-core.c | 65 +++++++++++++++++++++------------------- include/linux/mfd/arizona/core.h | 1 + 2 files changed, 36 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 42cb28b2b5c8..c8946a889a78 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -28,7 +29,6 @@ static const char *wm5102_core_supplies[] = { "AVDD", "DBVDD1", - "DCVDD", }; int arizona_clk32k_enable(struct arizona *arizona) @@ -223,8 +223,11 @@ static int arizona_runtime_resume(struct device *dev) struct arizona *arizona = dev_get_drvdata(dev); int ret; - if (arizona->pdata.ldoena) - gpio_set_value_cansleep(arizona->pdata.ldoena, 1); + ret = regulator_enable(arizona->dcvdd); + if (ret != 0) { + dev_err(arizona->dev, "Failed to enable DCVDD: %d\n", ret); + return ret; + } regcache_cache_only(arizona->regmap, false); @@ -241,11 +244,9 @@ static int arizona_runtime_suspend(struct device *dev) { struct arizona *arizona = dev_get_drvdata(dev); - if (arizona->pdata.ldoena) { - gpio_set_value_cansleep(arizona->pdata.ldoena, 0); - regcache_cache_only(arizona->regmap, true); - regcache_mark_dirty(arizona->regmap); - } + regulator_disable(arizona->dcvdd); + regcache_cache_only(arizona->regmap, true); + regcache_mark_dirty(arizona->regmap); return 0; } @@ -314,6 +315,13 @@ int __devinit arizona_dev_init(struct arizona *arizona) goto err_early; } + arizona->dcvdd = devm_regulator_get(arizona->dev, "DCVDD"); + if (IS_ERR(arizona->dcvdd)) { + ret = PTR_ERR(arizona->dcvdd); + dev_err(dev, "Failed to request DCVDD: %d\n", ret); + goto err_early; + } + ret = regulator_bulk_enable(arizona->num_core_supplies, arizona->core_supplies); if (ret != 0) { @@ -322,6 +330,12 @@ int __devinit arizona_dev_init(struct arizona *arizona) goto err_early; } + ret = regulator_enable(arizona->dcvdd); + if (ret != 0) { + dev_err(dev, "Failed to enable DCVDD: %d\n", ret); + goto err_enable; + } + if (arizona->pdata.reset) { /* Start out with /RESET low to put the chip into reset */ ret = gpio_request_one(arizona->pdata.reset, @@ -329,35 +343,25 @@ int __devinit arizona_dev_init(struct arizona *arizona) "arizona /RESET"); if (ret != 0) { dev_err(dev, "Failed to request /RESET: %d\n", ret); - goto err_enable; + goto err_dcvdd; } gpio_set_value_cansleep(arizona->pdata.reset, 1); } - if (arizona->pdata.ldoena) { - ret = gpio_request_one(arizona->pdata.ldoena, - GPIOF_DIR_OUT | GPIOF_INIT_HIGH, - "arizona LDOENA"); - if (ret != 0) { - dev_err(dev, "Failed to request LDOENA: %d\n", ret); - goto err_reset; - } - } - regcache_cache_only(arizona->regmap, false); ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, ®); if (ret != 0) { dev_err(dev, "Failed to read ID register: %d\n", ret); - goto err_ldoena; + goto err_reset; } ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION, &arizona->rev); if (ret != 0) { dev_err(dev, "Failed to read revision register: %d\n", ret); - goto err_ldoena; + goto err_reset; } arizona->rev &= ARIZONA_DEVICE_REVISION_MASK; @@ -374,7 +378,7 @@ int __devinit arizona_dev_init(struct arizona *arizona) default: dev_err(arizona->dev, "Unknown device ID %x\n", reg); - goto err_ldoena; + goto err_reset; } dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A'); @@ -387,7 +391,7 @@ int __devinit arizona_dev_init(struct arizona *arizona) ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0); if (ret != 0) { dev_err(dev, "Failed to reset device: %d\n", ret); - goto err_ldoena; + goto err_reset; } } @@ -424,7 +428,7 @@ int __devinit arizona_dev_init(struct arizona *arizona) dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n", arizona->pdata.clk32k_src); ret = -EINVAL; - goto err_ldoena; + goto err_reset; } for (i = 0; i < ARIZONA_MAX_INPUT; i++) { @@ -470,7 +474,7 @@ int __devinit arizona_dev_init(struct arizona *arizona) /* Set up for interrupts */ ret = arizona_irq_init(arizona); if (ret != 0) - goto err_ldoena; + goto err_reset; arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error", arizona_clkgen_err, arizona); @@ -491,20 +495,21 @@ int __devinit arizona_dev_init(struct arizona *arizona) goto err_irq; } +#ifdef CONFIG_PM_RUNTIME + regulator_disable(arizona->dcvdd); +#endif + return 0; err_irq: arizona_irq_exit(arizona); -err_ldoena: - if (arizona->pdata.ldoena) { - gpio_set_value_cansleep(arizona->pdata.ldoena, 0); - gpio_free(arizona->pdata.ldoena); - } err_reset: if (arizona->pdata.reset) { gpio_set_value_cansleep(arizona->pdata.reset, 1); gpio_free(arizona->pdata.reset); } +err_dcvdd: + regulator_disable(arizona->dcvdd); err_enable: regulator_bulk_disable(ARRAY_SIZE(arizona->core_supplies), arizona->core_supplies); diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 0157d845c2ff..3ef32b4c1136 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -77,6 +77,7 @@ struct arizona { int num_core_supplies; struct regulator_bulk_data core_supplies[ARIZONA_MAX_CORE_SUPPLIES]; + struct regulator *dcvdd; struct arizona_pdata pdata; -- cgit v1.2.3 From de2233365d5abc94993378330768786de2c606f6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 5 Jul 2012 20:35:28 +0100 Subject: mfd: Add more arizona register definitions These registers will be used in future devices. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- include/linux/mfd/arizona/registers.h | 163 ++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 989c47d681e0..8f49106d7bda 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -20,6 +20,9 @@ #define ARIZONA_DEVICE_REVISION 0x01 #define ARIZONA_CTRL_IF_SPI_CFG_1 0x08 #define ARIZONA_CTRL_IF_I2C1_CFG_1 0x09 +#define ARIZONA_CTRL_IF_I2C2_CFG_1 0x0A +#define ARIZONA_CTRL_IF_I2C1_CFG_2 0x0B +#define ARIZONA_CTRL_IF_I2C2_CFG_2 0x0C #define ARIZONA_CTRL_IF_STATUS_1 0x0D #define ARIZONA_WRITE_SEQUENCER_CTRL_0 0x16 #define ARIZONA_WRITE_SEQUENCER_CTRL_1 0x17 @@ -80,6 +83,7 @@ #define ARIZONA_FLL1_CONTROL_5 0x175 #define ARIZONA_FLL1_CONTROL_6 0x176 #define ARIZONA_FLL1_LOOP_FILTER_TEST_1 0x177 +#define ARIZONA_FLL1_NCO_TEST_0 0x178 #define ARIZONA_FLL1_SYNCHRONISER_1 0x181 #define ARIZONA_FLL1_SYNCHRONISER_2 0x182 #define ARIZONA_FLL1_SYNCHRONISER_3 0x183 @@ -95,6 +99,7 @@ #define ARIZONA_FLL2_CONTROL_5 0x195 #define ARIZONA_FLL2_CONTROL_6 0x196 #define ARIZONA_FLL2_LOOP_FILTER_TEST_1 0x197 +#define ARIZONA_FLL2_NCO_TEST_0 0x198 #define ARIZONA_FLL2_SYNCHRONISER_1 0x1A1 #define ARIZONA_FLL2_SYNCHRONISER_2 0x1A2 #define ARIZONA_FLL2_SYNCHRONISER_3 0x1A3 @@ -119,6 +124,7 @@ #define ARIZONA_ISOLATION_CONTROL 0x2CB #define ARIZONA_JACK_DETECT_ANALOGUE 0x2D3 #define ARIZONA_INPUT_ENABLES 0x300 +#define ARIZONA_INPUT_ENABLES_STATUS 0x301 #define ARIZONA_INPUT_RATE 0x308 #define ARIZONA_INPUT_VOLUME_RAMP 0x309 #define ARIZONA_IN1L_CONTROL 0x310 @@ -139,8 +145,14 @@ #define ARIZONA_IN3R_CONTROL 0x324 #define ARIZONA_ADC_DIGITAL_VOLUME_3R 0x325 #define ARIZONA_DMIC3R_CONTROL 0x326 +#define ARIZONA_IN4_CONTROL 0x328 +#define ARIZONA_ADC_DIGITAL_VOLUME_4L 0x329 +#define ARIZONA_DMIC4L_CONTROL 0x32A +#define ARIZONA_ADC_DIGITAL_VOLUME_4R 0x32D +#define ARIZONA_DMIC4R_CONTROL 0x32E #define ARIZONA_OUTPUT_ENABLES_1 0x400 #define ARIZONA_OUTPUT_STATUS_1 0x401 +#define ARIZONA_RAW_OUTPUT_STATUS_1 0x406 #define ARIZONA_OUTPUT_RATE_1 0x408 #define ARIZONA_OUTPUT_VOLUME_RAMP 0x409 #define ARIZONA_OUTPUT_PATH_CONFIG_1L 0x410 @@ -166,6 +178,7 @@ #define ARIZONA_OUTPUT_PATH_CONFIG_3R 0x424 #define ARIZONA_DAC_DIGITAL_VOLUME_3R 0x425 #define ARIZONA_DAC_VOLUME_LIMIT_3R 0x426 +#define ARIZONA_NOISE_GATE_SELECT_3R 0x427 #define ARIZONA_OUTPUT_PATH_CONFIG_4L 0x428 #define ARIZONA_DAC_DIGITAL_VOLUME_4L 0x429 #define ARIZONA_OUT_VOLUME_4L 0x42A @@ -182,10 +195,20 @@ #define ARIZONA_DAC_DIGITAL_VOLUME_5R 0x435 #define ARIZONA_DAC_VOLUME_LIMIT_5R 0x436 #define ARIZONA_NOISE_GATE_SELECT_5R 0x437 +#define ARIZONA_OUTPUT_PATH_CONFIG_6L 0x438 +#define ARIZONA_DAC_DIGITAL_VOLUME_6L 0x439 +#define ARIZONA_DAC_VOLUME_LIMIT_6L 0x43A +#define ARIZONA_NOISE_GATE_SELECT_6L 0x43B +#define ARIZONA_OUTPUT_PATH_CONFIG_6R 0x43C +#define ARIZONA_DAC_DIGITAL_VOLUME_6R 0x43D +#define ARIZONA_DAC_VOLUME_LIMIT_6R 0x43E +#define ARIZONA_NOISE_GATE_SELECT_6R 0x43F #define ARIZONA_DAC_AEC_CONTROL_1 0x450 #define ARIZONA_NOISE_GATE_CONTROL 0x458 #define ARIZONA_PDM_SPK1_CTRL_1 0x490 #define ARIZONA_PDM_SPK1_CTRL_2 0x491 +#define ARIZONA_PDM_SPK2_CTRL_1 0x492 +#define ARIZONA_PDM_SPK2_CTRL_2 0x493 #define ARIZONA_DAC_COMP_1 0x4DC #define ARIZONA_DAC_COMP_2 0x4DD #define ARIZONA_DAC_COMP_3 0x4DE @@ -335,6 +358,14 @@ #define ARIZONA_OUT3LMIX_INPUT_3_VOLUME 0x6A5 #define ARIZONA_OUT3LMIX_INPUT_4_SOURCE 0x6A6 #define ARIZONA_OUT3LMIX_INPUT_4_VOLUME 0x6A7 +#define ARIZONA_OUT3RMIX_INPUT_1_SOURCE 0x6A8 +#define ARIZONA_OUT3RMIX_INPUT_1_VOLUME 0x6A9 +#define ARIZONA_OUT3RMIX_INPUT_2_SOURCE 0x6AA +#define ARIZONA_OUT3RMIX_INPUT_2_VOLUME 0x6AB +#define ARIZONA_OUT3RMIX_INPUT_3_SOURCE 0x6AC +#define ARIZONA_OUT3RMIX_INPUT_3_VOLUME 0x6AD +#define ARIZONA_OUT3RMIX_INPUT_4_SOURCE 0x6AE +#define ARIZONA_OUT3RMIX_INPUT_4_VOLUME 0x6AF #define ARIZONA_OUT4LMIX_INPUT_1_SOURCE 0x6B0 #define ARIZONA_OUT4LMIX_INPUT_1_VOLUME 0x6B1 #define ARIZONA_OUT4LMIX_INPUT_2_SOURCE 0x6B2 @@ -367,6 +398,22 @@ #define ARIZONA_OUT5RMIX_INPUT_3_VOLUME 0x6CD #define ARIZONA_OUT5RMIX_INPUT_4_SOURCE 0x6CE #define ARIZONA_OUT5RMIX_INPUT_4_VOLUME 0x6CF +#define ARIZONA_OUT6LMIX_INPUT_1_SOURCE 0x6D0 +#define ARIZONA_OUT6LMIX_INPUT_1_VOLUME 0x6D1 +#define ARIZONA_OUT6LMIX_INPUT_2_SOURCE 0x6D2 +#define ARIZONA_OUT6LMIX_INPUT_2_VOLUME 0x6D3 +#define ARIZONA_OUT6LMIX_INPUT_3_SOURCE 0x6D4 +#define ARIZONA_OUT6LMIX_INPUT_3_VOLUME 0x6D5 +#define ARIZONA_OUT6LMIX_INPUT_4_SOURCE 0x6D6 +#define ARIZONA_OUT6LMIX_INPUT_4_VOLUME 0x6D7 +#define ARIZONA_OUT6RMIX_INPUT_1_SOURCE 0x6D8 +#define ARIZONA_OUT6RMIX_INPUT_1_VOLUME 0x6D9 +#define ARIZONA_OUT6RMIX_INPUT_2_SOURCE 0x6DA +#define ARIZONA_OUT6RMIX_INPUT_2_VOLUME 0x6DB +#define ARIZONA_OUT6RMIX_INPUT_3_SOURCE 0x6DC +#define ARIZONA_OUT6RMIX_INPUT_3_VOLUME 0x6DD +#define ARIZONA_OUT6RMIX_INPUT_4_SOURCE 0x6DE +#define ARIZONA_OUT6RMIX_INPUT_4_VOLUME 0x6DF #define ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE 0x700 #define ARIZONA_AIF1TX1MIX_INPUT_1_VOLUME 0x701 #define ARIZONA_AIF1TX1MIX_INPUT_2_SOURCE 0x702 @@ -645,18 +692,106 @@ #define ARIZONA_DSP1AUX4MIX_INPUT_1_SOURCE 0x968 #define ARIZONA_DSP1AUX5MIX_INPUT_1_SOURCE 0x970 #define ARIZONA_DSP1AUX6MIX_INPUT_1_SOURCE 0x978 +#define ARIZONA_DSP2LMIX_INPUT_1_SOURCE 0x980 +#define ARIZONA_DSP2LMIX_INPUT_1_VOLUME 0x981 +#define ARIZONA_DSP2LMIX_INPUT_2_SOURCE 0x982 +#define ARIZONA_DSP2LMIX_INPUT_2_VOLUME 0x983 +#define ARIZONA_DSP2LMIX_INPUT_3_SOURCE 0x984 +#define ARIZONA_DSP2LMIX_INPUT_3_VOLUME 0x985 +#define ARIZONA_DSP2LMIX_INPUT_4_SOURCE 0x986 +#define ARIZONA_DSP2LMIX_INPUT_4_VOLUME 0x987 +#define ARIZONA_DSP2RMIX_INPUT_1_SOURCE 0x988 +#define ARIZONA_DSP2RMIX_INPUT_1_VOLUME 0x989 +#define ARIZONA_DSP2RMIX_INPUT_2_SOURCE 0x98A +#define ARIZONA_DSP2RMIX_INPUT_2_VOLUME 0x98B +#define ARIZONA_DSP2RMIX_INPUT_3_SOURCE 0x98C +#define ARIZONA_DSP2RMIX_INPUT_3_VOLUME 0x98D +#define ARIZONA_DSP2RMIX_INPUT_4_SOURCE 0x98E +#define ARIZONA_DSP2RMIX_INPUT_4_VOLUME 0x98F +#define ARIZONA_DSP2AUX1MIX_INPUT_1_SOURCE 0x990 +#define ARIZONA_DSP2AUX2MIX_INPUT_1_SOURCE 0x998 +#define ARIZONA_DSP2AUX3MIX_INPUT_1_SOURCE 0x9A0 +#define ARIZONA_DSP2AUX4MIX_INPUT_1_SOURCE 0x9A8 +#define ARIZONA_DSP2AUX5MIX_INPUT_1_SOURCE 0x9B0 +#define ARIZONA_DSP2AUX6MIX_INPUT_1_SOURCE 0x9B8 +#define ARIZONA_DSP3LMIX_INPUT_1_SOURCE 0x9C0 +#define ARIZONA_DSP3LMIX_INPUT_1_VOLUME 0x9C1 +#define ARIZONA_DSP3LMIX_INPUT_2_SOURCE 0x9C2 +#define ARIZONA_DSP3LMIX_INPUT_2_VOLUME 0x9C3 +#define ARIZONA_DSP3LMIX_INPUT_3_SOURCE 0x9C4 +#define ARIZONA_DSP3LMIX_INPUT_3_VOLUME 0x9C5 +#define ARIZONA_DSP3LMIX_INPUT_4_SOURCE 0x9C6 +#define ARIZONA_DSP3LMIX_INPUT_4_VOLUME 0x9C7 +#define ARIZONA_DSP3RMIX_INPUT_1_SOURCE 0x9C8 +#define ARIZONA_DSP3RMIX_INPUT_1_VOLUME 0x9C9 +#define ARIZONA_DSP3RMIX_INPUT_2_SOURCE 0x9CA +#define ARIZONA_DSP3RMIX_INPUT_2_VOLUME 0x9CB +#define ARIZONA_DSP3RMIX_INPUT_3_SOURCE 0x9CC +#define ARIZONA_DSP3RMIX_INPUT_3_VOLUME 0x9CD +#define ARIZONA_DSP3RMIX_INPUT_4_SOURCE 0x9CE +#define ARIZONA_DSP3RMIX_INPUT_4_VOLUME 0x9CF +#define ARIZONA_DSP3AUX1MIX_INPUT_1_SOURCE 0x9D0 +#define ARIZONA_DSP3AUX2MIX_INPUT_1_SOURCE 0x9D8 +#define ARIZONA_DSP3AUX3MIX_INPUT_1_SOURCE 0x9E0 +#define ARIZONA_DSP3AUX4MIX_INPUT_1_SOURCE 0x9E8 +#define ARIZONA_DSP3AUX5MIX_INPUT_1_SOURCE 0x9F0 +#define ARIZONA_DSP3AUX6MIX_INPUT_1_SOURCE 0x9F8 +#define ARIZONA_DSP4LMIX_INPUT_1_SOURCE 0xA00 +#define ARIZONA_DSP4LMIX_INPUT_1_VOLUME 0xA01 +#define ARIZONA_DSP4LMIX_INPUT_2_SOURCE 0xA02 +#define ARIZONA_DSP4LMIX_INPUT_2_VOLUME 0xA03 +#define ARIZONA_DSP4LMIX_INPUT_3_SOURCE 0xA04 +#define ARIZONA_DSP4LMIX_INPUT_3_VOLUME 0xA05 +#define ARIZONA_DSP4LMIX_INPUT_4_SOURCE 0xA06 +#define ARIZONA_DSP4LMIX_INPUT_4_VOLUME 0xA07 +#define ARIZONA_DSP4RMIX_INPUT_1_SOURCE 0xA08 +#define ARIZONA_DSP4RMIX_INPUT_1_VOLUME 0xA09 +#define ARIZONA_DSP4RMIX_INPUT_2_SOURCE 0xA0A +#define ARIZONA_DSP4RMIX_INPUT_2_VOLUME 0xA0B +#define ARIZONA_DSP4RMIX_INPUT_3_SOURCE 0xA0C +#define ARIZONA_DSP4RMIX_INPUT_3_VOLUME 0xA0D +#define ARIZONA_DSP4RMIX_INPUT_4_SOURCE 0xA0E +#define ARIZONA_DSP4RMIX_INPUT_4_VOLUME 0xA0F +#define ARIZONA_DSP4AUX1MIX_INPUT_1_SOURCE 0xA10 +#define ARIZONA_DSP4AUX2MIX_INPUT_1_SOURCE 0xA18 +#define ARIZONA_DSP4AUX3MIX_INPUT_1_SOURCE 0xA20 +#define ARIZONA_DSP4AUX4MIX_INPUT_1_SOURCE 0xA28 +#define ARIZONA_DSP4AUX5MIX_INPUT_1_SOURCE 0xA30 +#define ARIZONA_DSP4AUX6MIX_INPUT_1_SOURCE 0xA38 #define ARIZONA_ASRC1LMIX_INPUT_1_SOURCE 0xA80 #define ARIZONA_ASRC1RMIX_INPUT_1_SOURCE 0xA88 #define ARIZONA_ASRC2LMIX_INPUT_1_SOURCE 0xA90 #define ARIZONA_ASRC2RMIX_INPUT_1_SOURCE 0xA98 #define ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE 0xB00 #define ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE 0xB08 +#define ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE 0xB10 +#define ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE 0xB18 #define ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE 0xB20 #define ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE 0xB28 +#define ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE 0xB30 +#define ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE 0xB38 #define ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE 0xB40 #define ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE 0xB48 #define ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE 0xB60 #define ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE 0xB68 +#define ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE 0xB30 +#define ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE 0xB38 +#define ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE 0xB40 +#define ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE 0xB48 +#define ARIZONA_ISRC2DEC3MIX_INPUT_1_SOURCE 0xB50 +#define ARIZONA_ISRC2DEC4MIX_INPUT_1_SOURCE 0xB58 +#define ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE 0xB60 +#define ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE 0xB68 +#define ARIZONA_ISRC2INT3MIX_INPUT_1_SOURCE 0xB70 +#define ARIZONA_ISRC2INT4MIX_INPUT_1_SOURCE 0xB78 +#define ARIZONA_ISRC3DEC1MIX_INPUT_1_SOURCE 0xB80 +#define ARIZONA_ISRC3DEC2MIX_INPUT_1_SOURCE 0xB88 +#define ARIZONA_ISRC3DEC3MIX_INPUT_1_SOURCE 0xB90 +#define ARIZONA_ISRC3DEC4MIX_INPUT_1_SOURCE 0xB98 +#define ARIZONA_ISRC3INT1MIX_INPUT_1_SOURCE 0xBA0 +#define ARIZONA_ISRC3INT2MIX_INPUT_1_SOURCE 0xBA8 +#define ARIZONA_ISRC3INT3MIX_INPUT_1_SOURCE 0xBB0 +#define ARIZONA_ISRC3INT4MIX_INPUT_1_SOURCE 0xBB8 #define ARIZONA_GPIO1_CTRL 0xC00 #define ARIZONA_GPIO2_CTRL 0xC01 #define ARIZONA_GPIO3_CTRL 0xC02 @@ -670,6 +805,18 @@ #define ARIZONA_MISC_PAD_CTRL_4 0xC23 #define ARIZONA_MISC_PAD_CTRL_5 0xC24 #define ARIZONA_MISC_PAD_CTRL_6 0xC25 +#define ARIZONA_MISC_PAD_CTRL_7 0xC30 +#define ARIZONA_MISC_PAD_CTRL_8 0xC31 +#define ARIZONA_MISC_PAD_CTRL_9 0xC32 +#define ARIZONA_MISC_PAD_CTRL_10 0xC33 +#define ARIZONA_MISC_PAD_CTRL_11 0xC34 +#define ARIZONA_MISC_PAD_CTRL_12 0xC35 +#define ARIZONA_MISC_PAD_CTRL_13 0xC36 +#define ARIZONA_MISC_PAD_CTRL_14 0xC37 +#define ARIZONA_MISC_PAD_CTRL_15 0xC38 +#define ARIZONA_MISC_PAD_CTRL_16 0xC39 +#define ARIZONA_MISC_PAD_CTRL_17 0xC3A +#define ARIZONA_MISC_PAD_CTRL_18 0xC3B #define ARIZONA_INTERRUPT_STATUS_1 0xD00 #define ARIZONA_INTERRUPT_STATUS_2 0xD01 #define ARIZONA_INTERRUPT_STATUS_3 0xD02 @@ -813,6 +960,7 @@ #define ARIZONA_HPLPF4_1 0xECC #define ARIZONA_HPLPF4_2 0xECD #define ARIZONA_ASRC_ENABLE 0xEE0 +#define ARIZONA_ASRC_STATUS 0xEE1 #define ARIZONA_ASRC_RATE1 0xEE2 #define ARIZONA_ASRC_RATE2 0xEE3 #define ARIZONA_ISRC_1_CTRL_1 0xEF0 @@ -824,10 +972,25 @@ #define ARIZONA_ISRC_3_CTRL_1 0xEF6 #define ARIZONA_ISRC_3_CTRL_2 0xEF7 #define ARIZONA_ISRC_3_CTRL_3 0xEF8 +#define ARIZONA_CLOCK_CONTROL 0xF00 +#define ARIZONA_ANC_SRC 0xF01 +#define ARIZONA_DSP_STATUS 0xF02 #define ARIZONA_DSP1_CONTROL_1 0x1100 #define ARIZONA_DSP1_CLOCKING_1 0x1101 #define ARIZONA_DSP1_STATUS_1 0x1104 #define ARIZONA_DSP1_STATUS_2 0x1105 +#define ARIZONA_DSP2_CONTROL_1 0x1200 +#define ARIZONA_DSP2_CLOCKING_1 0x1201 +#define ARIZONA_DSP2_STATUS_1 0x1204 +#define ARIZONA_DSP2_STATUS_2 0x1205 +#define ARIZONA_DSP3_CONTROL_1 0x1300 +#define ARIZONA_DSP3_CLOCKING_1 0x1301 +#define ARIZONA_DSP3_STATUS_1 0x1304 +#define ARIZONA_DSP3_STATUS_2 0x1305 +#define ARIZONA_DSP4_CONTROL_1 0x1400 +#define ARIZONA_DSP4_CLOCKING_1 0x1401 +#define ARIZONA_DSP4_STATUS_1 0x1404 +#define ARIZONA_DSP4_STATUS_2 0x1405 /* * Field Definitions. -- cgit v1.2.3 From 68602120e496a31d8e3b36d0bfc7d9d2456fb05c Mon Sep 17 00:00:00 2001 From: Sean Hefty Date: Thu, 14 Jun 2012 20:31:39 +0000 Subject: RDMA/cma: Allow user to restrict listens to bound address family Provide an option for the user to specify that listens should only accept connections where the incoming address family matches that of the locally bound address. This is used to support the equivalent of IPV6_V6ONLY socket option, which allows an app to only accept connection requests directed to IPv6 addresses. Signed-off-by: Sean Hefty Signed-off-by: Roland Dreier --- drivers/infiniband/core/cma.c | 35 +++++++++++++++++++++++++++++++---- drivers/infiniband/core/ucma.c | 7 +++++++ include/rdma/rdma_cm.h | 10 ++++++++++ include/rdma/rdma_user_cm.h | 1 + 4 files changed, 49 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 454e7ea111e6..8734a6af35d7 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -99,6 +99,10 @@ struct rdma_bind_list { unsigned short port; }; +enum { + CMA_OPTION_AFONLY, +}; + /* * Device removal can occur at anytime, so we need extra handling to * serialize notifying the user of device removal with other callbacks. @@ -137,6 +141,7 @@ struct rdma_id_private { u32 qkey; u32 qp_num; pid_t owner; + u32 options; u8 srq; u8 tos; u8 reuseaddr; @@ -2104,6 +2109,26 @@ int rdma_set_reuseaddr(struct rdma_cm_id *id, int reuse) } EXPORT_SYMBOL(rdma_set_reuseaddr); +int rdma_set_afonly(struct rdma_cm_id *id, int afonly) +{ + struct rdma_id_private *id_priv; + unsigned long flags; + int ret; + + id_priv = container_of(id, struct rdma_id_private, id); + spin_lock_irqsave(&id_priv->lock, flags); + if (id_priv->state == RDMA_CM_IDLE || id_priv->state == RDMA_CM_ADDR_BOUND) { + id_priv->options |= (1 << CMA_OPTION_AFONLY); + id_priv->afonly = afonly; + ret = 0; + } else { + ret = -EINVAL; + } + spin_unlock_irqrestore(&id_priv->lock, flags); + return ret; +} +EXPORT_SYMBOL(rdma_set_afonly); + static void cma_bind_port(struct rdma_bind_list *bind_list, struct rdma_id_private *id_priv) { @@ -2379,12 +2404,14 @@ int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr) } memcpy(&id->route.addr.src_addr, addr, ip_addr_size(addr)); - if (addr->sa_family == AF_INET) - id_priv->afonly = 1; + if (!(id_priv->options & (1 << CMA_OPTION_AFONLY))) { + if (addr->sa_family == AF_INET) + id_priv->afonly = 1; #if IS_ENABLED(CONFIG_IPV6) - else if (addr->sa_family == AF_INET6) - id_priv->afonly = init_net.ipv6.sysctl.bindv6only; + else if (addr->sa_family == AF_INET6) + id_priv->afonly = init_net.ipv6.sysctl.bindv6only; #endif + } ret = cma_get_port(id_priv); if (ret) goto err2; diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 8002ae642cfe..893cb879462c 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -909,6 +909,13 @@ static int ucma_set_option_id(struct ucma_context *ctx, int optname, } ret = rdma_set_reuseaddr(ctx->cm_id, *((int *) optval) ? 1 : 0); break; + case RDMA_OPTION_ID_AFONLY: + if (optlen != sizeof(int)) { + ret = -EINVAL; + break; + } + ret = rdma_set_afonly(ctx->cm_id, *((int *) optval) ? 1 : 0); + break; default: ret = -ENOSYS; } diff --git a/include/rdma/rdma_cm.h b/include/rdma/rdma_cm.h index 51988f808181..ad3a3142383a 100644 --- a/include/rdma/rdma_cm.h +++ b/include/rdma/rdma_cm.h @@ -357,4 +357,14 @@ void rdma_set_service_type(struct rdma_cm_id *id, int tos); */ int rdma_set_reuseaddr(struct rdma_cm_id *id, int reuse); +/** + * rdma_set_afonly - Specify that listens are restricted to the + * bound address family only. + * @id: Communication identifer to configure. + * @afonly: Value indicating if listens are restricted. + * + * Must be set before identifier is in the listening state. + */ +int rdma_set_afonly(struct rdma_cm_id *id, int afonly); + #endif /* RDMA_CM_H */ diff --git a/include/rdma/rdma_user_cm.h b/include/rdma/rdma_user_cm.h index 5348a000c8f3..1ee9239ff8c2 100644 --- a/include/rdma/rdma_user_cm.h +++ b/include/rdma/rdma_user_cm.h @@ -224,6 +224,7 @@ enum { enum { RDMA_OPTION_ID_TOS = 0, RDMA_OPTION_ID_REUSEADDR = 1, + RDMA_OPTION_ID_AFONLY = 2, RDMA_OPTION_IB_PATH = 1 }; -- cgit v1.2.3 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') 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 From aeab97ed1503bedbe14d1e1c5ab7b90253a67664 Mon Sep 17 00:00:00 2001 From: Erez Shitrit Date: Tue, 19 Jun 2012 11:21:38 +0300 Subject: IB/sa: Add GuidInfoRecord query support This query is needed for SRIOV alias GUID support. The query is implemented per the IB Spec definition in section 15.2.5.18 (GuidInfoRecord). Signed-off-by: Erez Shitrit Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/infiniband/core/sa_query.c | 133 +++++++++++++++++++++++++++++++++++++ include/rdma/ib_sa.h | 33 +++++++++ 2 files changed, 166 insertions(+) (limited to 'include') diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index fbbfa24cf572..a8905abc56e4 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -94,6 +94,12 @@ struct ib_sa_path_query { struct ib_sa_query sa_query; }; +struct ib_sa_guidinfo_query { + void (*callback)(int, struct ib_sa_guidinfo_rec *, void *); + void *context; + struct ib_sa_query sa_query; +}; + struct ib_sa_mcmember_query { void (*callback)(int, struct ib_sa_mcmember_rec *, void *); void *context; @@ -347,6 +353,34 @@ static const struct ib_field service_rec_table[] = { .size_bits = 2*64 }, }; +#define GUIDINFO_REC_FIELD(field) \ + .struct_offset_bytes = offsetof(struct ib_sa_guidinfo_rec, field), \ + .struct_size_bytes = sizeof((struct ib_sa_guidinfo_rec *) 0)->field, \ + .field_name = "sa_guidinfo_rec:" #field + +static const struct ib_field guidinfo_rec_table[] = { + { GUIDINFO_REC_FIELD(lid), + .offset_words = 0, + .offset_bits = 0, + .size_bits = 16 }, + { GUIDINFO_REC_FIELD(block_num), + .offset_words = 0, + .offset_bits = 16, + .size_bits = 8 }, + { GUIDINFO_REC_FIELD(res1), + .offset_words = 0, + .offset_bits = 24, + .size_bits = 8 }, + { GUIDINFO_REC_FIELD(res2), + .offset_words = 1, + .offset_bits = 0, + .size_bits = 32 }, + { GUIDINFO_REC_FIELD(guid_info_list), + .offset_words = 2, + .offset_bits = 0, + .size_bits = 512 }, +}; + static void free_sm_ah(struct kref *kref) { struct ib_sa_sm_ah *sm_ah = container_of(kref, struct ib_sa_sm_ah, ref); @@ -945,6 +979,105 @@ err1: return ret; } +/* Support GuidInfoRecord */ +static void ib_sa_guidinfo_rec_callback(struct ib_sa_query *sa_query, + int status, + struct ib_sa_mad *mad) +{ + struct ib_sa_guidinfo_query *query = + container_of(sa_query, struct ib_sa_guidinfo_query, sa_query); + + if (mad) { + struct ib_sa_guidinfo_rec rec; + + ib_unpack(guidinfo_rec_table, ARRAY_SIZE(guidinfo_rec_table), + mad->data, &rec); + query->callback(status, &rec, query->context); + } else + query->callback(status, NULL, query->context); +} + +static void ib_sa_guidinfo_rec_release(struct ib_sa_query *sa_query) +{ + kfree(container_of(sa_query, struct ib_sa_guidinfo_query, sa_query)); +} + +int ib_sa_guid_info_rec_query(struct ib_sa_client *client, + struct ib_device *device, u8 port_num, + struct ib_sa_guidinfo_rec *rec, + ib_sa_comp_mask comp_mask, u8 method, + int timeout_ms, gfp_t gfp_mask, + void (*callback)(int status, + struct ib_sa_guidinfo_rec *resp, + void *context), + void *context, + struct ib_sa_query **sa_query) +{ + struct ib_sa_guidinfo_query *query; + struct ib_sa_device *sa_dev = ib_get_client_data(device, &sa_client); + struct ib_sa_port *port; + struct ib_mad_agent *agent; + struct ib_sa_mad *mad; + int ret; + + if (!sa_dev) + return -ENODEV; + + if (method != IB_MGMT_METHOD_GET && + method != IB_MGMT_METHOD_SET && + method != IB_SA_METHOD_DELETE) { + return -EINVAL; + } + + port = &sa_dev->port[port_num - sa_dev->start_port]; + agent = port->agent; + + query = kmalloc(sizeof *query, gfp_mask); + if (!query) + return -ENOMEM; + + query->sa_query.port = port; + ret = alloc_mad(&query->sa_query, gfp_mask); + if (ret) + goto err1; + + ib_sa_client_get(client); + query->sa_query.client = client; + query->callback = callback; + query->context = context; + + mad = query->sa_query.mad_buf->mad; + init_mad(mad, agent); + + query->sa_query.callback = callback ? ib_sa_guidinfo_rec_callback : NULL; + query->sa_query.release = ib_sa_guidinfo_rec_release; + + mad->mad_hdr.method = method; + mad->mad_hdr.attr_id = cpu_to_be16(IB_SA_ATTR_GUID_INFO_REC); + mad->sa_hdr.comp_mask = comp_mask; + + ib_pack(guidinfo_rec_table, ARRAY_SIZE(guidinfo_rec_table), rec, + mad->data); + + *sa_query = &query->sa_query; + + ret = send_mad(&query->sa_query, timeout_ms, gfp_mask); + if (ret < 0) + goto err2; + + return ret; + +err2: + *sa_query = NULL; + ib_sa_client_put(query->sa_query.client); + free_mad(&query->sa_query); + +err1: + kfree(query); + return ret; +} +EXPORT_SYMBOL(ib_sa_guid_info_rec_query); + static void send_handler(struct ib_mad_agent *agent, struct ib_mad_send_wc *mad_send_wc) { diff --git a/include/rdma/ib_sa.h b/include/rdma/ib_sa.h index d44a56388a3e..8275e539bace 100644 --- a/include/rdma/ib_sa.h +++ b/include/rdma/ib_sa.h @@ -251,6 +251,28 @@ struct ib_sa_service_rec { u64 data64[2]; }; +#define IB_SA_GUIDINFO_REC_LID IB_SA_COMP_MASK(0) +#define IB_SA_GUIDINFO_REC_BLOCK_NUM IB_SA_COMP_MASK(1) +#define IB_SA_GUIDINFO_REC_RES1 IB_SA_COMP_MASK(2) +#define IB_SA_GUIDINFO_REC_RES2 IB_SA_COMP_MASK(3) +#define IB_SA_GUIDINFO_REC_GID0 IB_SA_COMP_MASK(4) +#define IB_SA_GUIDINFO_REC_GID1 IB_SA_COMP_MASK(5) +#define IB_SA_GUIDINFO_REC_GID2 IB_SA_COMP_MASK(6) +#define IB_SA_GUIDINFO_REC_GID3 IB_SA_COMP_MASK(7) +#define IB_SA_GUIDINFO_REC_GID4 IB_SA_COMP_MASK(8) +#define IB_SA_GUIDINFO_REC_GID5 IB_SA_COMP_MASK(9) +#define IB_SA_GUIDINFO_REC_GID6 IB_SA_COMP_MASK(10) +#define IB_SA_GUIDINFO_REC_GID7 IB_SA_COMP_MASK(11) + +struct ib_sa_guidinfo_rec { + __be16 lid; + u8 block_num; + /* reserved */ + u8 res1; + __be32 res2; + u8 guid_info_list[64]; +}; + struct ib_sa_client { atomic_t users; struct completion comp; @@ -385,4 +407,15 @@ int ib_init_ah_from_path(struct ib_device *device, u8 port_num, */ void ib_sa_unpack_path(void *attribute, struct ib_sa_path_rec *rec); +/* Support GuidInfoRecord */ +int ib_sa_guid_info_rec_query(struct ib_sa_client *client, + struct ib_device *device, u8 port_num, + struct ib_sa_guidinfo_rec *rec, + ib_sa_comp_mask comp_mask, u8 method, + int timeout_ms, gfp_t gfp_mask, + void (*callback)(int status, + struct ib_sa_guidinfo_rec *resp, + void *context), + void *context, + struct ib_sa_query **sa_query); #endif /* IB_SA_H */ -- cgit v1.2.3 From 3045f0920367e625bbec7d66fadb444e673515af Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Tue, 19 Jun 2012 11:21:39 +0300 Subject: IB/core: Move CM_xxx_ATTR_ID macros from cm_msgs.h to ib_cm.h These macros will be reused by the mlx4 SRIOV-IB CM paravirtualization code, and there is no reason to have them declared both in the IB core in the mlx4 IB driver. Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/infiniband/core/cm_msgs.h | 12 ------------ include/rdma/ib_cm.h | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/cm_msgs.h b/drivers/infiniband/core/cm_msgs.h index 7da9b2102341..be068f47e47e 100644 --- a/drivers/infiniband/core/cm_msgs.h +++ b/drivers/infiniband/core/cm_msgs.h @@ -44,18 +44,6 @@ #define IB_CM_CLASS_VERSION 2 /* IB specification 1.2 */ -#define CM_REQ_ATTR_ID cpu_to_be16(0x0010) -#define CM_MRA_ATTR_ID cpu_to_be16(0x0011) -#define CM_REJ_ATTR_ID cpu_to_be16(0x0012) -#define CM_REP_ATTR_ID cpu_to_be16(0x0013) -#define CM_RTU_ATTR_ID cpu_to_be16(0x0014) -#define CM_DREQ_ATTR_ID cpu_to_be16(0x0015) -#define CM_DREP_ATTR_ID cpu_to_be16(0x0016) -#define CM_SIDR_REQ_ATTR_ID cpu_to_be16(0x0017) -#define CM_SIDR_REP_ATTR_ID cpu_to_be16(0x0018) -#define CM_LAP_ATTR_ID cpu_to_be16(0x0019) -#define CM_APR_ATTR_ID cpu_to_be16(0x001A) - enum cm_msg_sequence { CM_MSG_SEQUENCE_REQ, CM_MSG_SEQUENCE_LAP, diff --git a/include/rdma/ib_cm.h b/include/rdma/ib_cm.h index 83f77ac33957..0e3ff30647d5 100644 --- a/include/rdma/ib_cm.h +++ b/include/rdma/ib_cm.h @@ -262,6 +262,18 @@ struct ib_cm_event { void *private_data; }; +#define CM_REQ_ATTR_ID cpu_to_be16(0x0010) +#define CM_MRA_ATTR_ID cpu_to_be16(0x0011) +#define CM_REJ_ATTR_ID cpu_to_be16(0x0012) +#define CM_REP_ATTR_ID cpu_to_be16(0x0013) +#define CM_RTU_ATTR_ID cpu_to_be16(0x0014) +#define CM_DREQ_ATTR_ID cpu_to_be16(0x0015) +#define CM_DREP_ATTR_ID cpu_to_be16(0x0016) +#define CM_SIDR_REQ_ATTR_ID cpu_to_be16(0x0017) +#define CM_SIDR_REP_ATTR_ID cpu_to_be16(0x0018) +#define CM_LAP_ATTR_ID cpu_to_be16(0x0019) +#define CM_APR_ATTR_ID cpu_to_be16(0x001A) + /** * ib_cm_handler - User-defined callback to process communication events. * @cm_id: Communication identifier associated with the reported event. -- cgit v1.2.3 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') 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 From 039363f38bfe5f6281e9eae5e0518b11577d9d50 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Fri, 6 Jul 2012 15:25:10 -0500 Subject: mm, sl[aou]b: Extract common code for kmem_cache_create() Kmem_cache_create() does a variety of sanity checks but those vary depending on the allocator. Use the strictest tests and put them into a slab_common file. Make the tests conditional on CONFIG_DEBUG_VM. This patch has the effect of adding sanity checks for SLUB and SLOB under CONFIG_DEBUG_VM and removes the checks in SLAB for !CONFIG_DEBUG_VM. Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab.h | 4 ++++ mm/Makefile | 3 ++- mm/slab.c | 24 +++++++------------ mm/slab_common.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ mm/slob.c | 8 +++---- mm/slub.c | 11 +-------- 6 files changed, 87 insertions(+), 31 deletions(-) create mode 100644 mm/slab_common.c (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index 0dd2dfa7beca..0cb7c7eb0416 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -130,6 +130,10 @@ int kmem_cache_shrink(struct kmem_cache *); void kmem_cache_free(struct kmem_cache *, void *); unsigned int kmem_cache_size(struct kmem_cache *); +/* Slab internal function */ +struct kmem_cache *__kmem_cache_create(const char *, size_t, size_t, + unsigned long, + void (*)(void *)); /* * Please use this macro to create slab caches. Simply specify the * name of the structure and maybe some flags that are listed above. diff --git a/mm/Makefile b/mm/Makefile index a156285ce88d..ae370783612d 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -16,7 +16,8 @@ obj-y := filemap.o mempool.o oom_kill.o fadvise.o \ readahead.o swap.o truncate.o vmscan.o shmem.o \ prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ page_isolation.o mm_init.o mmu_context.o percpu.o \ - compaction.o $(mmu-y) + compaction.o slab_common.o $(mmu-y) + obj-y += init-mm.o ifdef CONFIG_NO_BOOTMEM diff --git a/mm/slab.c b/mm/slab.c index 105f188d14a3..10c821e492bf 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1558,7 +1558,7 @@ void __init kmem_cache_init(void) * bug. */ - sizes[INDEX_AC].cs_cachep = kmem_cache_create(names[INDEX_AC].name, + sizes[INDEX_AC].cs_cachep = __kmem_cache_create(names[INDEX_AC].name, sizes[INDEX_AC].cs_size, ARCH_KMALLOC_MINALIGN, ARCH_KMALLOC_FLAGS|SLAB_PANIC, @@ -1566,7 +1566,7 @@ void __init kmem_cache_init(void) if (INDEX_AC != INDEX_L3) { sizes[INDEX_L3].cs_cachep = - kmem_cache_create(names[INDEX_L3].name, + __kmem_cache_create(names[INDEX_L3].name, sizes[INDEX_L3].cs_size, ARCH_KMALLOC_MINALIGN, ARCH_KMALLOC_FLAGS|SLAB_PANIC, @@ -1584,14 +1584,14 @@ void __init kmem_cache_init(void) * allow tighter packing of the smaller caches. */ if (!sizes->cs_cachep) { - sizes->cs_cachep = kmem_cache_create(names->name, + sizes->cs_cachep = __kmem_cache_create(names->name, sizes->cs_size, ARCH_KMALLOC_MINALIGN, ARCH_KMALLOC_FLAGS|SLAB_PANIC, NULL); } #ifdef CONFIG_ZONE_DMA - sizes->cs_dmacachep = kmem_cache_create( + sizes->cs_dmacachep = __kmem_cache_create( names->name_dma, sizes->cs_size, ARCH_KMALLOC_MINALIGN, @@ -2220,7 +2220,7 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp) } /** - * kmem_cache_create - Create a cache. + * __kmem_cache_create - Create a cache. * @name: A string which is used in /proc/slabinfo to identify this cache. * @size: The size of objects to be created in this cache. * @align: The required alignment for the objects. @@ -2247,7 +2247,7 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp) * as davem. */ struct kmem_cache * -kmem_cache_create (const char *name, size_t size, size_t align, +__kmem_cache_create (const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *)) { size_t left_over, slab_size, ralign; @@ -2388,7 +2388,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, /* Get cache's description obj. */ cachep = kmem_cache_zalloc(&cache_cache, gfp); if (!cachep) - goto oops; + return NULL; cachep->nodelists = (struct kmem_list3 **)&cachep->array[nr_cpu_ids]; cachep->object_size = size; @@ -2445,8 +2445,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, printk(KERN_ERR "kmem_cache_create: couldn't create cache %s.\n", name); kmem_cache_free(&cache_cache, cachep); - cachep = NULL; - goto oops; + return NULL; } slab_size = ALIGN(cachep->num * sizeof(kmem_bufctl_t) + sizeof(struct slab), align); @@ -2504,8 +2503,7 @@ kmem_cache_create (const char *name, size_t size, size_t align, if (setup_cpu_cache(cachep, gfp)) { __kmem_cache_destroy(cachep); - cachep = NULL; - goto oops; + return NULL; } if (flags & SLAB_DEBUG_OBJECTS) { @@ -2521,16 +2519,12 @@ kmem_cache_create (const char *name, size_t size, size_t align, /* cache setup completed, link it into the list */ list_add(&cachep->list, &cache_chain); oops: - if (!cachep && (flags & SLAB_PANIC)) - panic("kmem_cache_create(): failed to create slab `%s'\n", - name); if (slab_is_available()) { mutex_unlock(&cache_chain_mutex); put_online_cpus(); } return cachep; } -EXPORT_SYMBOL(kmem_cache_create); #if DEBUG static void check_irq_off(void) diff --git a/mm/slab_common.c b/mm/slab_common.c new file mode 100644 index 000000000000..80412beb67cc --- /dev/null +++ b/mm/slab_common.c @@ -0,0 +1,68 @@ +/* + * Slab allocator functions that are independent of the allocator strategy + * + * (C) 2012 Christoph Lameter + */ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * kmem_cache_create - Create a cache. + * @name: A string which is used in /proc/slabinfo to identify this cache. + * @size: The size of objects to be created in this cache. + * @align: The required alignment for the objects. + * @flags: SLAB flags + * @ctor: A constructor for the objects. + * + * Returns a ptr to the cache on success, NULL on failure. + * Cannot be called within a interrupt, but can be interrupted. + * The @ctor is run when new pages are allocated by the cache. + * + * The flags are + * + * %SLAB_POISON - Poison the slab with a known test pattern (a5a5a5a5) + * to catch references to uninitialised memory. + * + * %SLAB_RED_ZONE - Insert `Red' zones around the allocated memory to check + * for buffer overruns. + * + * %SLAB_HWCACHE_ALIGN - Align the objects in this cache to a hardware + * cacheline. This can be beneficial if you're counting cycles as closely + * as davem. + */ + +struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align, + unsigned long flags, void (*ctor)(void *)) +{ + struct kmem_cache *s = NULL; + +#ifdef CONFIG_DEBUG_VM + if (!name || in_interrupt() || size < sizeof(void *) || + size > KMALLOC_MAX_SIZE) { + printk(KERN_ERR "kmem_cache_create(%s) integrity check" + " failed\n", name); + goto out; + } +#endif + + s = __kmem_cache_create(name, size, align, flags, ctor); + +#ifdef CONFIG_DEBUG_VM +out: +#endif + if (!s && (flags & SLAB_PANIC)) + panic("kmem_cache_create: Failed to create slab '%s'\n", name); + + return s; +} +EXPORT_SYMBOL(kmem_cache_create); diff --git a/mm/slob.c b/mm/slob.c index 95d1c7dd88e0..d63923d549ec 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -506,7 +506,7 @@ size_t ksize(const void *block) } EXPORT_SYMBOL(ksize); -struct kmem_cache *kmem_cache_create(const char *name, size_t size, +struct kmem_cache *__kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *)) { struct kmem_cache *c; @@ -529,13 +529,11 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t size, c->align = ARCH_SLAB_MINALIGN; if (c->align < align) c->align = align; - } else if (flags & SLAB_PANIC) - panic("Cannot create slab cache %s\n", name); - kmemleak_alloc(c, sizeof(struct kmem_cache), 1, GFP_KERNEL); + kmemleak_alloc(c, sizeof(struct kmem_cache), 1, GFP_KERNEL); + } return c; } -EXPORT_SYMBOL(kmem_cache_create); void kmem_cache_destroy(struct kmem_cache *c) { diff --git a/mm/slub.c b/mm/slub.c index 79fe9c6b93cf..6551cc9a51f8 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -3920,15 +3920,12 @@ static struct kmem_cache *find_mergeable(size_t size, return NULL; } -struct kmem_cache *kmem_cache_create(const char *name, size_t size, +struct kmem_cache *__kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *)) { struct kmem_cache *s; char *n; - if (WARN_ON(!name)) - return NULL; - down_write(&slub_lock); s = find_mergeable(size, align, flags, name, ctor); if (s) { @@ -3972,14 +3969,8 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t size, kfree(n); err: up_write(&slub_lock); - - if (flags & SLAB_PANIC) - panic("Cannot create slabcache %s\n", name); - else - s = NULL; return s; } -EXPORT_SYMBOL(kmem_cache_create); #ifdef CONFIG_SMP /* -- cgit v1.2.3 From 97d06609158e61f6bdf538c4a6788e2de492236f Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Fri, 6 Jul 2012 15:25:11 -0500 Subject: mm, sl[aou]b: Common definition for boot state of the slab allocators All allocators have some sort of support for the bootstrap status. Setup a common definition for the boot states and make all slab allocators use that definition. Reviewed-by: Glauber Costa Reviewed-by: Joonsoo Kim Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab.h | 4 ---- mm/slab.c | 45 ++++++++++++++------------------------------- mm/slab.h | 29 +++++++++++++++++++++++++++++ mm/slab_common.c | 9 +++++++++ mm/slob.c | 14 +++++--------- mm/slub.c | 21 +++++---------------- 6 files changed, 62 insertions(+), 60 deletions(-) create mode 100644 mm/slab.h (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index 0cb7c7eb0416..0dd2dfa7beca 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -130,10 +130,6 @@ int kmem_cache_shrink(struct kmem_cache *); void kmem_cache_free(struct kmem_cache *, void *); unsigned int kmem_cache_size(struct kmem_cache *); -/* Slab internal function */ -struct kmem_cache *__kmem_cache_create(const char *, size_t, size_t, - unsigned long, - void (*)(void *)); /* * Please use this macro to create slab caches. Simply specify the * name of the structure and maybe some flags that are listed above. diff --git a/mm/slab.c b/mm/slab.c index 10c821e492bf..59a466b85b0f 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -87,6 +87,7 @@ */ #include +#include "slab.h" #include #include #include @@ -565,27 +566,6 @@ static struct kmem_cache cache_cache = { #define BAD_ALIEN_MAGIC 0x01020304ul -/* - * chicken and egg problem: delay the per-cpu array allocation - * until the general caches are up. - */ -static enum { - NONE, - PARTIAL_AC, - PARTIAL_L3, - EARLY, - LATE, - FULL -} g_cpucache_up; - -/* - * used by boot code to determine if it can use slab based allocator - */ -int slab_is_available(void) -{ - return g_cpucache_up >= EARLY; -} - #ifdef CONFIG_LOCKDEP /* @@ -651,7 +631,7 @@ static void init_node_lock_keys(int q) { struct cache_sizes *s = malloc_sizes; - if (g_cpucache_up < LATE) + if (slab_state < UP) return; for (s = malloc_sizes; s->cs_size != ULONG_MAX; s++) { @@ -1649,14 +1629,14 @@ void __init kmem_cache_init(void) } } - g_cpucache_up = EARLY; + slab_state = UP; } void __init kmem_cache_init_late(void) { struct kmem_cache *cachep; - g_cpucache_up = LATE; + slab_state = UP; /* Annotate slab for lockdep -- annotate the malloc caches */ init_lock_keys(); @@ -1668,6 +1648,9 @@ void __init kmem_cache_init_late(void) BUG(); mutex_unlock(&cache_chain_mutex); + /* Done! */ + slab_state = FULL; + /* * Register a cpu startup notifier callback that initializes * cpu_cache_get for all new cpus @@ -1699,7 +1682,7 @@ static int __init cpucache_init(void) start_cpu_timer(cpu); /* Done! */ - g_cpucache_up = FULL; + slab_state = FULL; return 0; } __initcall(cpucache_init); @@ -2167,10 +2150,10 @@ static size_t calculate_slab_order(struct kmem_cache *cachep, static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp) { - if (g_cpucache_up >= LATE) + if (slab_state >= FULL) return enable_cpucache(cachep, gfp); - if (g_cpucache_up == NONE) { + if (slab_state == DOWN) { /* * Note: the first kmem_cache_create must create the cache * that's used by kmalloc(24), otherwise the creation of @@ -2185,16 +2168,16 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp) */ set_up_list3s(cachep, SIZE_AC); if (INDEX_AC == INDEX_L3) - g_cpucache_up = PARTIAL_L3; + slab_state = PARTIAL_L3; else - g_cpucache_up = PARTIAL_AC; + slab_state = PARTIAL_ARRAYCACHE; } else { cachep->array[smp_processor_id()] = kmalloc(sizeof(struct arraycache_init), gfp); - if (g_cpucache_up == PARTIAL_AC) { + if (slab_state == PARTIAL_ARRAYCACHE) { set_up_list3s(cachep, SIZE_L3); - g_cpucache_up = PARTIAL_L3; + slab_state = PARTIAL_L3; } else { int node; for_each_online_node(node) { diff --git a/mm/slab.h b/mm/slab.h new file mode 100644 index 000000000000..f9a9815cdc82 --- /dev/null +++ b/mm/slab.h @@ -0,0 +1,29 @@ +#ifndef MM_SLAB_H +#define MM_SLAB_H +/* + * Internal slab definitions + */ + +/* + * State of the slab allocator. + * + * This is used to describe the states of the allocator during bootup. + * Allocators use this to gradually bootstrap themselves. Most allocators + * have the problem that the structures used for managing slab caches are + * allocated from slab caches themselves. + */ +enum slab_state { + DOWN, /* No slab functionality yet */ + PARTIAL, /* SLUB: kmem_cache_node available */ + PARTIAL_ARRAYCACHE, /* SLAB: kmalloc size for arraycache available */ + PARTIAL_L3, /* SLAB: kmalloc size for l3 struct available */ + UP, /* Slab caches usable but not all extras yet */ + FULL /* Everything is working */ +}; + +extern enum slab_state slab_state; + +struct kmem_cache *__kmem_cache_create(const char *name, size_t size, + size_t align, unsigned long flags, void (*ctor)(void *)); + +#endif diff --git a/mm/slab_common.c b/mm/slab_common.c index 80412beb67cc..ca1aaf69a1f5 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -16,6 +16,10 @@ #include #include +#include "slab.h" + +enum slab_state slab_state; + /* * kmem_cache_create - Create a cache. * @name: A string which is used in /proc/slabinfo to identify this cache. @@ -66,3 +70,8 @@ out: return s; } EXPORT_SYMBOL(kmem_cache_create); + +int slab_is_available(void) +{ + return slab_state >= UP; +} diff --git a/mm/slob.c b/mm/slob.c index d63923d549ec..0111e0dece93 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -59,6 +59,8 @@ #include #include +#include "slab.h" + #include #include /* struct reclaim_state */ #include @@ -531,6 +533,7 @@ struct kmem_cache *__kmem_cache_create(const char *name, size_t size, c->align = align; kmemleak_alloc(c, sizeof(struct kmem_cache), 1, GFP_KERNEL); + c->refcount = 1; } return c; } @@ -616,19 +619,12 @@ int kmem_cache_shrink(struct kmem_cache *d) } EXPORT_SYMBOL(kmem_cache_shrink); -static unsigned int slob_ready __read_mostly; - -int slab_is_available(void) -{ - return slob_ready; -} - void __init kmem_cache_init(void) { - slob_ready = 1; + slab_state = UP; } void __init kmem_cache_init_late(void) { - /* Nothing to do */ + slab_state = FULL; } diff --git a/mm/slub.c b/mm/slub.c index 6551cc9a51f8..4c385164d9f7 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -16,6 +16,7 @@ #include #include #include +#include "slab.h" #include #include #include @@ -182,13 +183,6 @@ static int kmem_size = sizeof(struct kmem_cache); static struct notifier_block slab_notifier; #endif -static enum { - DOWN, /* No slab functionality available */ - PARTIAL, /* Kmem_cache_node works */ - UP, /* Everything works but does not show up in sysfs */ - SYSFS /* Sysfs up */ -} slab_state = DOWN; - /* A list of all slab caches on the system */ static DECLARE_RWSEM(slub_lock); static LIST_HEAD(slab_caches); @@ -237,11 +231,6 @@ static inline void stat(const struct kmem_cache *s, enum stat_item si) * Core slab cache functions *******************************************************************/ -int slab_is_available(void) -{ - return slab_state >= UP; -} - static inline struct kmem_cache_node *get_node(struct kmem_cache *s, int node) { return s->node[node]; @@ -5274,7 +5263,7 @@ static int sysfs_slab_add(struct kmem_cache *s) const char *name; int unmergeable; - if (slab_state < SYSFS) + if (slab_state < FULL) /* Defer until later */ return 0; @@ -5319,7 +5308,7 @@ static int sysfs_slab_add(struct kmem_cache *s) static void sysfs_slab_remove(struct kmem_cache *s) { - if (slab_state < SYSFS) + if (slab_state < FULL) /* * Sysfs has not been setup yet so no need to remove the * cache from sysfs. @@ -5347,7 +5336,7 @@ static int sysfs_slab_alias(struct kmem_cache *s, const char *name) { struct saved_alias *al; - if (slab_state == SYSFS) { + if (slab_state == FULL) { /* * If we have a leftover link then remove it. */ @@ -5380,7 +5369,7 @@ static int __init slab_sysfs_init(void) return -ENOSYS; } - slab_state = SYSFS; + slab_state = FULL; list_for_each_entry(s, &slab_caches, list) { err = sysfs_slab_add(s); -- cgit v1.2.3 From af97bace2cca58ee7c94bf4d31e820f29688d7a5 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Mon, 11 Jun 2012 22:56:26 +0200 Subject: i2c-nomadik: move header to The header and driver are only used by arm/mach-u8500 (and potentially arm/mach-nomadik), but the STA2X11 I/O Hub exports on PCIe a number of devices, including i2c-nomadik. This patch allows compilation of the driver under x86. Signed-off-by: Alessandro Rubini Acked-by: Giancarlo Asnaghi Tested-by: Linus Walleij Signed-off-by: Wolfram Sang --- arch/arm/mach-ux500/board-mop500.c | 2 +- arch/arm/mach-ux500/devices-common.h | 2 +- arch/arm/plat-nomadik/include/plat/i2c.h | 39 ------------------------------- drivers/i2c/busses/i2c-nomadik.c | 3 +-- include/linux/platform_data/i2c-nomadik.h | 39 +++++++++++++++++++++++++++++++ 5 files changed, 42 insertions(+), 43 deletions(-) delete mode 100644 arch/arm/plat-nomadik/include/plat/i2c.h create mode 100644 include/linux/platform_data/i2c-nomadik.h (limited to 'include') diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 1509a3cb5833..f9a964836d3a 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -39,7 +40,6 @@ #include #include -#include #include #include diff --git a/arch/arm/mach-ux500/devices-common.h b/arch/arm/mach-ux500/devices-common.h index 6e4706560266..23cf734b5384 100644 --- a/arch/arm/mach-ux500/devices-common.h +++ b/arch/arm/mach-ux500/devices-common.h @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include struct spi_master_cntlr; diff --git a/arch/arm/plat-nomadik/include/plat/i2c.h b/arch/arm/plat-nomadik/include/plat/i2c.h deleted file mode 100644 index 8ba70ffc31ec..000000000000 --- a/arch/arm/plat-nomadik/include/plat/i2c.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2009 ST-Ericsson - * - * 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 __PLAT_I2C_H -#define __PLAT_I2C_H - -enum i2c_freq_mode { - I2C_FREQ_MODE_STANDARD, /* up to 100 Kb/s */ - I2C_FREQ_MODE_FAST, /* up to 400 Kb/s */ - I2C_FREQ_MODE_HIGH_SPEED, /* up to 3.4 Mb/s */ - I2C_FREQ_MODE_FAST_PLUS, /* up to 1 Mb/s */ -}; - -/** - * struct nmk_i2c_controller - client specific controller configuration - * @clk_freq: clock frequency for the operation mode - * @slsu: Slave data setup time in ns. - * The needed setup time for three modes of operation - * are 250ns, 100ns and 10ns respectively thus leading - * to the values of 14, 6, 2 for a 48 MHz i2c clk - * @tft: Tx FIFO Threshold in bytes - * @rft: Rx FIFO Threshold in bytes - * @timeout Slave response timeout(ms) - * @sm: speed mode - */ -struct nmk_i2c_controller { - unsigned long clk_freq; - unsigned short slsu; - unsigned char tft; - unsigned char rft; - int timeout; - enum i2c_freq_mode sm; -}; - -#endif /* __PLAT_I2C_H */ diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 5267ab93d550..752f1fda371f 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -23,8 +23,7 @@ #include #include #include - -#include +#include #define DRIVER_NAME "nmk-i2c" diff --git a/include/linux/platform_data/i2c-nomadik.h b/include/linux/platform_data/i2c-nomadik.h new file mode 100644 index 000000000000..c2303c3e4803 --- /dev/null +++ b/include/linux/platform_data/i2c-nomadik.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009 ST-Ericsson + * + * 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 __PDATA_I2C_NOMADIK_H +#define __PDATA_I2C_NOMADIK_H + +enum i2c_freq_mode { + I2C_FREQ_MODE_STANDARD, /* up to 100 Kb/s */ + I2C_FREQ_MODE_FAST, /* up to 400 Kb/s */ + I2C_FREQ_MODE_HIGH_SPEED, /* up to 3.4 Mb/s */ + I2C_FREQ_MODE_FAST_PLUS, /* up to 1 Mb/s */ +}; + +/** + * struct nmk_i2c_controller - client specific controller configuration + * @clk_freq: clock frequency for the operation mode + * @slsu: Slave data setup time in ns. + * The needed setup time for three modes of operation + * are 250ns, 100ns and 10ns respectively thus leading + * to the values of 14, 6, 2 for a 48 MHz i2c clk + * @tft: Tx FIFO Threshold in bytes + * @rft: Rx FIFO Threshold in bytes + * @timeout Slave response timeout(ms) + * @sm: speed mode + */ +struct nmk_i2c_controller { + unsigned long clk_freq; + unsigned short slsu; + unsigned char tft; + unsigned char rft; + int timeout; + enum i2c_freq_mode sm; +}; + +#endif /* __PDATA_I2C_NOMADIK_H */ -- cgit v1.2.3 From f72b85b8eb6657fae95ac8f5cb20954b4d87a520 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 6 Jul 2012 19:49:54 +0200 Subject: mac80211: remove ieee80211_key_removed This API call was intended to be used by drivers if they want to optimize key handling by removing one key when another is added. Remove it since no driver is using it. If needed, it can always be added back. Signed-off-by: Johannes Berg --- Documentation/DocBook/80211.tmpl | 1 - include/net/mac80211.h | 16 ---------------- net/mac80211/key.c | 20 -------------------- 3 files changed, 37 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index f3e214f9e256..42e7f030cb16 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -404,7 +404,6 @@ !Finclude/net/mac80211.h ieee80211_get_tkip_p1k !Finclude/net/mac80211.h ieee80211_get_tkip_p1k_iv !Finclude/net/mac80211.h ieee80211_get_tkip_p2k -!Finclude/net/mac80211.h ieee80211_key_removed diff --git a/include/net/mac80211.h b/include/net/mac80211.h index e3fa90ce9ecb..c5dbb46debb0 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3591,22 +3591,6 @@ void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success); void ieee80211_request_smps(struct ieee80211_vif *vif, enum ieee80211_smps_mode smps_mode); -/** - * ieee80211_key_removed - disable hw acceleration for key - * @key_conf: The key hw acceleration should be disabled for - * - * This allows drivers to indicate that the given key has been - * removed from hardware acceleration, due to a new key that - * was added. Don't use this if the key can continue to be used - * for TX, if the key restriction is on RX only it is permitted - * to keep the key for TX only and not call this function. - * - * Due to locking constraints, it may only be called during - * @set_key. This function must be allowed to sleep, and the - * key it tries to disable may still be used until it returns. - */ -void ieee80211_key_removed(struct ieee80211_key_conf *key_conf); - /** * ieee80211_ready_on_channel - notification of remain-on-channel start * @hw: pointer as obtained from ieee80211_alloc_hw() diff --git a/net/mac80211/key.c b/net/mac80211/key.c index b3b7e526e245..7ae678ba5d67 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -194,26 +194,6 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; } -void ieee80211_key_removed(struct ieee80211_key_conf *key_conf) -{ - struct ieee80211_key *key; - - key = container_of(key_conf, struct ieee80211_key, conf); - - might_sleep(); - assert_key_lock(key->local); - - key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; - - /* - * Flush TX path to avoid attempts to use this key - * after this function returns. Until then, drivers - * must be prepared to handle the key. - */ - synchronize_rcu(); -} -EXPORT_SYMBOL_GPL(ieee80211_key_removed); - static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx, bool uni, bool multi) { -- cgit v1.2.3 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') 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 From 71bbc9943883cffaf5d7a7728a4e4c50b3ac44d3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 15 Jun 2012 15:30:18 +0200 Subject: cfg80211: use wdev in mgmt-tx/ROC APIs The management frame and remain-on-channel APIs will be needed in the P2P device abstraction, so move them over to the new wdev-based APIs. Userspace can still use both the interface index and wdev identifier for them so it's backward compatible, but for the P2P Device wdev it will be able to use the wdev identifier only. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 22 +++--- drivers/net/wireless/ath/ath6kl/core.h | 5 ++ drivers/net/wireless/ath/ath6kl/wmi.c | 10 +-- include/net/cfg80211.h | 27 ++++--- net/mac80211/cfg.c | 21 +++-- net/mac80211/ieee80211_i.h | 6 ++ net/mac80211/offchannel.c | 6 +- net/mac80211/rx.c | 2 +- net/mac80211/status.c | 9 ++- net/wireless/core.h | 2 +- net/wireless/mlme.c | 34 ++++----- net/wireless/nl80211.c | 119 +++++++++++++++++------------ net/wireless/nl80211.h | 10 +-- 13 files changed, 152 insertions(+), 121 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index aca1d2689e90..5f0c66bb6bdf 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2975,14 +2975,14 @@ static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev, } static int ath6kl_remain_on_channel(struct wiphy *wiphy, - struct net_device *dev, + struct wireless_dev *wdev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie) { - struct ath6kl *ar = ath6kl_priv(dev); - struct ath6kl_vif *vif = netdev_priv(dev); + struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); + struct ath6kl *ar = ath6kl_priv(vif->ndev); u32 id; /* TODO: if already pending or ongoing remain-on-channel, @@ -2999,11 +2999,11 @@ static int ath6kl_remain_on_channel(struct wiphy *wiphy, } static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy, - struct net_device *dev, + struct wireless_dev *wdev, u64 cookie) { - struct ath6kl *ar = ath6kl_priv(dev); - struct ath6kl_vif *vif = netdev_priv(dev); + struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); + struct ath6kl *ar = ath6kl_priv(vif->ndev); if (cookie != vif->last_roc_id) return -ENOENT; @@ -3134,15 +3134,15 @@ static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len) return false; } -static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, +static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, bool no_cck, bool dont_wait_for_ack, u64 *cookie) { - struct ath6kl *ar = ath6kl_priv(dev); - struct ath6kl_vif *vif = netdev_priv(dev); + struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); + struct ath6kl *ar = ath6kl_priv(vif->ndev); u32 id; const struct ieee80211_mgmt *mgmt; bool more_data, queued; @@ -3187,10 +3187,10 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, } static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, - struct net_device *dev, + struct wireless_dev *wdev, u16 frame_type, bool reg) { - struct ath6kl_vif *vif = netdev_priv(dev); + struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n", __func__, frame_type, reg); diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index d38a31de344c..cec49a31029a 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -589,6 +589,11 @@ struct ath6kl_vif { struct list_head mc_filter; }; +static inline struct ath6kl_vif *ath6kl_vif_from_wdev(struct wireless_dev *wdev) +{ + return container_of(wdev, struct ath6kl_vif, wdev); +} + #define WOW_LIST_ID 0 #define WOW_HOST_REQ_DELAY 500 /* ms */ diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index a6caa673e8ad..c30ab4b11d61 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -474,7 +474,7 @@ static int ath6kl_wmi_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap, return -EINVAL; } id = vif->last_roc_id; - cfg80211_ready_on_channel(vif->ndev, id, chan, NL80211_CHAN_NO_HT, + cfg80211_ready_on_channel(&vif->wdev, id, chan, NL80211_CHAN_NO_HT, dur, GFP_ATOMIC); return 0; @@ -513,7 +513,7 @@ static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi, else id = vif->last_roc_id; /* timeout on uncanceled r-o-c */ vif->last_cancel_roc_id = 0; - cfg80211_remain_on_channel_expired(vif->ndev, id, chan, + cfg80211_remain_on_channel_expired(&vif->wdev, id, chan, NL80211_CHAN_NO_HT, GFP_ATOMIC); return 0; @@ -533,7 +533,7 @@ static int ath6kl_wmi_tx_status_event_rx(struct wmi *wmi, u8 *datap, int len, ath6kl_dbg(ATH6KL_DBG_WMI, "tx_status: id=%x ack_status=%u\n", id, ev->ack_status); if (wmi->last_mgmt_tx_frame) { - cfg80211_mgmt_tx_status(vif->ndev, id, + cfg80211_mgmt_tx_status(&vif->wdev, id, wmi->last_mgmt_tx_frame, wmi->last_mgmt_tx_frame_len, !!ev->ack_status, GFP_ATOMIC); @@ -568,7 +568,7 @@ static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len, dlen, freq, vif->probe_req_report); if (vif->probe_req_report || vif->nw_type == AP_NETWORK) - cfg80211_rx_mgmt(vif->ndev, freq, 0, + cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, GFP_ATOMIC); return 0; @@ -608,7 +608,7 @@ static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len, return -EINVAL; } ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq); - cfg80211_rx_mgmt(vif->ndev, freq, 0, + cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, GFP_ATOMIC); return 0; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a14e6a406681..7eaaee7bb07d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1753,23 +1753,23 @@ struct cfg80211_ops { int (*flush_pmksa)(struct wiphy *wiphy, struct net_device *netdev); int (*remain_on_channel)(struct wiphy *wiphy, - struct net_device *dev, + struct wireless_dev *wdev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie); int (*cancel_remain_on_channel)(struct wiphy *wiphy, - struct net_device *dev, + struct wireless_dev *wdev, u64 cookie); - int (*mgmt_tx)(struct wiphy *wiphy, struct net_device *dev, + int (*mgmt_tx)(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, bool no_cck, bool dont_wait_for_ack, u64 *cookie); int (*mgmt_tx_cancel_wait)(struct wiphy *wiphy, - struct net_device *dev, + struct wireless_dev *wdev, u64 cookie); int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev, @@ -1780,7 +1780,7 @@ struct cfg80211_ops { s32 rssi_thold, u32 rssi_hyst); void (*mgmt_frame_register)(struct wiphy *wiphy, - struct net_device *dev, + struct wireless_dev *wdev, u16 frame_type, bool reg); int (*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant); @@ -3279,7 +3279,7 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason, /** * cfg80211_ready_on_channel - notification of remain_on_channel start - * @dev: network device + * @wdev: wireless device * @cookie: the request cookie * @chan: The current channel (from remain_on_channel request) * @channel_type: Channel type @@ -3287,21 +3287,20 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason, * channel * @gfp: allocation flags */ -void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie, +void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp); /** * cfg80211_remain_on_channel_expired - remain_on_channel duration expired - * @dev: network device + * @wdev: wireless device * @cookie: the request cookie * @chan: The current channel (from remain_on_channel request) * @channel_type: Channel type * @gfp: allocation flags */ -void cfg80211_remain_on_channel_expired(struct net_device *dev, - u64 cookie, +void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, gfp_t gfp); @@ -3329,7 +3328,7 @@ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp); /** * cfg80211_rx_mgmt - notification of received, unprocessed management frame - * @dev: network device + * @wdev: wireless device receiving the frame * @freq: Frequency on which the frame was received in MHz * @sig_dbm: signal strength in mBm, or 0 if unknown * @buf: Management frame (header + body) @@ -3344,12 +3343,12 @@ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp); * This function is called whenever an Action frame is received for a station * mode interface, but is not processed in kernel. */ -bool cfg80211_rx_mgmt(struct net_device *dev, int freq, int sig_dbm, +bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_dbm, const u8 *buf, size_t len, gfp_t gfp); /** * cfg80211_mgmt_tx_status - notification of TX status for management frame - * @dev: network device + * @wdev: wireless device receiving the frame * @cookie: Cookie returned by cfg80211_ops::mgmt_tx() * @buf: Management frame (header + body) * @len: length of the frame data @@ -3360,7 +3359,7 @@ bool cfg80211_rx_mgmt(struct net_device *dev, int freq, int sig_dbm, * transmitted with cfg80211_ops::mgmt_tx() to report the TX status of the * transmission attempt. */ -void cfg80211_mgmt_tx_status(struct net_device *dev, u64 cookie, +void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie, const u8 *buf, size_t len, bool ack, gfp_t gfp); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 6a171e299b57..7d9abea37b17 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2299,13 +2299,13 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, } static int ieee80211_remain_on_channel(struct wiphy *wiphy, - struct net_device *dev, + struct wireless_dev *wdev, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_local *local = sdata->local; int ret; @@ -2392,23 +2392,23 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local, } static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, - struct net_device *dev, + struct wireless_dev *wdev, u64 cookie) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_local *local = sdata->local; return ieee80211_cancel_roc(local, cookie, false); } -static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, +static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, bool no_cck, bool dont_wait_for_ack, u64 *cookie) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct sta_info *sta; @@ -2513,21 +2513,20 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, - struct net_device *dev, + struct wireless_dev *wdev, u64 cookie) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_local *local = sdata->local; + struct ieee80211_local *local = wiphy_priv(wiphy); return ieee80211_cancel_roc(local, cookie, true); } static void ieee80211_mgmt_frame_register(struct wiphy *wiphy, - struct net_device *dev, + struct wireless_dev *wdev, u16 frame_type, bool reg) { struct ieee80211_local *local = wiphy_priv(wiphy); - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); switch (frame_type) { case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH: diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e0423f8c0ce1..8f8535ee5995 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1091,6 +1091,12 @@ IEEE80211_DEV_TO_SUB_IF(struct net_device *dev) return netdev_priv(dev); } +static inline struct ieee80211_sub_if_data * +IEEE80211_WDEV_TO_SUB_IF(struct wireless_dev *wdev) +{ + return container_of(wdev, struct ieee80211_sub_if_data, wdev); +} + /* this struct represents 802.11n's RA/TID combination */ struct ieee80211_ra_tid { u8 ra[ETH_ALEN]; diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index b0fb6a2b89ad..8c047fc8b325 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -191,7 +191,7 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc) roc->frame = NULL; } } else { - cfg80211_ready_on_channel(roc->sdata->dev, (unsigned long)roc, + cfg80211_ready_on_channel(&roc->sdata->wdev, (unsigned long)roc, roc->chan, roc->chan_type, roc->req_duration, GFP_KERNEL); } @@ -299,7 +299,7 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc) /* was never transmitted */ if (roc->frame) { - cfg80211_mgmt_tx_status(roc->sdata->dev, + cfg80211_mgmt_tx_status(&roc->sdata->wdev, (unsigned long)roc->frame, roc->frame->data, roc->frame->len, false, GFP_KERNEL); @@ -307,7 +307,7 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc) } if (!roc->mgmt_tx_cookie) - cfg80211_remain_on_channel_expired(roc->sdata->dev, + cfg80211_remain_on_channel_expired(&roc->sdata->wdev, (unsigned long)roc, roc->chan, roc->chan_type, GFP_KERNEL); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index ab5185054e6c..f8cf9e7477a3 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2404,7 +2404,7 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx) if (rx->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) sig = status->signal; - if (cfg80211_rx_mgmt(rx->sdata->dev, status->freq, sig, + if (cfg80211_rx_mgmt(&rx->sdata->wdev, status->freq, sig, rx->skb->data, rx->skb->len, GFP_ATOMIC)) { if (rx->sta) diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 2ed2f27fe8a7..8cd72914cdaf 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -519,14 +519,19 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) u64 cookie = (unsigned long)skb; acked = info->flags & IEEE80211_TX_STAT_ACK; + /* + * TODO: When we have non-netdev frame TX, + * we cannot use skb->dev->ieee80211_ptr + */ + if (ieee80211_is_nullfunc(hdr->frame_control) || ieee80211_is_qos_nullfunc(hdr->frame_control)) cfg80211_probe_status(skb->dev, hdr->addr1, cookie, acked, GFP_ATOMIC); else cfg80211_mgmt_tx_status( - skb->dev, cookie, skb->data, skb->len, - acked, GFP_ATOMIC); + skb->dev->ieee80211_ptr, cookie, skb->data, + skb->len, acked, GFP_ATOMIC); } if (unlikely(info->ack_frame_id)) { diff --git a/net/wireless/core.h b/net/wireless/core.h index 6b0170a5f05f..eae5a25a1691 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -372,7 +372,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid); void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, - struct net_device *dev, + struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index d4fece3bb18a..abe9f82d5a82 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -567,29 +567,28 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, } } -void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie, +void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - nl80211_send_remain_on_channel(rdev, dev, cookie, chan, channel_type, + nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, channel_type, duration, gfp); } EXPORT_SYMBOL(cfg80211_ready_on_channel); -void cfg80211_remain_on_channel_expired(struct net_device *dev, - u64 cookie, +void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, gfp_t gfp) { - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - nl80211_send_remain_on_channel_cancel(rdev, dev, cookie, chan, + nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan, channel_type, gfp); } EXPORT_SYMBOL(cfg80211_remain_on_channel_expired); @@ -678,8 +677,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, list_add(&nreg->list, &wdev->mgmt_registrations); if (rdev->ops->mgmt_frame_register) - rdev->ops->mgmt_frame_register(wiphy, wdev->netdev, - frame_type, true); + rdev->ops->mgmt_frame_register(wiphy, wdev, frame_type, true); out: spin_unlock_bh(&wdev->mgmt_registrations_lock); @@ -702,7 +700,7 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid) if (rdev->ops->mgmt_frame_register) { u16 frame_type = le16_to_cpu(reg->frame_type); - rdev->ops->mgmt_frame_register(wiphy, wdev->netdev, + rdev->ops->mgmt_frame_register(wiphy, wdev, frame_type, false); } @@ -731,14 +729,14 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) } int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, - struct net_device *dev, + struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, bool no_cck, bool dont_wait_for_ack, u64 *cookie) { - struct wireless_dev *wdev = dev->ieee80211_ptr; + struct net_device *dev = wdev->netdev; const struct ieee80211_mgmt *mgmt; u16 stype; @@ -825,16 +823,15 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, return -EINVAL; /* Transmit the Action frame as requested by user space */ - return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, offchan, + return rdev->ops->mgmt_tx(&rdev->wiphy, wdev, chan, offchan, channel_type, channel_type_valid, wait, buf, len, no_cck, dont_wait_for_ack, cookie); } -bool cfg80211_rx_mgmt(struct net_device *dev, int freq, int sig_mbm, +bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, const u8 *buf, size_t len, gfp_t gfp) { - struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct cfg80211_mgmt_registration *reg; @@ -871,7 +868,7 @@ bool cfg80211_rx_mgmt(struct net_device *dev, int freq, int sig_mbm, /* found match! */ /* Indicate the received Action frame to user space */ - if (nl80211_send_mgmt(rdev, dev, reg->nlpid, + if (nl80211_send_mgmt(rdev, wdev, reg->nlpid, freq, sig_mbm, buf, len, gfp)) continue; @@ -886,15 +883,14 @@ bool cfg80211_rx_mgmt(struct net_device *dev, int freq, int sig_mbm, } EXPORT_SYMBOL(cfg80211_rx_mgmt); -void cfg80211_mgmt_tx_status(struct net_device *dev, u64 cookie, +void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie, const u8 *buf, size_t len, bool ack, gfp_t gfp) { - struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); /* Indicate TX status of the Action frame to user space */ - nl80211_send_mgmt_tx_status(rdev, dev, cookie, buf, len, ack, gfp); + nl80211_send_mgmt_tx_status(rdev, wdev, cookie, buf, len, ack, gfp); } EXPORT_SYMBOL(cfg80211_mgmt_tx_status); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5800c49d6942..0dc3356eea40 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1728,6 +1728,11 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) return result; } +static inline u64 wdev_id(struct wireless_dev *wdev) +{ + return (u64)wdev->identifier | + ((u64)wiphy_to_dev(wdev->wiphy)->wiphy_idx << 32); +} static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct cfg80211_registered_device *rdev, @@ -1735,8 +1740,6 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, { struct net_device *dev = wdev->netdev; void *hdr; - u64 wdev_id = (u64)wdev->identifier | - ((u64)rdev->wiphy_idx << 32); hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE); if (!hdr) @@ -1750,7 +1753,7 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFTYPE, wdev->iftype) || - nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id) || + nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) || nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->devlist_generation ^ (cfg80211_rdev_list_generation << 2))) @@ -5752,7 +5755,7 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = info->user_ptr[1]; struct ieee80211_channel *chan; struct sk_buff *msg; void *hdr; @@ -5800,7 +5803,7 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, goto free_msg; } - err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan, + err = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan, channel_type, duration, &cookie); if (err) @@ -5824,7 +5827,7 @@ static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = info->user_ptr[1]; u64 cookie; if (!info->attrs[NL80211_ATTR_COOKIE]) @@ -5835,7 +5838,7 @@ static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); - return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie); + return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, wdev, cookie); } static u32 rateset_to_mask(struct ieee80211_supported_band *sband, @@ -5984,7 +5987,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = info->user_ptr[1]; u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION; if (!info->attrs[NL80211_ATTR_FRAME_MATCH]) @@ -5993,21 +5996,24 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_FRAME_TYPE]) frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]); - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_P2P_GO: + break; + default: return -EOPNOTSUPP; + } /* not much point in registering if we can't reply */ if (!rdev->ops->mgmt_tx) return -EOPNOTSUPP; - return cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid, - frame_type, + return cfg80211_mlme_register_mgmt(wdev, info->snd_pid, frame_type, nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH])); } @@ -6015,7 +6021,7 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = info->user_ptr[1]; struct ieee80211_channel *chan; enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; bool channel_type_valid = false; @@ -6036,14 +6042,18 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->mgmt_tx) return -EOPNOTSUPP; - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_P2P_GO: + break; + default: return -EOPNOTSUPP; + } if (info->attrs[NL80211_ATTR_DURATION]) { if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) @@ -6092,7 +6102,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) } } - err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type, + err = cfg80211_mlme_mgmt_tx(rdev, wdev, chan, offchan, channel_type, channel_type_valid, wait, nla_data(info->attrs[NL80211_ATTR_FRAME]), nla_len(info->attrs[NL80211_ATTR_FRAME]), @@ -6120,7 +6130,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = info->user_ptr[1]; u64 cookie; if (!info->attrs[NL80211_ATTR_COOKIE]) @@ -6129,17 +6139,21 @@ static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *in if (!rdev->ops->mgmt_tx_cancel_wait) return -EOPNOTSUPP; - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_P2P_GO: + break; + default: return -EOPNOTSUPP; + } cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); - return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie); + return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie); } static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) @@ -7172,7 +7186,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_remain_on_channel, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -7180,7 +7194,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_cancel_remain_on_channel, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -7196,7 +7210,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_register_mgmt, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_WDEV | NL80211_FLAG_NEED_RTNL, }, { @@ -7204,7 +7218,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_tx_mgmt, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -7212,7 +7226,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_tx_mgmt_cancel_wait, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -8040,7 +8054,7 @@ nla_put_failure: static void nl80211_send_remain_on_chan_event( int cmd, struct cfg80211_registered_device *rdev, - struct net_device *netdev, u64 cookie, + struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp) @@ -8059,7 +8073,9 @@ static void nl80211_send_remain_on_chan_event( } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || - nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || + (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, + wdev->netdev->ifindex)) || + nla_put_u32(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) || nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) || nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type) || nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) @@ -8081,23 +8097,24 @@ static void nl80211_send_remain_on_chan_event( } void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u64 cookie, + struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp) { nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, - rdev, netdev, cookie, chan, + rdev, wdev, cookie, chan, channel_type, duration, gfp); } void nl80211_send_remain_on_channel_cancel( - struct cfg80211_registered_device *rdev, struct net_device *netdev, + struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, gfp_t gfp) { nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, - rdev, netdev, cookie, chan, + rdev, wdev, cookie, chan, channel_type, 0, gfp); } @@ -8211,10 +8228,11 @@ bool nl80211_unexpected_4addr_frame(struct net_device *dev, } int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 nlpid, + struct wireless_dev *wdev, u32 nlpid, int freq, int sig_dbm, const u8 *buf, size_t len, gfp_t gfp) { + struct net_device *netdev = wdev->netdev; struct sk_buff *msg; void *hdr; @@ -8229,7 +8247,8 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || - nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || + (netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, + netdev->ifindex)) || nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) || (sig_dbm && nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) || @@ -8247,10 +8266,11 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, } void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u64 cookie, + struct wireless_dev *wdev, u64 cookie, const u8 *buf, size_t len, bool ack, gfp_t gfp) { + struct net_device *netdev = wdev->netdev; struct sk_buff *msg; void *hdr; @@ -8265,7 +8285,8 @@ void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev, } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || - nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || + (netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, + netdev->ifindex)) || nla_put(msg, NL80211_ATTR_FRAME, len, buf) || nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie) || (ack && nla_put_flag(msg, NL80211_ATTR_ACK))) diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 01a1122c3b33..0469303b5c3c 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -74,13 +74,13 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, gfp_t gfp); void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - u64 cookie, + struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp); void nl80211_send_remain_on_channel_cancel( - struct cfg80211_registered_device *rdev, struct net_device *netdev, + struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, gfp_t gfp); @@ -92,11 +92,11 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, gfp_t gfp); int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 nlpid, + struct wireless_dev *wdev, u32 nlpid, int freq, int sig_dbm, const u8 *buf, size_t len, gfp_t gfp); void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u64 cookie, + struct wireless_dev *wdev, u64 cookie, const u8 *buf, size_t len, bool ack, gfp_t gfp); -- cgit v1.2.3 From 70c6cce040661204986ebbf22224cb24bd77ea71 Mon Sep 17 00:00:00 2001 From: Qiao Zhou Date: Mon, 9 Jul 2012 14:37:32 +0800 Subject: mfd: Support 88pm80x in 80x driver 88PM800 and 88PM805 are two discrete chips used for power management. Hardware designer can use them together or only one of them according to requirement. 88pm80x.c provides common i2c driver handling for both 800 and 805, such as i2c_driver init, regmap init, read/write api etc. 88pm800.c handles specifically for 800, such as chip init, irq init/handle, mfd device register, including rtc, onkey, regulator( to be add later) etc. besides that, 800 has three i2c device, one regular i2c client, two other i2c dummy for gpadc and power purpose. 88pm805.c handles specifically for 805, such as chip init, irq init/handle, mfd device register, including codec, headset/mic detect etc. the i2c operation of both 800 and 805 are via regmap, and 88pm80x-i2c exported a group of r/w bulk r/w and bits set API for facility. Signed-off-by: Qiao Zhou Reviewed-by: Arnd Bergmann Signed-off-by: Samuel Ortiz --- drivers/mfd/88pm800.c | 593 ++++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/88pm805.c | 299 ++++++++++++++++++++++ drivers/mfd/88pm80x.c | 116 +++++++++ drivers/mfd/Kconfig | 24 ++ drivers/mfd/Makefile | 2 + include/linux/mfd/88pm80x.h | 368 +++++++++++++++++++++++++++ 6 files changed, 1402 insertions(+) create mode 100644 drivers/mfd/88pm800.c create mode 100644 drivers/mfd/88pm805.c create mode 100644 drivers/mfd/88pm80x.c create mode 100644 include/linux/mfd/88pm80x.h (limited to 'include') diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c new file mode 100644 index 000000000000..fe479ccfaa19 --- /dev/null +++ b/drivers/mfd/88pm800.c @@ -0,0 +1,593 @@ +/* + * Base driver for Marvell 88PM800 + * + * Copyright (C) 2012 Marvell International Ltd. + * Haojian Zhuang + * Joseph(Yossi) Hanin + * Qiao Zhou + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#define PM800_CHIP_ID (0x00) + +/* Interrupt Registers */ +#define PM800_INT_STATUS1 (0x05) +#define PM800_ONKEY_INT_STS1 (1 << 0) +#define PM800_EXTON_INT_STS1 (1 << 1) +#define PM800_CHG_INT_STS1 (1 << 2) +#define PM800_BAT_INT_STS1 (1 << 3) +#define PM800_RTC_INT_STS1 (1 << 4) +#define PM800_CLASSD_OC_INT_STS1 (1 << 5) + +#define PM800_INT_STATUS2 (0x06) +#define PM800_VBAT_INT_STS2 (1 << 0) +#define PM800_VSYS_INT_STS2 (1 << 1) +#define PM800_VCHG_INT_STS2 (1 << 2) +#define PM800_TINT_INT_STS2 (1 << 3) +#define PM800_GPADC0_INT_STS2 (1 << 4) +#define PM800_TBAT_INT_STS2 (1 << 5) +#define PM800_GPADC2_INT_STS2 (1 << 6) +#define PM800_GPADC3_INT_STS2 (1 << 7) + +#define PM800_INT_STATUS3 (0x07) + +#define PM800_INT_STATUS4 (0x08) +#define PM800_GPIO0_INT_STS4 (1 << 0) +#define PM800_GPIO1_INT_STS4 (1 << 1) +#define PM800_GPIO2_INT_STS4 (1 << 2) +#define PM800_GPIO3_INT_STS4 (1 << 3) +#define PM800_GPIO4_INT_STS4 (1 << 4) + +#define PM800_INT_ENA_1 (0x09) +#define PM800_ONKEY_INT_ENA1 (1 << 0) +#define PM800_EXTON_INT_ENA1 (1 << 1) +#define PM800_CHG_INT_ENA1 (1 << 2) +#define PM800_BAT_INT_ENA1 (1 << 3) +#define PM800_RTC_INT_ENA1 (1 << 4) +#define PM800_CLASSD_OC_INT_ENA1 (1 << 5) + +#define PM800_INT_ENA_2 (0x0A) +#define PM800_VBAT_INT_ENA2 (1 << 0) +#define PM800_VSYS_INT_ENA2 (1 << 1) +#define PM800_VCHG_INT_ENA2 (1 << 2) +#define PM800_TINT_INT_ENA2 (1 << 3) + +#define PM800_INT_ENA_3 (0x0B) +#define PM800_GPADC0_INT_ENA3 (1 << 0) +#define PM800_GPADC1_INT_ENA3 (1 << 1) +#define PM800_GPADC2_INT_ENA3 (1 << 2) +#define PM800_GPADC3_INT_ENA3 (1 << 3) +#define PM800_GPADC4_INT_ENA3 (1 << 4) + +#define PM800_INT_ENA_4 (0x0C) +#define PM800_GPIO0_INT_ENA4 (1 << 0) +#define PM800_GPIO1_INT_ENA4 (1 << 1) +#define PM800_GPIO2_INT_ENA4 (1 << 2) +#define PM800_GPIO3_INT_ENA4 (1 << 3) +#define PM800_GPIO4_INT_ENA4 (1 << 4) + +/* number of INT_ENA & INT_STATUS regs */ +#define PM800_INT_REG_NUM (4) + +/* Interrupt Number in 88PM800 */ +enum { + PM800_IRQ_ONKEY, /*EN1b0 *//*0 */ + PM800_IRQ_EXTON, /*EN1b1 */ + PM800_IRQ_CHG, /*EN1b2 */ + PM800_IRQ_BAT, /*EN1b3 */ + PM800_IRQ_RTC, /*EN1b4 */ + PM800_IRQ_CLASSD, /*EN1b5 *//*5 */ + PM800_IRQ_VBAT, /*EN2b0 */ + PM800_IRQ_VSYS, /*EN2b1 */ + PM800_IRQ_VCHG, /*EN2b2 */ + PM800_IRQ_TINT, /*EN2b3 */ + PM800_IRQ_GPADC0, /*EN3b0 *//*10 */ + PM800_IRQ_GPADC1, /*EN3b1 */ + PM800_IRQ_GPADC2, /*EN3b2 */ + PM800_IRQ_GPADC3, /*EN3b3 */ + PM800_IRQ_GPADC4, /*EN3b4 */ + PM800_IRQ_GPIO0, /*EN4b0 *//*15 */ + PM800_IRQ_GPIO1, /*EN4b1 */ + PM800_IRQ_GPIO2, /*EN4b2 */ + PM800_IRQ_GPIO3, /*EN4b3 */ + PM800_IRQ_GPIO4, /*EN4b4 *//*19 */ + PM800_MAX_IRQ, +}; + +enum { + /* Procida */ + PM800_CHIP_A0 = 0x60, + PM800_CHIP_A1 = 0x61, + PM800_CHIP_B0 = 0x62, + PM800_CHIP_C0 = 0x63, + PM800_CHIP_END = PM800_CHIP_C0, + + /* Make sure to update this to the last stepping */ + PM8XXX_CHIP_END = PM800_CHIP_END +}; + +static const struct i2c_device_id pm80x_id_table[] = { + {"88PM800", CHIP_PM800}, +}; +MODULE_DEVICE_TABLE(i2c, pm80x_id_table); + +static struct resource rtc_resources[] = { + { + .name = "88pm80x-rtc", + .start = PM800_IRQ_RTC, + .end = PM800_IRQ_RTC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell rtc_devs[] = { + { + .name = "88pm80x-rtc", + .num_resources = ARRAY_SIZE(rtc_resources), + .resources = &rtc_resources[0], + .id = -1, + }, +}; + +static struct resource onkey_resources[] = { + { + .name = "88pm80x-onkey", + .start = PM800_IRQ_ONKEY, + .end = PM800_IRQ_ONKEY, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell onkey_devs[] = { + { + .name = "88pm80x-onkey", + .num_resources = 1, + .resources = &onkey_resources[0], + .id = -1, + }, +}; + +static const struct regmap_irq pm800_irqs[] = { + /* INT0 */ + [PM800_IRQ_ONKEY] = { + .mask = PM800_ONKEY_INT_ENA1, + }, + [PM800_IRQ_EXTON] = { + .mask = PM800_EXTON_INT_ENA1, + }, + [PM800_IRQ_CHG] = { + .mask = PM800_CHG_INT_ENA1, + }, + [PM800_IRQ_BAT] = { + .mask = PM800_BAT_INT_ENA1, + }, + [PM800_IRQ_RTC] = { + .mask = PM800_RTC_INT_ENA1, + }, + [PM800_IRQ_CLASSD] = { + .mask = PM800_CLASSD_OC_INT_ENA1, + }, + /* INT1 */ + [PM800_IRQ_VBAT] = { + .reg_offset = 1, + .mask = PM800_VBAT_INT_ENA2, + }, + [PM800_IRQ_VSYS] = { + .reg_offset = 1, + .mask = PM800_VSYS_INT_ENA2, + }, + [PM800_IRQ_VCHG] = { + .reg_offset = 1, + .mask = PM800_VCHG_INT_ENA2, + }, + [PM800_IRQ_TINT] = { + .reg_offset = 1, + .mask = PM800_TINT_INT_ENA2, + }, + /* INT2 */ + [PM800_IRQ_GPADC0] = { + .reg_offset = 2, + .mask = PM800_GPADC0_INT_ENA3, + }, + [PM800_IRQ_GPADC1] = { + .reg_offset = 2, + .mask = PM800_GPADC1_INT_ENA3, + }, + [PM800_IRQ_GPADC2] = { + .reg_offset = 2, + .mask = PM800_GPADC2_INT_ENA3, + }, + [PM800_IRQ_GPADC3] = { + .reg_offset = 2, + .mask = PM800_GPADC3_INT_ENA3, + }, + [PM800_IRQ_GPADC4] = { + .reg_offset = 2, + .mask = PM800_GPADC4_INT_ENA3, + }, + /* INT3 */ + [PM800_IRQ_GPIO0] = { + .reg_offset = 3, + .mask = PM800_GPIO0_INT_ENA4, + }, + [PM800_IRQ_GPIO1] = { + .reg_offset = 3, + .mask = PM800_GPIO1_INT_ENA4, + }, + [PM800_IRQ_GPIO2] = { + .reg_offset = 3, + .mask = PM800_GPIO2_INT_ENA4, + }, + [PM800_IRQ_GPIO3] = { + .reg_offset = 3, + .mask = PM800_GPIO3_INT_ENA4, + }, + [PM800_IRQ_GPIO4] = { + .reg_offset = 3, + .mask = PM800_GPIO4_INT_ENA4, + }, +}; + +static int __devinit device_gpadc_init(struct pm80x_chip *chip, + struct pm80x_platform_data *pdata) +{ + struct pm80x_subchip *subchip = chip->subchip; + struct regmap *map = subchip->regmap_gpadc; + int data = 0, mask = 0, ret = 0; + + if (!map) { + dev_warn(chip->dev, + "Warning: gpadc regmap is not available!\n"); + return -EINVAL; + } + /* + * initialize GPADC without activating it turn on GPADC + * measurments + */ + ret = regmap_update_bits(map, + PM800_GPADC_MISC_CONFIG2, + PM800_GPADC_MISC_GPFSM_EN, + PM800_GPADC_MISC_GPFSM_EN); + if (ret < 0) + goto out; + /* + * This function configures the ADC as requires for + * CP implementation.CP does not "own" the ADC configuration + * registers and relies on AP. + * Reason: enable automatic ADC measurements needed + * for CP to get VBAT and RF temperature readings. + */ + ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN1, + PM800_MEAS_EN1_VBAT, PM800_MEAS_EN1_VBAT); + if (ret < 0) + goto out; + ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN2, + (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN), + (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN)); + if (ret < 0) + goto out; + + /* + * the defult of PM800 is GPADC operates at 100Ks/s rate + * and Number of GPADC slots with active current bias prior + * to GPADC sampling = 1 slot for all GPADCs set for + * Temprature mesurmants + */ + mask = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 | + PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3); + + if (pdata && (pdata->batt_det == 0)) + data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 | + PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3); + else + data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN2 | + PM800_GPADC_GP_BIAS_EN3); + + ret = regmap_update_bits(map, PM800_GP_BIAS_ENA1, mask, data); + if (ret < 0) + goto out; + + dev_info(chip->dev, "pm800 device_gpadc_init: Done\n"); + return 0; + +out: + dev_info(chip->dev, "pm800 device_gpadc_init: Failed!\n"); + return ret; +} + +static int __devinit device_irq_init_800(struct pm80x_chip *chip) +{ + struct regmap *map = chip->regmap; + unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + int data, mask, ret = -EINVAL; + + if (!map || !chip->irq) { + dev_err(chip->dev, "incorrect parameters\n"); + return -EINVAL; + } + + /* + * irq_mode defines the way of clearing interrupt. it's read-clear by + * default. + */ + mask = + PM800_WAKEUP2_INV_INT | PM800_WAKEUP2_INT_CLEAR | + PM800_WAKEUP2_INT_MASK; + + data = PM800_WAKEUP2_INT_CLEAR; + ret = regmap_update_bits(map, PM800_WAKEUP2, mask, data); + + if (ret < 0) + goto out; + + ret = + regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1, + chip->regmap_irq_chip, &chip->irq_data); + +out: + return ret; +} + +static void device_irq_exit_800(struct pm80x_chip *chip) +{ + regmap_del_irq_chip(chip->irq, chip->irq_data); +} + +static struct regmap_irq_chip pm800_irq_chip = { + .name = "88pm800", + .irqs = pm800_irqs, + .num_irqs = ARRAY_SIZE(pm800_irqs), + + .num_regs = 4, + .status_base = PM800_INT_STATUS1, + .mask_base = PM800_INT_ENA_1, + .ack_base = PM800_INT_STATUS1, +}; + +static int pm800_pages_init(struct pm80x_chip *chip) +{ + struct pm80x_subchip *subchip; + struct i2c_client *client = chip->client; + + subchip = chip->subchip; + /* PM800 block power: i2c addr 0x31 */ + if (subchip->power_page_addr) { + subchip->power_page = + i2c_new_dummy(client->adapter, subchip->power_page_addr); + subchip->regmap_power = + devm_regmap_init_i2c(subchip->power_page, + &pm80x_regmap_config); + i2c_set_clientdata(subchip->power_page, chip); + } else + dev_info(chip->dev, + "PM800 block power 0x31: No power_page_addr\n"); + + /* PM800 block GPADC: i2c addr 0x32 */ + if (subchip->gpadc_page_addr) { + subchip->gpadc_page = i2c_new_dummy(client->adapter, + subchip->gpadc_page_addr); + subchip->regmap_gpadc = + devm_regmap_init_i2c(subchip->gpadc_page, + &pm80x_regmap_config); + i2c_set_clientdata(subchip->gpadc_page, chip); + } else + dev_info(chip->dev, + "PM800 block GPADC 0x32: No gpadc_page_addr\n"); + + return 0; +} + +static void pm800_pages_exit(struct pm80x_chip *chip) +{ + struct pm80x_subchip *subchip; + + regmap_exit(chip->regmap); + i2c_unregister_device(chip->client); + + subchip = chip->subchip; + if (subchip->power_page) { + regmap_exit(subchip->regmap_power); + i2c_unregister_device(subchip->power_page); + } + if (subchip->gpadc_page) { + regmap_exit(subchip->regmap_gpadc); + i2c_unregister_device(subchip->gpadc_page); + } +} + +static int __devinit device_800_init(struct pm80x_chip *chip, + struct pm80x_platform_data *pdata) +{ + int ret, pmic_id; + + regmap_read(chip->regmap, PM800_CHIP_ID, &ret); + if (ret < 0) { + dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret); + goto out; + } + + pmic_id = ret & PM80X_VERSION_MASK; + + if ((pmic_id >= PM800_CHIP_A0) && (pmic_id <= PM800_CHIP_END)) { + chip->version = ret; + dev_info(chip->dev, + "88PM80x:Marvell 88PM800 (ID:0x%x) detected\n", ret); + } else { + dev_err(chip->dev, + "Failed to detect Marvell 88PM800:ChipID[0x%x]\n", ret); + goto out; + } + + /* + * alarm wake up bit will be clear in device_irq_init(), + * read before that + */ + regmap_read(chip->regmap, PM800_RTC_CONTROL, &ret); + if (ret < 0) { + dev_err(chip->dev, "Failed to read RTC register: %d\n", ret); + goto out; + } + if (ret & PM800_ALARM_WAKEUP) { + if (pdata && pdata->rtc) + pdata->rtc->rtc_wakeup = 1; + } + + ret = device_gpadc_init(chip, pdata); + if (ret < 0) { + dev_err(chip->dev, "[%s]Failed to init gpadc\n", __func__); + goto out; + } + + chip->regmap_irq_chip = &pm800_irq_chip; + + ret = device_irq_init_800(chip); + if (ret < 0) { + dev_err(chip->dev, "[%s]Failed to init pm800 irq\n", __func__); + goto out; + } + + ret = + mfd_add_devices(chip->dev, 0, &onkey_devs[0], + ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0); + if (ret < 0) { + dev_err(chip->dev, "Failed to add onkey subdev\n"); + goto out_dev; + } else + dev_info(chip->dev, "[%s]:Added mfd onkey_devs\n", __func__); + + if (pdata && pdata->rtc) { + rtc_devs[0].platform_data = pdata->rtc; + rtc_devs[0].pdata_size = sizeof(struct pm80x_rtc_pdata); + ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0], + ARRAY_SIZE(rtc_devs), NULL, 0); + if (ret < 0) { + dev_err(chip->dev, "Failed to add rtc subdev\n"); + goto out_dev; + } else + dev_info(chip->dev, + "[%s]:Added mfd rtc_devs\n", __func__); + } + + return 0; +out_dev: + mfd_remove_devices(chip->dev); + device_irq_exit_800(chip); +out: + return ret; +} + +static int __devinit pm800_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct pm80x_chip *chip; + struct pm80x_platform_data *pdata = client->dev.platform_data; + struct pm80x_subchip *subchip; + + ret = pm80x_init(client, id); + if (ret) { + dev_err(&client->dev, "pm800_init fail\n"); + goto out_init; + } + + chip = i2c_get_clientdata(client); + + /* init subchip for PM800 */ + subchip = + devm_kzalloc(&client->dev, sizeof(struct pm80x_subchip), + GFP_KERNEL); + if (!subchip) { + ret = -ENOMEM; + goto err_subchip_alloc; + } + + subchip->power_page_addr = pdata->power_page_addr; + subchip->gpadc_page_addr = pdata->gpadc_page_addr; + chip->subchip = subchip; + + ret = device_800_init(chip, pdata); + if (ret) { + dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id); + goto err_800_init; + } + + ret = pm800_pages_init(chip); + if (ret) { + dev_err(&client->dev, "pm800_pages_init failed!\n"); + goto err_page_init; + } + + if (pdata->plat_config) + pdata->plat_config(chip, pdata); + +err_page_init: + mfd_remove_devices(chip->dev); + device_irq_exit_800(chip); +err_800_init: + devm_kfree(&client->dev, subchip); +err_subchip_alloc: + pm80x_deinit(client); +out_init: + return ret; +} + +static int __devexit pm800_remove(struct i2c_client *client) +{ + struct pm80x_chip *chip = i2c_get_clientdata(client); + + mfd_remove_devices(chip->dev); + device_irq_exit_800(chip); + + pm800_pages_exit(chip); + devm_kfree(&client->dev, chip->subchip); + + pm80x_deinit(client); + + return 0; +} + +static struct i2c_driver pm800_driver = { + .driver = { + .name = "88PM80X", + .owner = THIS_MODULE, + .pm = &pm80x_pm_ops, + }, + .probe = pm800_probe, + .remove = __devexit_p(pm800_remove), + .id_table = pm80x_id_table, +}; + +static int __init pm800_i2c_init(void) +{ + return i2c_add_driver(&pm800_driver); +} +subsys_initcall(pm800_i2c_init); + +static void __exit pm800_i2c_exit(void) +{ + i2c_del_driver(&pm800_driver); +} +module_exit(pm800_i2c_exit); + +MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM800"); +MODULE_AUTHOR("Qiao Zhou "); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/88pm805.c b/drivers/mfd/88pm805.c new file mode 100644 index 000000000000..d93c3091cb9f --- /dev/null +++ b/drivers/mfd/88pm805.c @@ -0,0 +1,299 @@ +/* + * Base driver for Marvell 88PM805 + * + * Copyright (C) 2012 Marvell International Ltd. + * Haojian Zhuang + * Joseph(Yossi) Hanin + * Qiao Zhou + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PM805_CHIP_ID (0x00) + +static const struct i2c_device_id pm80x_id_table[] = { + {"88PM805", CHIP_PM805}, +}; +MODULE_DEVICE_TABLE(i2c, pm80x_id_table); + +/* Interrupt Number in 88PM805 */ +enum { + PM805_IRQ_LDO_OFF, /*0 */ + PM805_IRQ_SRC_DPLL_LOCK, /*1 */ + PM805_IRQ_CLIP_FAULT, + PM805_IRQ_MIC_CONFLICT, + PM805_IRQ_HP2_SHRT, + PM805_IRQ_HP1_SHRT, /*5 */ + PM805_IRQ_FINE_PLL_FAULT, + PM805_IRQ_RAW_PLL_FAULT, + PM805_IRQ_VOLP_BTN_DET, + PM805_IRQ_VOLM_BTN_DET, + PM805_IRQ_SHRT_BTN_DET, /*10 */ + PM805_IRQ_MIC_DET, /*11 */ + + PM805_MAX_IRQ, +}; + +static struct resource codec_resources[] = { + { + /* Headset microphone insertion or removal */ + .name = "micin", + .start = PM805_IRQ_MIC_DET, + .end = PM805_IRQ_MIC_DET, + .flags = IORESOURCE_IRQ, + }, + { + /* Audio short HP1 */ + .name = "audio-short1", + .start = PM805_IRQ_HP1_SHRT, + .end = PM805_IRQ_HP1_SHRT, + .flags = IORESOURCE_IRQ, + }, + { + /* Audio short HP2 */ + .name = "audio-short2", + .start = PM805_IRQ_HP2_SHRT, + .end = PM805_IRQ_HP2_SHRT, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell codec_devs[] = { + { + .name = "88pm80x-codec", + .num_resources = ARRAY_SIZE(codec_resources), + .resources = &codec_resources[0], + .id = -1, + }, +}; + +static struct regmap_irq pm805_irqs[] = { + /* INT0 */ + [PM805_IRQ_LDO_OFF] = { + .mask = PM805_INT1_HP1_SHRT, + }, + [PM805_IRQ_SRC_DPLL_LOCK] = { + .mask = PM805_INT1_HP2_SHRT, + }, + [PM805_IRQ_CLIP_FAULT] = { + .mask = PM805_INT1_MIC_CONFLICT, + }, + [PM805_IRQ_MIC_CONFLICT] = { + .mask = PM805_INT1_CLIP_FAULT, + }, + [PM805_IRQ_HP2_SHRT] = { + .mask = PM805_INT1_LDO_OFF, + }, + [PM805_IRQ_HP1_SHRT] = { + .mask = PM805_INT1_SRC_DPLL_LOCK, + }, + /* INT1 */ + [PM805_IRQ_FINE_PLL_FAULT] = { + .reg_offset = 1, + .mask = PM805_INT2_MIC_DET, + }, + [PM805_IRQ_RAW_PLL_FAULT] = { + .reg_offset = 1, + .mask = PM805_INT2_SHRT_BTN_DET, + }, + [PM805_IRQ_VOLP_BTN_DET] = { + .reg_offset = 1, + .mask = PM805_INT2_VOLM_BTN_DET, + }, + [PM805_IRQ_VOLM_BTN_DET] = { + .reg_offset = 1, + .mask = PM805_INT2_VOLP_BTN_DET, + }, + [PM805_IRQ_SHRT_BTN_DET] = { + .reg_offset = 1, + .mask = PM805_INT2_RAW_PLL_FAULT, + }, + [PM805_IRQ_MIC_DET] = { + .reg_offset = 1, + .mask = PM805_INT2_FINE_PLL_FAULT, + }, +}; + +static int __devinit device_irq_init_805(struct pm80x_chip *chip) +{ + struct regmap *map = chip->regmap; + unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + int data, mask, ret = -EINVAL; + + if (!map || !chip->irq) { + dev_err(chip->dev, "incorrect parameters\n"); + return -EINVAL; + } + + /* + * irq_mode defines the way of clearing interrupt. it's read-clear by + * default. + */ + mask = + PM805_STATUS0_INT_CLEAR | PM805_STATUS0_INV_INT | + PM800_STATUS0_INT_MASK; + + data = PM805_STATUS0_INT_CLEAR; + ret = regmap_update_bits(map, PM805_INT_STATUS0, mask, data); + /* + * PM805_INT_STATUS is under 32K clock domain, so need to + * add proper delay before the next I2C register access. + */ + msleep(1); + + if (ret < 0) + goto out; + + ret = + regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1, + chip->regmap_irq_chip, &chip->irq_data); + +out: + return ret; +} + +static void device_irq_exit_805(struct pm80x_chip *chip) +{ + regmap_del_irq_chip(chip->irq, chip->irq_data); +} + +static struct regmap_irq_chip pm805_irq_chip = { + .name = "88pm805", + .irqs = pm805_irqs, + .num_irqs = ARRAY_SIZE(pm805_irqs), + + .num_regs = 2, + .status_base = PM805_INT_STATUS1, + .mask_base = PM805_INT_MASK1, + .ack_base = PM805_INT_STATUS1, +}; + +static int __devinit device_805_init(struct pm80x_chip *chip) +{ + int ret = 0; + struct regmap *map = chip->regmap; + + if (!map) { + dev_err(chip->dev, "regmap is invalid\n"); + return -EINVAL; + } + + regmap_read(map, PM805_CHIP_ID, &ret); + if (ret < 0) { + dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret); + goto out_irq_init; + } + chip->version = ret; + + chip->regmap_irq_chip = &pm805_irq_chip; + + ret = device_irq_init_805(chip); + if (ret < 0) { + dev_err(chip->dev, "Failed to init pm805 irq!\n"); + goto out_irq_init; + } + + ret = mfd_add_devices(chip->dev, 0, &codec_devs[0], + ARRAY_SIZE(codec_devs), &codec_resources[0], 0); + if (ret < 0) { + dev_err(chip->dev, "Failed to add codec subdev\n"); + goto out_codec; + } else + dev_info(chip->dev, "[%s]:Added mfd codec_devs\n", __func__); + + return 0; + +out_codec: + device_irq_exit_805(chip); +out_irq_init: + return ret; +} + +static int __devinit pm805_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct pm80x_chip *chip; + struct pm80x_platform_data *pdata = client->dev.platform_data; + + ret = pm80x_init(client, id); + if (ret) { + dev_err(&client->dev, "pm805_init fail!\n"); + goto out_init; + } + + chip = i2c_get_clientdata(client); + + ret = device_805_init(chip); + if (ret) { + dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id); + goto err_805_init; + } + + if (pdata->plat_config) + pdata->plat_config(chip, pdata); + +err_805_init: + pm80x_deinit(client); +out_init: + return ret; +} + +static int __devexit pm805_remove(struct i2c_client *client) +{ + struct pm80x_chip *chip = i2c_get_clientdata(client); + + mfd_remove_devices(chip->dev); + device_irq_exit_805(chip); + + pm80x_deinit(client); + + return 0; +} + +static struct i2c_driver pm805_driver = { + .driver = { + .name = "88PM80X", + .owner = THIS_MODULE, + .pm = &pm80x_pm_ops, + }, + .probe = pm805_probe, + .remove = __devexit_p(pm805_remove), + .id_table = pm80x_id_table, +}; + +static int __init pm805_i2c_init(void) +{ + return i2c_add_driver(&pm805_driver); +} +subsys_initcall(pm805_i2c_init); + +static void __exit pm805_i2c_exit(void) +{ + i2c_del_driver(&pm805_driver); +} +module_exit(pm805_i2c_exit); + +MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM805"); +MODULE_AUTHOR("Qiao Zhou "); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/88pm80x.c b/drivers/mfd/88pm80x.c new file mode 100644 index 000000000000..90aa18a37f79 --- /dev/null +++ b/drivers/mfd/88pm80x.c @@ -0,0 +1,116 @@ +/* + * I2C driver for Marvell 88PM80x + * + * Copyright (C) 2012 Marvell International Ltd. + * Haojian Zhuang + * Joseph(Yossi) Hanin + * Qiao Zhou + * + * 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 + + +const struct regmap_config pm80x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +int __devinit pm80x_init(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pm80x_chip *chip; + struct regmap *map; + int ret = 0; + + chip = + devm_kzalloc(&client->dev, sizeof(struct pm80x_chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + map = devm_regmap_init_i2c(client, &pm80x_regmap_config); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + ret); + goto err_regmap_init; + } + + chip->id = id->driver_data; + if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805) { + ret = -EINVAL; + goto err_chip_id; + } + + chip->client = client; + chip->regmap = map; + + chip->irq = client->irq; + + chip->dev = &client->dev; + dev_set_drvdata(chip->dev, chip); + i2c_set_clientdata(chip->client, chip); + + device_init_wakeup(&client->dev, 1); + + return 0; + +err_chip_id: + regmap_exit(map); +err_regmap_init: + devm_kfree(&client->dev, chip); + return ret; +} +EXPORT_SYMBOL_GPL(pm80x_init); + +int __devexit pm80x_deinit(struct i2c_client *client) +{ + struct pm80x_chip *chip = i2c_get_clientdata(client); + + regmap_exit(chip->regmap); + devm_kfree(&client->dev, chip); + + return 0; +} +EXPORT_SYMBOL_GPL(pm80x_deinit); + +#ifdef CONFIG_PM_SLEEP +static int pm80x_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct pm80x_chip *chip = i2c_get_clientdata(client); + + if (chip && chip->wu_flag) + if (device_may_wakeup(chip->dev)) + enable_irq_wake(chip->irq); + + return 0; +} + +static int pm80x_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct pm80x_chip *chip = i2c_get_clientdata(client); + + if (chip && chip->wu_flag) + if (device_may_wakeup(chip->dev)) + disable_irq_wake(chip->irq); + + return 0; +} +#endif + +SIMPLE_DEV_PM_OPS(pm80x_pm_ops, pm80x_suspend, pm80x_resume); +EXPORT_SYMBOL_GPL(pm80x_pm_ops); + +MODULE_DESCRIPTION("I2C Driver for Marvell 88PM80x"); +MODULE_AUTHOR("Qiao Zhou "); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 5c043693f52c..9c3ab2ab7dc7 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -21,6 +21,30 @@ config MFD_88PM860X select individual components like voltage regulators, RTC and battery-charger under the corresponding menus. +config MFD_88PM800 + tristate "Support Marvell 88PM800" + depends on I2C=y && GENERIC_HARDIRQS + select REGMAP_I2C + select REGMAP_IRQ + select MFD_CORE + help + This supports for Marvell 88PM800 Power Management IC. + This includes the I2C driver and the core APIs _only_, you have to + select individual components like voltage regulators, RTC and + battery-charger under the corresponding menus. + +config MFD_88PM805 + tristate "Support Marvell 88PM805" + depends on I2C=y && GENERIC_HARDIRQS + select REGMAP_I2C + select REGMAP_IRQ + select MFD_CORE + help + This supports for Marvell 88PM805 Power Management IC. This includes + the I2C driver and the core APIs _only_, you have to select individual + components like codec device, headset/Mic device under the + corresponding menus. + config MFD_SM501 tristate "Support for Silicon Motion SM501" ---help--- diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f28885bb103c..09674a99eb60 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -4,6 +4,8 @@ 88pm860x-objs := 88pm860x-core.o 88pm860x-i2c.o obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o +obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o +obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o diff --git a/include/linux/mfd/88pm80x.h b/include/linux/mfd/88pm80x.h new file mode 100644 index 000000000000..6c126e9714a3 --- /dev/null +++ b/include/linux/mfd/88pm80x.h @@ -0,0 +1,368 @@ +/* + * Marvell 88PM80x Interface + * + * Copyright (C) 2012 Marvell International Ltd. + * Qiao Zhou + * + * 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 __LINUX_MFD_88PM80X_H +#define __LINUX_MFD_88PM80X_H + +#include +#include +#include +#include + +#define PM80X_VERSION_MASK (0xFF) /* 80X chip ID mask */ +enum { + CHIP_INVALID = 0, + CHIP_PM800, + CHIP_PM805, + CHIP_MAX, +}; + +enum { + PM800_ID_BUCK1 = 0, + PM800_ID_BUCK2, + PM800_ID_BUCK3, + PM800_ID_BUCK4, + PM800_ID_BUCK5, + + PM800_ID_LDO1, + PM800_ID_LDO2, + PM800_ID_LDO3, + PM800_ID_LDO4, + PM800_ID_LDO5, + PM800_ID_LDO6, + PM800_ID_LDO7, + PM800_ID_LDO8, + PM800_ID_LDO9, + PM800_ID_LDO10, + PM800_ID_LDO11, + PM800_ID_LDO12, + PM800_ID_LDO13, + PM800_ID_LDO14, + PM800_ID_LDO15, + PM800_ID_LDO16, + PM800_ID_LDO17, + PM800_ID_LDO18, + PM800_ID_LDO19, + + PM800_ID_RG_MAX, +}; +#define PM800_MAX_REGULATOR PM800_ID_RG_MAX /* 5 Bucks, 19 LDOs */ +#define PM800_NUM_BUCK (5) /*5 Bucks */ +#define PM800_NUM_LDO (19) /*19 Bucks */ + +/* page 0 basic: slave adder 0x60 */ + +#define PM800_STATUS_1 (0x01) +#define PM800_ONKEY_STS1 (1 << 0) +#define PM800_EXTON_STS1 (1 << 1) +#define PM800_CHG_STS1 (1 << 2) +#define PM800_BAT_STS1 (1 << 3) +#define PM800_VBUS_STS1 (1 << 4) +#define PM800_LDO_PGOOD_STS1 (1 << 5) +#define PM800_BUCK_PGOOD_STS1 (1 << 6) + +#define PM800_STATUS_2 (0x02) +#define PM800_RTC_ALARM_STS2 (1 << 0) + +/* Wakeup Registers */ +#define PM800_WAKEUP1 (0x0D) + +#define PM800_WAKEUP2 (0x0E) +#define PM800_WAKEUP2_INV_INT (1 << 0) +#define PM800_WAKEUP2_INT_CLEAR (1 << 1) +#define PM800_WAKEUP2_INT_MASK (1 << 2) + +#define PM800_POWER_UP_LOG (0x10) + +/* Referance and low power registers */ +#define PM800_LOW_POWER1 (0x20) +#define PM800_LOW_POWER2 (0x21) +#define PM800_LOW_POWER_CONFIG3 (0x22) +#define PM800_LOW_POWER_CONFIG4 (0x23) + +/* GPIO register */ +#define PM800_GPIO_0_1_CNTRL (0x30) +#define PM800_GPIO0_VAL (1 << 0) +#define PM800_GPIO0_GPIO_MODE(x) (x << 1) +#define PM800_GPIO1_VAL (1 << 4) +#define PM800_GPIO1_GPIO_MODE(x) (x << 5) + +#define PM800_GPIO_2_3_CNTRL (0x31) +#define PM800_GPIO2_VAL (1 << 0) +#define PM800_GPIO2_GPIO_MODE(x) (x << 1) +#define PM800_GPIO3_VAL (1 << 4) +#define PM800_GPIO3_GPIO_MODE(x) (x << 5) +#define PM800_GPIO3_MODE_MASK 0x1F +#define PM800_GPIO3_HEADSET_MODE PM800_GPIO3_GPIO_MODE(6) + +#define PM800_GPIO_4_CNTRL (0x32) +#define PM800_GPIO4_VAL (1 << 0) +#define PM800_GPIO4_GPIO_MODE(x) (x << 1) + +#define PM800_HEADSET_CNTRL (0x38) +#define PM800_HEADSET_DET_EN (1 << 7) +#define PM800_HSDET_SLP (1 << 1) +/* PWM register */ +#define PM800_PWM1 (0x40) +#define PM800_PWM2 (0x41) +#define PM800_PWM3 (0x42) +#define PM800_PWM4 (0x43) + +/* RTC Registers */ +#define PM800_RTC_CONTROL (0xD0) +#define PM800_RTC_MISC1 (0xE1) +#define PM800_RTC_MISC2 (0xE2) +#define PM800_RTC_MISC3 (0xE3) +#define PM800_RTC_MISC4 (0xE4) +#define PM800_RTC_MISC5 (0xE7) +/* bit definitions of RTC Register 1 (0xD0) */ +#define PM800_ALARM1_EN (1 << 0) +#define PM800_ALARM_WAKEUP (1 << 4) +#define PM800_ALARM (1 << 5) +#define PM800_RTC1_USE_XO (1 << 7) + +/* Regulator Control Registers: BUCK1,BUCK5,LDO1 have DVC */ + +/* buck registers */ +#define PM800_SLEEP_BUCK1 (0x30) + +/* BUCK Sleep Mode Register 1: BUCK[1..4] */ +#define PM800_BUCK_SLP1 (0x5A) +#define PM800_BUCK1_SLP1_SHIFT 0 +#define PM800_BUCK1_SLP1_MASK (0x3 << PM800_BUCK1_SLP1_SHIFT) + +/* page 2 GPADC: slave adder 0x02 */ +#define PM800_GPADC_MEAS_EN1 (0x01) +#define PM800_MEAS_EN1_VBAT (1 << 2) +#define PM800_GPADC_MEAS_EN2 (0x02) +#define PM800_MEAS_EN2_RFTMP (1 << 0) +#define PM800_MEAS_GP0_EN (1 << 2) +#define PM800_MEAS_GP1_EN (1 << 3) +#define PM800_MEAS_GP2_EN (1 << 4) +#define PM800_MEAS_GP3_EN (1 << 5) +#define PM800_MEAS_GP4_EN (1 << 6) + +#define PM800_GPADC_MISC_CONFIG1 (0x05) +#define PM800_GPADC_MISC_CONFIG2 (0x06) +#define PM800_GPADC_MISC_GPFSM_EN (1 << 0) +#define PM800_GPADC_SLOW_MODE(x) (x << 3) + +#define PM800_GPADC_MISC_CONFIG3 (0x09) +#define PM800_GPADC_MISC_CONFIG4 (0x0A) + +#define PM800_GPADC_PREBIAS1 (0x0F) +#define PM800_GPADC0_GP_PREBIAS_TIME(x) (x << 0) +#define PM800_GPADC_PREBIAS2 (0x10) + +#define PM800_GP_BIAS_ENA1 (0x14) +#define PM800_GPADC_GP_BIAS_EN0 (1 << 0) +#define PM800_GPADC_GP_BIAS_EN1 (1 << 1) +#define PM800_GPADC_GP_BIAS_EN2 (1 << 2) +#define PM800_GPADC_GP_BIAS_EN3 (1 << 3) + +#define PM800_GP_BIAS_OUT1 (0x15) +#define PM800_BIAS_OUT_GP0 (1 << 0) +#define PM800_BIAS_OUT_GP1 (1 << 1) +#define PM800_BIAS_OUT_GP2 (1 << 2) +#define PM800_BIAS_OUT_GP3 (1 << 3) + +#define PM800_GPADC0_LOW_TH 0x20 +#define PM800_GPADC1_LOW_TH 0x21 +#define PM800_GPADC2_LOW_TH 0x22 +#define PM800_GPADC3_LOW_TH 0x23 +#define PM800_GPADC4_LOW_TH 0x24 + +#define PM800_GPADC0_UPP_TH 0x30 +#define PM800_GPADC1_UPP_TH 0x31 +#define PM800_GPADC2_UPP_TH 0x32 +#define PM800_GPADC3_UPP_TH 0x33 +#define PM800_GPADC4_UPP_TH 0x34 + +#define PM800_VBBAT_MEAS1 0x40 +#define PM800_VBBAT_MEAS2 0x41 +#define PM800_VBAT_MEAS1 0x42 +#define PM800_VBAT_MEAS2 0x43 +#define PM800_VSYS_MEAS1 0x44 +#define PM800_VSYS_MEAS2 0x45 +#define PM800_VCHG_MEAS1 0x46 +#define PM800_VCHG_MEAS2 0x47 +#define PM800_TINT_MEAS1 0x50 +#define PM800_TINT_MEAS2 0x51 +#define PM800_PMOD_MEAS1 0x52 +#define PM800_PMOD_MEAS2 0x53 + +#define PM800_GPADC0_MEAS1 0x54 +#define PM800_GPADC0_MEAS2 0x55 +#define PM800_GPADC1_MEAS1 0x56 +#define PM800_GPADC1_MEAS2 0x57 +#define PM800_GPADC2_MEAS1 0x58 +#define PM800_GPADC2_MEAS2 0x59 +#define PM800_GPADC3_MEAS1 0x5A +#define PM800_GPADC3_MEAS2 0x5B +#define PM800_GPADC4_MEAS1 0x5C +#define PM800_GPADC4_MEAS2 0x5D + +#define PM800_GPADC4_AVG1 0xA8 +#define PM800_GPADC4_AVG2 0xA9 + +/* 88PM805 Registers */ +#define PM805_MAIN_POWERUP (0x01) +#define PM805_INT_STATUS0 (0x02) /* for ena/dis all interrupts */ + +#define PM805_STATUS0_INT_CLEAR (1 << 0) +#define PM805_STATUS0_INV_INT (1 << 1) +#define PM800_STATUS0_INT_MASK (1 << 2) + +#define PM805_INT_STATUS1 (0x03) + +#define PM805_INT1_HP1_SHRT (1 << 0) +#define PM805_INT1_HP2_SHRT (1 << 1) +#define PM805_INT1_MIC_CONFLICT (1 << 2) +#define PM805_INT1_CLIP_FAULT (1 << 3) +#define PM805_INT1_LDO_OFF (1 << 4) +#define PM805_INT1_SRC_DPLL_LOCK (1 << 5) + +#define PM805_INT_STATUS2 (0x04) + +#define PM805_INT2_MIC_DET (1 << 0) +#define PM805_INT2_SHRT_BTN_DET (1 << 1) +#define PM805_INT2_VOLM_BTN_DET (1 << 2) +#define PM805_INT2_VOLP_BTN_DET (1 << 3) +#define PM805_INT2_RAW_PLL_FAULT (1 << 4) +#define PM805_INT2_FINE_PLL_FAULT (1 << 5) + +#define PM805_INT_MASK1 (0x05) +#define PM805_INT_MASK2 (0x06) +#define PM805_SHRT_BTN_DET (1 << 1) + +/* number of status and int reg in a row */ +#define PM805_INT_REG_NUM (2) + +#define PM805_MIC_DET1 (0x07) +#define PM805_MIC_DET_EN_MIC_DET (1 << 0) +#define PM805_MIC_DET2 (0x08) +#define PM805_MIC_DET_STATUS1 (0x09) + +#define PM805_MIC_DET_STATUS3 (0x0A) +#define PM805_AUTO_SEQ_STATUS1 (0x0B) +#define PM805_AUTO_SEQ_STATUS2 (0x0C) + +#define PM805_ADC_SETTING1 (0x10) +#define PM805_ADC_SETTING2 (0x11) +#define PM805_ADC_SETTING3 (0x11) +#define PM805_ADC_GAIN1 (0x12) +#define PM805_ADC_GAIN2 (0x13) +#define PM805_DMIC_SETTING (0x15) +#define PM805_DWS_SETTING (0x16) +#define PM805_MIC_CONFLICT_STS (0x17) + +#define PM805_PDM_SETTING1 (0x20) +#define PM805_PDM_SETTING2 (0x21) +#define PM805_PDM_SETTING3 (0x22) +#define PM805_PDM_CONTROL1 (0x23) +#define PM805_PDM_CONTROL2 (0x24) +#define PM805_PDM_CONTROL3 (0x25) + +#define PM805_HEADPHONE_SETTING (0x26) +#define PM805_HEADPHONE_GAIN_A2A (0x27) +#define PM805_HEADPHONE_SHORT_STATE (0x28) +#define PM805_EARPHONE_SETTING (0x29) +#define PM805_AUTO_SEQ_SETTING (0x2A) + +struct pm80x_rtc_pdata { + int vrtc; + int rtc_wakeup; +}; + +struct pm80x_subchip { + struct i2c_client *power_page; /* chip client for power page */ + struct i2c_client *gpadc_page; /* chip client for gpadc page */ + struct regmap *regmap_power; + struct regmap *regmap_gpadc; + unsigned short power_page_addr; /* power page I2C address */ + unsigned short gpadc_page_addr; /* gpadc page I2C address */ +}; + +struct pm80x_chip { + struct pm80x_subchip *subchip; + struct device *dev; + struct i2c_client *client; + struct regmap *regmap; + struct regmap_irq_chip *regmap_irq_chip; + struct regmap_irq_chip_data *irq_data; + unsigned char version; + int id; + int irq; + int irq_mode; + unsigned long wu_flag; + spinlock_t lock; +}; + +struct pm80x_platform_data { + struct pm80x_rtc_pdata *rtc; + unsigned short power_page_addr; /* power page I2C address */ + unsigned short gpadc_page_addr; /* gpadc page I2C address */ + int irq_mode; /* Clear interrupt by read/write(0/1) */ + int batt_det; /* enable/disable */ + int (*plat_config)(struct pm80x_chip *chip, + struct pm80x_platform_data *pdata); +}; + +extern const struct dev_pm_ops pm80x_pm_ops; +extern const struct regmap_config pm80x_regmap_config; + +static inline int pm80x_request_irq(struct pm80x_chip *pm80x, int irq, + irq_handler_t handler, unsigned long flags, + const char *name, void *data) +{ + if (!pm80x->irq_data) + return -EINVAL; + return request_threaded_irq(regmap_irq_get_virq(pm80x->irq_data, irq), + NULL, handler, flags, name, data); +} + +static inline void pm80x_free_irq(struct pm80x_chip *pm80x, int irq, void *data) +{ + if (!pm80x->irq_data) + return; + free_irq(regmap_irq_get_virq(pm80x->irq_data, irq), data); +} + +#ifdef CONFIG_PM +static inline int pm80x_dev_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent); + int irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(dev)) + set_bit((1 << irq), &chip->wu_flag); + + return 0; +} + +static inline int pm80x_dev_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent); + int irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(dev)) + clear_bit((1 << irq), &chip->wu_flag); + + return 0; +} +#endif + +extern int pm80x_init(struct i2c_client *client, + const struct i2c_device_id *id) __devinit; +extern int pm80x_deinit(struct i2c_client *client) __devexit; +#endif /* __LINUX_MFD_88PM80X_H */ -- cgit v1.2.3 From 5500e3964b8c154dc5af51ebcd7cd4df5d4abfee Mon Sep 17 00:00:00 2001 From: Qiao Zhou Date: Mon, 9 Jul 2012 14:37:33 +0800 Subject: mfd: Add companion chip in 88pm80x in hw design, 800 is mainly for pmic control, while 805 for audio. but there are 3 registers which controls class D speaker property, and they are defined in 800 i2c client domain. so 805 codec driver needs to use 800 i2c client to access class D speaker reg for audio path management. so add this workaround for the purpose to let 805 access 800 i2c in some scenario. Signed-off-by: Qiao Zhou Reviewed-by: Arnd Bergmann Signed-off-by: Samuel Ortiz --- drivers/mfd/88pm80x.c | 28 ++++++++++++++++++++++++++++ include/linux/mfd/88pm80x.h | 1 + 2 files changed, 29 insertions(+) (limited to 'include') diff --git a/drivers/mfd/88pm80x.c b/drivers/mfd/88pm80x.c index 90aa18a37f79..77b865594c29 100644 --- a/drivers/mfd/88pm80x.c +++ b/drivers/mfd/88pm80x.c @@ -18,6 +18,12 @@ #include #include +/* + * workaround: some registers needed by pm805 are defined in pm800, so + * need to use this global variable to maintain the relation between + * pm800 and pm805. would remove it after HW chip fixes the issue. + */ +static struct pm80x_chip *g_pm80x_chip; const struct regmap_config pm80x_regmap_config = { .reg_bits = 8, @@ -61,6 +67,19 @@ int __devinit pm80x_init(struct i2c_client *client, device_init_wakeup(&client->dev, 1); + /* + * workaround: set g_pm80x_chip to the first probed chip. if the + * second chip is probed, just point to the companion to each + * other so that pm805 can access those specific register. would + * remove it after HW chip fixes the issue. + */ + if (!g_pm80x_chip) + g_pm80x_chip = chip; + else { + chip->companion = g_pm80x_chip->client; + g_pm80x_chip->companion = chip->client; + } + return 0; err_chip_id: @@ -75,6 +94,15 @@ int __devexit pm80x_deinit(struct i2c_client *client) { struct pm80x_chip *chip = i2c_get_clientdata(client); + /* + * workaround: clear the dependency between pm800 and pm805. + * would remove it after HW chip fixes the issue. + */ + if (g_pm80x_chip->companion) + g_pm80x_chip->companion = NULL; + else + g_pm80x_chip = NULL; + regmap_exit(chip->regmap); devm_kfree(&client->dev, chip); diff --git a/include/linux/mfd/88pm80x.h b/include/linux/mfd/88pm80x.h index 6c126e9714a3..103f06d1892d 100644 --- a/include/linux/mfd/88pm80x.h +++ b/include/linux/mfd/88pm80x.h @@ -295,6 +295,7 @@ struct pm80x_chip { struct pm80x_subchip *subchip; struct device *dev; struct i2c_client *client; + struct i2c_client *companion; struct regmap *regmap; struct regmap_irq_chip *regmap_irq_chip; struct regmap_irq_chip_data *irq_data; -- cgit v1.2.3 From a232d56e48024c41c87ba884cc0a0fc98e37f5c6 Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Thu, 5 Jul 2012 09:28:24 +0530 Subject: mfd: Remove the clocks from the list of max77686 regulators Remove the clocks from the list of regulators to correct the value of MAX77686_REG_MAX which is used in the regulator driver to represent the no. of regulators present in max77686. Signed-off-by: Yadwinder Singh Brar Signed-off-by: Samuel Ortiz --- include/linux/mfd/max77686.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/max77686.h b/include/linux/mfd/max77686.h index fcf312678cac..3d7ae4d7fd36 100644 --- a/include/linux/mfd/max77686.h +++ b/include/linux/mfd/max77686.h @@ -67,9 +67,6 @@ enum max77686_regulators { MAX77686_BUCK7, MAX77686_BUCK8, MAX77686_BUCK9, - MAX77686_EN32KHZ_AP, - MAX77686_EN32KHZ_CP, - MAX77686_P32KH, MAX77686_REG_MAX, }; -- cgit v1.2.3 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') 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 From 26c696c678c4ce180599330999e895cded0f625b Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sat, 7 Jul 2012 22:56:40 +0800 Subject: USB: Chipidea: rename struct ci13xxx variables from udc to ci struct ci13xxx represent the controller, which may be device or host, so name its variables as ci. Signed-off-by: Richard Zhao Reviewed-by: Felipe Balbi Signed-off-by: Alexander Shishkin Reviewed-by: Marek Vasut Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 26 +- drivers/usb/chipidea/ci13xxx_msm.c | 12 +- drivers/usb/chipidea/debug.c | 146 ++++----- drivers/usb/chipidea/udc.c | 628 ++++++++++++++++++------------------- include/linux/usb/chipidea.h | 2 +- 5 files changed, 407 insertions(+), 407 deletions(-) (limited to 'include') diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 0b093308548d..9655e3569d4c 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -36,7 +36,7 @@ * @name: string description of the endpoint * @qh: queue head for this endpoint * @wedge: is the endpoint wedged - * @udc: pointer to the controller + * @ci: pointer to the controller * @lock: pointer to controller's spinlock * @td_pool: pointer to controller's TD pool */ @@ -54,7 +54,7 @@ struct ci13xxx_ep { int wedge; /* global resources */ - struct ci13xxx *udc; + struct ci13xxx *ci; spinlock_t *lock; struct dma_pool *td_pool; }; @@ -250,9 +250,9 @@ static inline int ffs_nr(u32 x) * * This function returns register contents */ -static inline u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask) +static inline u32 hw_read(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask) { - return ioread32(udc->hw_bank.regmap[reg]) & mask; + return ioread32(ci->hw_bank.regmap[reg]) & mask; } /** @@ -261,14 +261,14 @@ static inline u32 hw_read(struct ci13xxx *udc, enum ci13xxx_regs reg, u32 mask) * @mask: bitfield mask * @data: new value */ -static inline void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg, +static inline void hw_write(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask, u32 data) { if (~mask) - data = (ioread32(udc->hw_bank.regmap[reg]) & ~mask) + data = (ioread32(ci->hw_bank.regmap[reg]) & ~mask) | (data & mask); - iowrite32(data, udc->hw_bank.regmap[reg]); + iowrite32(data, ci->hw_bank.regmap[reg]); } /** @@ -278,12 +278,12 @@ static inline void hw_write(struct ci13xxx *udc, enum ci13xxx_regs reg, * * This function returns register contents */ -static inline u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg, +static inline u32 hw_test_and_clear(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask) { - u32 val = ioread32(udc->hw_bank.regmap[reg]) & mask; + u32 val = ioread32(ci->hw_bank.regmap[reg]) & mask; - iowrite32(val, udc->hw_bank.regmap[reg]); + iowrite32(val, ci->hw_bank.regmap[reg]); return val; } @@ -295,12 +295,12 @@ static inline u32 hw_test_and_clear(struct ci13xxx *udc, enum ci13xxx_regs reg, * * This function returns register contents */ -static inline u32 hw_test_and_write(struct ci13xxx *udc, enum ci13xxx_regs reg, +static inline u32 hw_test_and_write(struct ci13xxx *ci, enum ci13xxx_regs reg, u32 mask, u32 data) { - u32 val = hw_read(udc, reg, ~0); + u32 val = hw_read(ci, reg, ~0); - hw_write(udc, reg, mask, data); + hw_write(ci, reg, mask, data); return (val & mask) >> ffs_nr(mask); } diff --git a/drivers/usb/chipidea/ci13xxx_msm.c b/drivers/usb/chipidea/ci13xxx_msm.c index 12c0dd6a300c..5a2fe5f9b6c3 100644 --- a/drivers/usb/chipidea/ci13xxx_msm.c +++ b/drivers/usb/chipidea/ci13xxx_msm.c @@ -15,11 +15,11 @@ #include "ci.h" -#define MSM_USB_BASE (udc->hw_bank.abs) +#define MSM_USB_BASE (ci->hw_bank.abs) -static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event) +static void ci13xxx_msm_notify_event(struct ci13xxx *ci, unsigned event) { - struct device *dev = udc->gadget.dev.parent; + struct device *dev = ci->gadget.dev.parent; int val; switch (event) { @@ -34,13 +34,13 @@ static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event) * Put the transceiver in non-driving mode. Otherwise host * may not detect soft-disconnection. */ - val = usb_phy_io_read(udc->transceiver, ULPI_FUNC_CTRL); + val = usb_phy_io_read(ci->transceiver, ULPI_FUNC_CTRL); val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; - usb_phy_io_write(udc->transceiver, val, ULPI_FUNC_CTRL); + usb_phy_io_write(ci->transceiver, val, ULPI_FUNC_CTRL); break; default: - dev_dbg(dev, "unknown ci13xxx_udc event\n"); + dev_dbg(dev, "unknown ci13xxx event\n"); break; } } diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index c4b3e15532db..c6f50a257565 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -68,15 +68,15 @@ void dbg_interrupt(u32 intmask) * * This function returns number of registers read */ -static size_t hw_register_read(struct ci13xxx *udc, u32 *buf, size_t size) +static size_t hw_register_read(struct ci13xxx *ci, u32 *buf, size_t size) { unsigned i; - if (size > udc->hw_bank.size) - size = udc->hw_bank.size; + if (size > ci->hw_bank.size) + size = ci->hw_bank.size; for (i = 0; i < size; i++) - buf[i] = hw_read(udc, i * sizeof(u32), ~0); + buf[i] = hw_read(ci, i * sizeof(u32), ~0); return size; } @@ -88,18 +88,18 @@ static size_t hw_register_read(struct ci13xxx *udc, u32 *buf, size_t size) * * This function returns an error code */ -static int hw_register_write(struct ci13xxx *udc, u16 addr, u32 data) +static int hw_register_write(struct ci13xxx *ci, u16 addr, u32 data) { /* align */ addr /= sizeof(u32); - if (addr >= udc->hw_bank.size) + if (addr >= ci->hw_bank.size) return -EINVAL; /* align */ addr *= sizeof(u32); - hw_write(udc, addr, ~0, data); + hw_write(ci, addr, ~0, data); return 0; } @@ -110,13 +110,13 @@ static int hw_register_write(struct ci13xxx *udc, u16 addr, u32 data) * * This function returns an error code */ -static int hw_intr_clear(struct ci13xxx *udc, int n) +static int hw_intr_clear(struct ci13xxx *ci, int n) { if (n >= REG_BITS) return -EINVAL; - hw_write(udc, OP_USBINTR, BIT(n), 0); - hw_write(udc, OP_USBSTS, BIT(n), BIT(n)); + hw_write(ci, OP_USBINTR, BIT(n), 0); + hw_write(ci, OP_USBSTS, BIT(n), BIT(n)); return 0; } @@ -127,15 +127,15 @@ static int hw_intr_clear(struct ci13xxx *udc, int n) * * This function returns an error code */ -static int hw_intr_force(struct ci13xxx *udc, int n) +static int hw_intr_force(struct ci13xxx *ci, int n) { if (n >= REG_BITS) return -EINVAL; - hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE); - hw_write(udc, OP_USBINTR, BIT(n), BIT(n)); - hw_write(udc, OP_USBSTS, BIT(n), BIT(n)); - hw_write(udc, CAP_TESTMODE, TESTMODE_FORCE, 0); + hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE); + hw_write(ci, OP_USBINTR, BIT(n), BIT(n)); + hw_write(ci, OP_USBSTS, BIT(n), BIT(n)); + hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, 0); return 0; } @@ -147,12 +147,12 @@ static int hw_intr_force(struct ci13xxx *udc, int n) static ssize_t show_device(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - struct usb_gadget *gadget = &udc->gadget; + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); + struct usb_gadget *gadget = &ci->gadget; int n = 0; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); return 0; } @@ -188,8 +188,8 @@ static DEVICE_ATTR(device, S_IRUSR, show_device, NULL); static ssize_t show_driver(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); - struct usb_gadget_driver *driver = udc->driver; + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); + struct usb_gadget_driver *driver = ci->driver; int n = 0; if (attr == NULL || buf == NULL) { @@ -412,22 +412,22 @@ static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events); static ssize_t show_inters(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; u32 intr; unsigned i, j, n = 0; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); return 0; } - spin_lock_irqsave(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); /*n += scnprintf(buf + n, PAGE_SIZE - n, - "status = %08x\n", hw_read_intr_status(udc)); + "status = %08x\n", hw_read_intr_status(ci)); n += scnprintf(buf + n, PAGE_SIZE - n, - "enable = %08x\n", hw_read_intr_enable(udc));*/ + "enable = %08x\n", hw_read_intr_enable(ci));*/ n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n", isr_statistics.test); @@ -471,7 +471,7 @@ static ssize_t show_inters(struct device *dev, struct device_attribute *attr, n += scnprintf(buf + n, PAGE_SIZE - n, "\n"); } - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return n; } @@ -485,31 +485,31 @@ static ssize_t show_inters(struct device *dev, struct device_attribute *attr, static ssize_t store_inters(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; unsigned en, bit; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "EINVAL\n"); + dev_err(ci->dev, "EINVAL\n"); goto done; } if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) { - dev_err(udc->dev, "<1|0> : enable|disable interrupt\n"); + dev_err(ci->dev, "<1|0> : enable|disable interrupt\n"); goto done; } - spin_lock_irqsave(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); if (en) { - if (hw_intr_force(udc, bit)) + if (hw_intr_force(ci, bit)) dev_err(dev, "invalid bit number\n"); else isr_statistics.test++; } else { - if (hw_intr_clear(udc, bit)) + if (hw_intr_clear(ci, bit)) dev_err(dev, "invalid bit number\n"); } - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); done: return count; @@ -524,18 +524,18 @@ static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters); static ssize_t show_port_test(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; unsigned mode; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "EINVAL\n"); + dev_err(ci->dev, "EINVAL\n"); return 0; } - spin_lock_irqsave(&udc->lock, flags); - mode = hw_port_test_get(udc); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + mode = hw_port_test_get(ci); + spin_unlock_irqrestore(&ci->lock, flags); return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode); } @@ -549,24 +549,24 @@ static ssize_t store_port_test(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; unsigned mode; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); goto done; } if (sscanf(buf, "%u", &mode) != 1) { - dev_err(udc->dev, ": set port test mode"); + dev_err(ci->dev, ": set port test mode"); goto done; } - spin_lock_irqsave(&udc->lock, flags); - if (hw_port_test_set(udc, mode)) - dev_err(udc->dev, "invalid mode\n"); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + if (hw_port_test_set(ci, mode)) + dev_err(ci->dev, "invalid mode\n"); + spin_unlock_irqrestore(&ci->lock, flags); done: return count; @@ -582,20 +582,20 @@ static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR, static ssize_t show_qheads(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; unsigned i, j, n = 0; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); return 0; } - spin_lock_irqsave(&udc->lock, flags); - for (i = 0; i < udc->hw_ep_max/2; i++) { - struct ci13xxx_ep *mEpRx = &udc->ci13xxx_ep[i]; + spin_lock_irqsave(&ci->lock, flags); + for (i = 0; i < ci->hw_ep_max/2; i++) { + struct ci13xxx_ep *mEpRx = &ci->ci13xxx_ep[i]; struct ci13xxx_ep *mEpTx = - &udc->ci13xxx_ep[i + udc->hw_ep_max/2]; + &ci->ci13xxx_ep[i + ci->hw_ep_max/2]; n += scnprintf(buf + n, PAGE_SIZE - n, "EP=%02i: RX=%08X TX=%08X\n", i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma); @@ -606,7 +606,7 @@ static ssize_t show_qheads(struct device *dev, struct device_attribute *attr, *((u32 *)mEpTx->qh.ptr + j)); } } - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return n; } @@ -621,25 +621,25 @@ static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL); static ssize_t show_registers(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; u32 *dump; unsigned i, k, n = 0; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); return 0; } dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL); if (!dump) { - dev_err(udc->dev, "%s: out of memory\n", __func__); + dev_err(ci->dev, "%s: out of memory\n", __func__); return 0; } - spin_lock_irqsave(&udc->lock, flags); - k = hw_register_read(udc, dump, DUMP_ENTRIES); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + k = hw_register_read(ci, dump, DUMP_ENTRIES); + spin_unlock_irqrestore(&ci->lock, flags); for (i = 0; i < k; i++) { n += scnprintf(buf + n, PAGE_SIZE - n, @@ -660,24 +660,24 @@ static ssize_t store_registers(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long addr, data, flags; if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); goto done; } if (sscanf(buf, "%li %li", &addr, &data) != 2) { - dev_err(udc->dev, + dev_err(ci->dev, " : write data to register address\n"); goto done; } - spin_lock_irqsave(&udc->lock, flags); - if (hw_register_write(udc, addr, data)) - dev_err(udc->dev, "invalid address range\n"); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + if (hw_register_write(ci, addr, data)) + dev_err(ci->dev, "invalid address range\n"); + spin_unlock_irqrestore(&ci->lock, flags); done: return count; @@ -693,34 +693,34 @@ static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR, static ssize_t show_requests(struct device *dev, struct device_attribute *attr, char *buf) { - struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev); unsigned long flags; struct list_head *ptr = NULL; struct ci13xxx_req *req = NULL; unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32); if (attr == NULL || buf == NULL) { - dev_err(udc->dev, "[%s] EINVAL\n", __func__); + dev_err(ci->dev, "[%s] EINVAL\n", __func__); return 0; } - spin_lock_irqsave(&udc->lock, flags); - for (i = 0; i < udc->hw_ep_max; i++) - list_for_each(ptr, &udc->ci13xxx_ep[i].qh.queue) + spin_lock_irqsave(&ci->lock, flags); + for (i = 0; i < ci->hw_ep_max; i++) + list_for_each(ptr, &ci->ci13xxx_ep[i].qh.queue) { req = list_entry(ptr, struct ci13xxx_req, queue); n += scnprintf(buf + n, PAGE_SIZE - n, "EP=%02i: TD=%08X %s\n", - i % udc->hw_ep_max/2, (u32)req->dma, - ((i < udc->hw_ep_max/2) ? "RX" : "TX")); + i % ci->hw_ep_max/2, (u32)req->dma, + ((i < ci->hw_ep_max/2) ? "RX" : "TX")); for (j = 0; j < qSize; j++) n += scnprintf(buf + n, PAGE_SIZE - n, " %04X: %08X\n", j, *((u32 *)req->ptr + j)); } - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return n; } diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 3094c85dc0b5..ba8284e2a237 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -67,11 +67,11 @@ static inline int hw_ep_bit(int num, int dir) return num + (dir ? 16 : 0); } -static inline int ep_to_bit(struct ci13xxx *udc, int n) +static inline int ep_to_bit(struct ci13xxx *ci, int n) { - int fill = 16 - udc->hw_ep_max / 2; + int fill = 16 - ci->hw_ep_max / 2; - if (n >= udc->hw_ep_max / 2) + if (n >= ci->hw_ep_max / 2) n += fill; return n; @@ -84,17 +84,17 @@ static inline int ep_to_bit(struct ci13xxx *udc, int n) * * This function returns an error code */ -static int hw_device_state(struct ci13xxx *udc, u32 dma) +static int hw_device_state(struct ci13xxx *ci, u32 dma) { if (dma) { - hw_write(udc, OP_ENDPTLISTADDR, ~0, dma); + hw_write(ci, OP_ENDPTLISTADDR, ~0, dma); /* interrupt, error, port change, reset, sleep/suspend */ - hw_write(udc, OP_USBINTR, ~0, + hw_write(ci, OP_USBINTR, ~0, USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); - hw_write(udc, OP_USBCMD, USBCMD_RS, USBCMD_RS); + hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); } else { - hw_write(udc, OP_USBCMD, USBCMD_RS, 0); - hw_write(udc, OP_USBINTR, ~0, 0); + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); + hw_write(ci, OP_USBINTR, ~0, 0); } return 0; } @@ -106,16 +106,16 @@ static int hw_device_state(struct ci13xxx *udc, u32 dma) * * This function returns an error code */ -static int hw_ep_flush(struct ci13xxx *udc, int num, int dir) +static int hw_ep_flush(struct ci13xxx *ci, int num, int dir) { int n = hw_ep_bit(num, dir); do { /* flush any pending transfer */ - hw_write(udc, OP_ENDPTFLUSH, BIT(n), BIT(n)); - while (hw_read(udc, OP_ENDPTFLUSH, BIT(n))) + hw_write(ci, OP_ENDPTFLUSH, BIT(n), BIT(n)); + while (hw_read(ci, OP_ENDPTFLUSH, BIT(n))) cpu_relax(); - } while (hw_read(udc, OP_ENDPTSTAT, BIT(n))); + } while (hw_read(ci, OP_ENDPTSTAT, BIT(n))); return 0; } @@ -127,10 +127,10 @@ static int hw_ep_flush(struct ci13xxx *udc, int num, int dir) * * This function returns an error code */ -static int hw_ep_disable(struct ci13xxx *udc, int num, int dir) +static int hw_ep_disable(struct ci13xxx *ci, int num, int dir) { - hw_ep_flush(udc, num, dir); - hw_write(udc, OP_ENDPTCTRL + num, + hw_ep_flush(ci, num, dir); + hw_write(ci, OP_ENDPTCTRL + num, dir ? ENDPTCTRL_TXE : ENDPTCTRL_RXE, 0); return 0; } @@ -143,7 +143,7 @@ static int hw_ep_disable(struct ci13xxx *udc, int num, int dir) * * This function returns an error code */ -static int hw_ep_enable(struct ci13xxx *udc, int num, int dir, int type) +static int hw_ep_enable(struct ci13xxx *ci, int num, int dir, int type) { u32 mask, data; @@ -166,7 +166,7 @@ static int hw_ep_enable(struct ci13xxx *udc, int num, int dir, int type) mask |= ENDPTCTRL_RXE; /* enable */ data |= ENDPTCTRL_RXE; } - hw_write(udc, OP_ENDPTCTRL + num, mask, data); + hw_write(ci, OP_ENDPTCTRL + num, mask, data); return 0; } @@ -177,11 +177,11 @@ static int hw_ep_enable(struct ci13xxx *udc, int num, int dir, int type) * * This function returns 1 if endpoint halted */ -static int hw_ep_get_halt(struct ci13xxx *udc, int num, int dir) +static int hw_ep_get_halt(struct ci13xxx *ci, int num, int dir) { u32 mask = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS; - return hw_read(udc, OP_ENDPTCTRL + num, mask) ? 1 : 0; + return hw_read(ci, OP_ENDPTCTRL + num, mask) ? 1 : 0; } /** @@ -191,10 +191,10 @@ static int hw_ep_get_halt(struct ci13xxx *udc, int num, int dir) * * This function returns setup status */ -static int hw_test_and_clear_setup_status(struct ci13xxx *udc, int n) +static int hw_test_and_clear_setup_status(struct ci13xxx *ci, int n) { - n = ep_to_bit(udc, n); - return hw_test_and_clear(udc, OP_ENDPTSETUPSTAT, BIT(n)); + n = ep_to_bit(ci, n); + return hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(n)); } /** @@ -205,18 +205,18 @@ static int hw_test_and_clear_setup_status(struct ci13xxx *udc, int n) * * This function returns an error code */ -static int hw_ep_prime(struct ci13xxx *udc, int num, int dir, int is_ctrl) +static int hw_ep_prime(struct ci13xxx *ci, int num, int dir, int is_ctrl) { int n = hw_ep_bit(num, dir); - if (is_ctrl && dir == RX && hw_read(udc, OP_ENDPTSETUPSTAT, BIT(num))) + if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num))) return -EAGAIN; - hw_write(udc, OP_ENDPTPRIME, BIT(n), BIT(n)); + hw_write(ci, OP_ENDPTPRIME, BIT(n), BIT(n)); - while (hw_read(udc, OP_ENDPTPRIME, BIT(n))) + while (hw_read(ci, OP_ENDPTPRIME, BIT(n))) cpu_relax(); - if (is_ctrl && dir == RX && hw_read(udc, OP_ENDPTSETUPSTAT, BIT(num))) + if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num))) return -EAGAIN; /* status shoult be tested according with manual but it doesn't work */ @@ -232,7 +232,7 @@ static int hw_ep_prime(struct ci13xxx *udc, int num, int dir, int is_ctrl) * * This function returns an error code */ -static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value) +static int hw_ep_set_halt(struct ci13xxx *ci, int num, int dir, int value) { if (value != 0 && value != 1) return -EINVAL; @@ -243,9 +243,9 @@ static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value) u32 mask_xr = dir ? ENDPTCTRL_TXR : ENDPTCTRL_RXR; /* data toggle - reserved for EP0 but it's in ESS */ - hw_write(udc, reg, mask_xs|mask_xr, + hw_write(ci, reg, mask_xs|mask_xr, value ? mask_xs : mask_xr); - } while (value != hw_ep_get_halt(udc, num, dir)); + } while (value != hw_ep_get_halt(ci, num, dir)); return 0; } @@ -255,10 +255,10 @@ static int hw_ep_set_halt(struct ci13xxx *udc, int num, int dir, int value) * * This function returns true if high speed port */ -static int hw_port_is_high_speed(struct ci13xxx *udc) +static int hw_port_is_high_speed(struct ci13xxx *ci) { - return udc->hw_bank.lpm ? hw_read(udc, OP_DEVLC, DEVLC_PSPD) : - hw_read(udc, OP_PORTSC, PORTSC_HSP); + return ci->hw_bank.lpm ? hw_read(ci, OP_DEVLC, DEVLC_PSPD) : + hw_read(ci, OP_PORTSC, PORTSC_HSP); } /** @@ -266,9 +266,9 @@ static int hw_port_is_high_speed(struct ci13xxx *udc) * * This function returns register data */ -static u32 hw_read_intr_enable(struct ci13xxx *udc) +static u32 hw_read_intr_enable(struct ci13xxx *ci) { - return hw_read(udc, OP_USBINTR, ~0); + return hw_read(ci, OP_USBINTR, ~0); } /** @@ -276,9 +276,9 @@ static u32 hw_read_intr_enable(struct ci13xxx *udc) * * This function returns register data */ -static u32 hw_read_intr_status(struct ci13xxx *udc) +static u32 hw_read_intr_status(struct ci13xxx *ci) { - return hw_read(udc, OP_USBSTS, ~0); + return hw_read(ci, OP_USBSTS, ~0); } /** @@ -288,10 +288,10 @@ static u32 hw_read_intr_status(struct ci13xxx *udc) * * This function returns complete status */ -static int hw_test_and_clear_complete(struct ci13xxx *udc, int n) +static int hw_test_and_clear_complete(struct ci13xxx *ci, int n) { - n = ep_to_bit(udc, n); - return hw_test_and_clear(udc, OP_ENDPTCOMPLETE, BIT(n)); + n = ep_to_bit(ci, n); + return hw_test_and_clear(ci, OP_ENDPTCOMPLETE, BIT(n)); } /** @@ -300,11 +300,11 @@ static int hw_test_and_clear_complete(struct ci13xxx *udc, int n) * * This function returns active interrutps */ -static u32 hw_test_and_clear_intr_active(struct ci13xxx *udc) +static u32 hw_test_and_clear_intr_active(struct ci13xxx *ci) { - u32 reg = hw_read_intr_status(udc) & hw_read_intr_enable(udc); + u32 reg = hw_read_intr_status(ci) & hw_read_intr_enable(ci); - hw_write(udc, OP_USBSTS, ~0, reg); + hw_write(ci, OP_USBSTS, ~0, reg); return reg; } @@ -314,9 +314,9 @@ static u32 hw_test_and_clear_intr_active(struct ci13xxx *udc) * * This function returns guard value */ -static int hw_test_and_clear_setup_guard(struct ci13xxx *udc) +static int hw_test_and_clear_setup_guard(struct ci13xxx *ci) { - return hw_test_and_write(udc, OP_USBCMD, USBCMD_SUTW, 0); + return hw_test_and_write(ci, OP_USBCMD, USBCMD_SUTW, 0); } /** @@ -325,9 +325,9 @@ static int hw_test_and_clear_setup_guard(struct ci13xxx *udc) * * This function returns guard value */ -static int hw_test_and_set_setup_guard(struct ci13xxx *udc) +static int hw_test_and_set_setup_guard(struct ci13xxx *ci) { - return hw_test_and_write(udc, OP_USBCMD, USBCMD_SUTW, USBCMD_SUTW); + return hw_test_and_write(ci, OP_USBCMD, USBCMD_SUTW, USBCMD_SUTW); } /** @@ -337,9 +337,9 @@ static int hw_test_and_set_setup_guard(struct ci13xxx *udc) * This function explicitly sets the address, without the "USBADRA" (advance) * feature, which is not supported by older versions of the controller. */ -static void hw_usb_set_address(struct ci13xxx *udc, u8 value) +static void hw_usb_set_address(struct ci13xxx *ci, u8 value) { - hw_write(udc, OP_DEVICEADDR, DEVICEADDR_USBADR, + hw_write(ci, OP_DEVICEADDR, DEVICEADDR_USBADR, value << ffs_nr(DEVICEADDR_USBADR)); } @@ -349,21 +349,21 @@ static void hw_usb_set_address(struct ci13xxx *udc, u8 value) * * This function returns an error code */ -static int hw_usb_reset(struct ci13xxx *udc) +static int hw_usb_reset(struct ci13xxx *ci) { - hw_usb_set_address(udc, 0); + hw_usb_set_address(ci, 0); /* ESS flushes only at end?!? */ - hw_write(udc, OP_ENDPTFLUSH, ~0, ~0); + hw_write(ci, OP_ENDPTFLUSH, ~0, ~0); /* clear setup token semaphores */ - hw_write(udc, OP_ENDPTSETUPSTAT, 0, 0); + hw_write(ci, OP_ENDPTSETUPSTAT, 0, 0); /* clear complete status */ - hw_write(udc, OP_ENDPTCOMPLETE, 0, 0); + hw_write(ci, OP_ENDPTCOMPLETE, 0, 0); /* wait until all bits cleared */ - while (hw_read(udc, OP_ENDPTPRIME, ~0)) + while (hw_read(ci, OP_ENDPTPRIME, ~0)) udelay(10); /* not RTOS friendly */ /* reset all endpoints ? */ @@ -395,7 +395,7 @@ static inline u8 _usb_addr(struct ci13xxx_ep *ep) */ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) { - struct ci13xxx *udc = mEp->udc; + struct ci13xxx *ci = mEp->ci; unsigned i; int ret = 0; unsigned length = mReq->req.length; @@ -418,7 +418,7 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) if (!mReq->req.no_interrupt) mReq->zptr->token |= TD_IOC; } - ret = usb_gadget_map_request(&udc->gadget, &mReq->req, mEp->dir); + ret = usb_gadget_map_request(&ci->gadget, &mReq->req, mEp->dir); if (ret) return ret; @@ -454,13 +454,13 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) else mReqPrev->ptr->next = mReq->dma & TD_ADDR_MASK; wmb(); - if (hw_read(udc, OP_ENDPTPRIME, BIT(n))) + if (hw_read(ci, OP_ENDPTPRIME, BIT(n))) goto done; do { - hw_write(udc, OP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW); - tmp_stat = hw_read(udc, OP_ENDPTSTAT, BIT(n)); - } while (!hw_read(udc, OP_USBCMD, USBCMD_ATDTW)); - hw_write(udc, OP_USBCMD, USBCMD_ATDTW, 0); + hw_write(ci, OP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW); + tmp_stat = hw_read(ci, OP_ENDPTSTAT, BIT(n)); + } while (!hw_read(ci, OP_USBCMD, USBCMD_ATDTW)); + hw_write(ci, OP_USBCMD, USBCMD_ATDTW, 0); if (tmp_stat) goto done; } @@ -472,7 +472,7 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) wmb(); /* synchronize before ep prime */ - ret = hw_ep_prime(udc, mEp->num, mEp->dir, + ret = hw_ep_prime(ci, mEp->num, mEp->dir, mEp->type == USB_ENDPOINT_XFER_CONTROL); done: return ret; @@ -502,7 +502,7 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) mReq->req.status = 0; - usb_gadget_unmap_request(&mEp->udc->gadget, &mReq->req, mEp->dir); + usb_gadget_unmap_request(&mEp->ci->gadget, &mReq->req, mEp->dir); mReq->req.status = mReq->ptr->token & TD_STATUS; if ((TD_STATUS_HALTED & mReq->req.status) != 0) @@ -534,7 +534,7 @@ __acquires(mEp->lock) if (mEp == NULL) return -EINVAL; - hw_ep_flush(mEp->udc, mEp->num, mEp->dir); + hw_ep_flush(mEp->ci, mEp->num, mEp->dir); while (!list_empty(&mEp->qh.queue)) { @@ -563,33 +563,33 @@ __acquires(mEp->lock) static int _gadget_stop_activity(struct usb_gadget *gadget) { struct usb_ep *ep; - struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(gadget, struct ci13xxx, gadget); unsigned long flags; - spin_lock_irqsave(&udc->lock, flags); - udc->gadget.speed = USB_SPEED_UNKNOWN; - udc->remote_wakeup = 0; - udc->suspended = 0; - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + ci->gadget.speed = USB_SPEED_UNKNOWN; + ci->remote_wakeup = 0; + ci->suspended = 0; + spin_unlock_irqrestore(&ci->lock, flags); /* flush all endpoints */ gadget_for_each_ep(ep, gadget) { usb_ep_fifo_flush(ep); } - usb_ep_fifo_flush(&udc->ep0out->ep); - usb_ep_fifo_flush(&udc->ep0in->ep); + usb_ep_fifo_flush(&ci->ep0out->ep); + usb_ep_fifo_flush(&ci->ep0in->ep); - if (udc->driver) - udc->driver->disconnect(gadget); + if (ci->driver) + ci->driver->disconnect(gadget); /* make sure to disable all endpoints */ gadget_for_each_ep(ep, gadget) { usb_ep_disable(ep); } - if (udc->status != NULL) { - usb_ep_free_request(&udc->ep0in->ep, udc->status); - udc->status = NULL; + if (ci->status != NULL) { + usb_ep_free_request(&ci->ep0in->ep, ci->status); + ci->status = NULL; } return 0; @@ -600,36 +600,36 @@ static int _gadget_stop_activity(struct usb_gadget *gadget) *****************************************************************************/ /** * isr_reset_handler: USB reset interrupt handler - * @udc: UDC device + * @ci: UDC device * * This function resets USB engine after a bus reset occurred */ -static void isr_reset_handler(struct ci13xxx *udc) -__releases(udc->lock) -__acquires(udc->lock) +static void isr_reset_handler(struct ci13xxx *ci) +__releases(ci->lock) +__acquires(ci->lock) { int retval; dbg_event(0xFF, "BUS RST", 0); - spin_unlock(&udc->lock); - retval = _gadget_stop_activity(&udc->gadget); + spin_unlock(&ci->lock); + retval = _gadget_stop_activity(&ci->gadget); if (retval) goto done; - retval = hw_usb_reset(udc); + retval = hw_usb_reset(ci); if (retval) goto done; - udc->status = usb_ep_alloc_request(&udc->ep0in->ep, GFP_ATOMIC); - if (udc->status == NULL) + ci->status = usb_ep_alloc_request(&ci->ep0in->ep, GFP_ATOMIC); + if (ci->status == NULL) retval = -ENOMEM; done: - spin_lock(&udc->lock); + spin_lock(&ci->lock); if (retval) - dev_err(udc->dev, "error: %i\n", retval); + dev_err(ci->dev, "error: %i\n", retval); } /** @@ -650,17 +650,17 @@ static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req) /** * isr_get_status_response: get_status request response - * @udc: udc struct + * @ci: ci struct * @setup: setup request packet * * This function returns an error code */ -static int isr_get_status_response(struct ci13xxx *udc, +static int isr_get_status_response(struct ci13xxx *ci, struct usb_ctrlrequest *setup) __releases(mEp->lock) __acquires(mEp->lock) { - struct ci13xxx_ep *mEp = udc->ep0in; + struct ci13xxx_ep *mEp = ci->ep0in; struct usb_request *req = NULL; gfp_t gfp_flags = GFP_ATOMIC; int dir, num, retval; @@ -684,14 +684,14 @@ __acquires(mEp->lock) if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { /* Assume that device is bus powered for now. */ - *(u16 *)req->buf = udc->remote_wakeup << 1; + *(u16 *)req->buf = ci->remote_wakeup << 1; retval = 0; } else if ((setup->bRequestType & USB_RECIP_MASK) \ == USB_RECIP_ENDPOINT) { dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ? TX : RX; num = le16_to_cpu(setup->wIndex) & USB_ENDPOINT_NUMBER_MASK; - *(u16 *)req->buf = hw_ep_get_halt(udc, num, dir); + *(u16 *)req->buf = hw_ep_get_halt(ci, num, dir); } /* else do nothing; reserved for future use */ @@ -723,39 +723,39 @@ __acquires(mEp->lock) static void isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req) { - struct ci13xxx *udc = req->context; + struct ci13xxx *ci = req->context; unsigned long flags; - if (udc->setaddr) { - hw_usb_set_address(udc, udc->address); - udc->setaddr = false; + if (ci->setaddr) { + hw_usb_set_address(ci, ci->address); + ci->setaddr = false; } - spin_lock_irqsave(&udc->lock, flags); - if (udc->test_mode) - hw_port_test_set(udc, udc->test_mode); - spin_unlock_irqrestore(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); + if (ci->test_mode) + hw_port_test_set(ci, ci->test_mode); + spin_unlock_irqrestore(&ci->lock, flags); } /** * isr_setup_status_phase: queues the status phase of a setup transation - * @udc: udc struct + * @ci: ci struct * * This function returns an error code */ -static int isr_setup_status_phase(struct ci13xxx *udc) +static int isr_setup_status_phase(struct ci13xxx *ci) __releases(mEp->lock) __acquires(mEp->lock) { int retval; struct ci13xxx_ep *mEp; - mEp = (udc->ep0_dir == TX) ? udc->ep0out : udc->ep0in; - udc->status->context = udc; - udc->status->complete = isr_setup_status_complete; + mEp = (ci->ep0_dir == TX) ? ci->ep0out : ci->ep0in; + ci->status->context = ci; + ci->status->complete = isr_setup_status_complete; spin_unlock(mEp->lock); - retval = usb_ep_queue(&mEp->ep, udc->status, GFP_ATOMIC); + retval = usb_ep_queue(&mEp->ep, ci->status, GFP_ATOMIC); spin_lock(mEp->lock); return retval; @@ -790,7 +790,7 @@ __acquires(mEp->lock) spin_unlock(mEp->lock); if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) && mReq->req.length) - mEpTemp = mEp->udc->ep0in; + mEpTemp = mEp->ci->ep0in; mReq->req.complete(&mEpTemp->ep, &mReq->req); spin_lock(mEp->lock); } @@ -806,48 +806,48 @@ __acquires(mEp->lock) /** * isr_tr_complete_handler: transaction complete interrupt handler - * @udc: UDC descriptor + * @ci: UDC descriptor * * This function handles traffic events */ -static void isr_tr_complete_handler(struct ci13xxx *udc) -__releases(udc->lock) -__acquires(udc->lock) +static void isr_tr_complete_handler(struct ci13xxx *ci) +__releases(ci->lock) +__acquires(ci->lock) { unsigned i; u8 tmode = 0; - for (i = 0; i < udc->hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + for (i = 0; i < ci->hw_ep_max; i++) { + struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[i]; int type, num, dir, err = -EINVAL; struct usb_ctrlrequest req; if (mEp->ep.desc == NULL) continue; /* not configured */ - if (hw_test_and_clear_complete(udc, i)) { + if (hw_test_and_clear_complete(ci, i)) { err = isr_tr_complete_low(mEp); if (mEp->type == USB_ENDPOINT_XFER_CONTROL) { if (err > 0) /* needs status phase */ - err = isr_setup_status_phase(udc); + err = isr_setup_status_phase(ci); if (err < 0) { dbg_event(_usb_addr(mEp), "ERROR", err); - spin_unlock(&udc->lock); + spin_unlock(&ci->lock); if (usb_ep_set_halt(&mEp->ep)) - dev_err(udc->dev, + dev_err(ci->dev, "error: ep_set_halt\n"); - spin_lock(&udc->lock); + spin_lock(&ci->lock); } } } if (mEp->type != USB_ENDPOINT_XFER_CONTROL || - !hw_test_and_clear_setup_status(udc, i)) + !hw_test_and_clear_setup_status(ci, i)) continue; if (i != 0) { - dev_warn(udc->dev, "ctrl traffic at endpoint %d\n", i); + dev_warn(ci->dev, "ctrl traffic at endpoint %d\n", i); continue; } @@ -855,18 +855,18 @@ __acquires(udc->lock) * Flush data and handshake transactions of previous * setup packet. */ - _ep_nuke(udc->ep0out); - _ep_nuke(udc->ep0in); + _ep_nuke(ci->ep0out); + _ep_nuke(ci->ep0in); /* read_setup_packet */ do { - hw_test_and_set_setup_guard(udc); + hw_test_and_set_setup_guard(ci); memcpy(&req, &mEp->qh.ptr->setup, sizeof(req)); - } while (!hw_test_and_clear_setup_guard(udc)); + } while (!hw_test_and_clear_setup_guard(ci)); type = req.bRequestType; - udc->ep0_dir = (type & USB_DIR_IN) ? TX : RX; + ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX; dbg_setup(_usb_addr(mEp), &req); @@ -881,23 +881,23 @@ __acquires(udc->lock) dir = num & USB_ENDPOINT_DIR_MASK; num &= USB_ENDPOINT_NUMBER_MASK; if (dir) /* TX */ - num += udc->hw_ep_max/2; - if (!udc->ci13xxx_ep[num].wedge) { - spin_unlock(&udc->lock); + num += ci->hw_ep_max/2; + if (!ci->ci13xxx_ep[num].wedge) { + spin_unlock(&ci->lock); err = usb_ep_clear_halt( - &udc->ci13xxx_ep[num].ep); - spin_lock(&udc->lock); + &ci->ci13xxx_ep[num].ep); + spin_lock(&ci->lock); if (err) break; } - err = isr_setup_status_phase(udc); + err = isr_setup_status_phase(ci); } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) && le16_to_cpu(req.wValue) == USB_DEVICE_REMOTE_WAKEUP) { if (req.wLength != 0) break; - udc->remote_wakeup = 0; - err = isr_setup_status_phase(udc); + ci->remote_wakeup = 0; + err = isr_setup_status_phase(ci); } else { goto delegate; } @@ -910,7 +910,7 @@ __acquires(udc->lock) if (le16_to_cpu(req.wLength) != 2 || le16_to_cpu(req.wValue) != 0) break; - err = isr_get_status_response(udc, &req); + err = isr_get_status_response(ci, &req); break; case USB_REQ_SET_ADDRESS: if (type != (USB_DIR_OUT|USB_RECIP_DEVICE)) @@ -918,9 +918,9 @@ __acquires(udc->lock) if (le16_to_cpu(req.wLength) != 0 || le16_to_cpu(req.wIndex) != 0) break; - udc->address = (u8)le16_to_cpu(req.wValue); - udc->setaddr = true; - err = isr_setup_status_phase(udc); + ci->address = (u8)le16_to_cpu(req.wValue); + ci->setaddr = true; + err = isr_setup_status_phase(ci); break; case USB_REQ_SET_FEATURE: if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) && @@ -932,20 +932,20 @@ __acquires(udc->lock) dir = num & USB_ENDPOINT_DIR_MASK; num &= USB_ENDPOINT_NUMBER_MASK; if (dir) /* TX */ - num += udc->hw_ep_max/2; + num += ci->hw_ep_max/2; - spin_unlock(&udc->lock); - err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep); - spin_lock(&udc->lock); + spin_unlock(&ci->lock); + err = usb_ep_set_halt(&ci->ci13xxx_ep[num].ep); + spin_lock(&ci->lock); if (!err) - isr_setup_status_phase(udc); + isr_setup_status_phase(ci); } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) { if (req.wLength != 0) break; switch (le16_to_cpu(req.wValue)) { case USB_DEVICE_REMOTE_WAKEUP: - udc->remote_wakeup = 1; - err = isr_setup_status_phase(udc); + ci->remote_wakeup = 1; + err = isr_setup_status_phase(ci); break; case USB_DEVICE_TEST_MODE: tmode = le16_to_cpu(req.wIndex) >> 8; @@ -955,9 +955,9 @@ __acquires(udc->lock) case TEST_SE0_NAK: case TEST_PACKET: case TEST_FORCE_EN: - udc->test_mode = tmode; + ci->test_mode = tmode; err = isr_setup_status_phase( - udc); + ci); break; default: break; @@ -972,21 +972,21 @@ __acquires(udc->lock) default: delegate: if (req.wLength == 0) /* no data phase */ - udc->ep0_dir = TX; + ci->ep0_dir = TX; - spin_unlock(&udc->lock); - err = udc->driver->setup(&udc->gadget, &req); - spin_lock(&udc->lock); + spin_unlock(&ci->lock); + err = ci->driver->setup(&ci->gadget, &req); + spin_lock(&ci->lock); break; } if (err < 0) { dbg_event(_usb_addr(mEp), "ERROR", err); - spin_unlock(&udc->lock); + spin_unlock(&ci->lock); if (usb_ep_set_halt(&mEp->ep)) - dev_err(udc->dev, "error: ep_set_halt\n"); - spin_lock(&udc->lock); + dev_err(ci->dev, "error: ep_set_halt\n"); + spin_lock(&ci->lock); } } } @@ -1016,7 +1016,7 @@ static int ep_enable(struct usb_ep *ep, mEp->ep.desc = desc; if (!list_empty(&mEp->qh.queue)) - dev_warn(mEp->udc->dev, "enabling a non-empty endpoint!\n"); + dev_warn(mEp->ci->dev, "enabling a non-empty endpoint!\n"); mEp->dir = usb_endpoint_dir_in(desc) ? TX : RX; mEp->num = usb_endpoint_num(desc); @@ -1044,7 +1044,7 @@ static int ep_enable(struct usb_ep *ep, * is always enabled */ if (mEp->num) - retval |= hw_ep_enable(mEp->udc, mEp->num, mEp->dir, mEp->type); + retval |= hw_ep_enable(mEp->ci, mEp->num, mEp->dir, mEp->type); spin_unlock_irqrestore(mEp->lock, flags); return retval; @@ -1075,7 +1075,7 @@ static int ep_disable(struct usb_ep *ep) dbg_event(_usb_addr(mEp), "DISABLE", 0); retval |= _ep_nuke(mEp); - retval |= hw_ep_disable(mEp->udc, mEp->num, mEp->dir); + retval |= hw_ep_disable(mEp->ci, mEp->num, mEp->dir); if (mEp->type == USB_ENDPOINT_XFER_CONTROL) mEp->dir = (mEp->dir == TX) ? RX : TX; @@ -1132,7 +1132,7 @@ static void ep_free_request(struct usb_ep *ep, struct usb_request *req) if (ep == NULL || req == NULL) { return; } else if (!list_empty(&mReq->queue)) { - dev_err(mEp->udc->dev, "freeing queued request\n"); + dev_err(mEp->ci->dev, "freeing queued request\n"); return; } @@ -1157,7 +1157,7 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, { struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); - struct ci13xxx *udc = mEp->udc; + struct ci13xxx *ci = mEp->ci; int retval = 0; unsigned long flags; @@ -1168,12 +1168,12 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, if (mEp->type == USB_ENDPOINT_XFER_CONTROL) { if (req->length) - mEp = (udc->ep0_dir == RX) ? - udc->ep0out : udc->ep0in; + mEp = (ci->ep0_dir == RX) ? + ci->ep0out : ci->ep0in; if (!list_empty(&mEp->qh.queue)) { _ep_nuke(mEp); retval = -EOVERFLOW; - dev_warn(mEp->udc->dev, "endpoint ctrl %X nuked\n", + dev_warn(mEp->ci->dev, "endpoint ctrl %X nuked\n", _usb_addr(mEp)); } } @@ -1181,14 +1181,14 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, /* first nuke then test link, e.g. previous status has not sent */ if (!list_empty(&mReq->queue)) { retval = -EBUSY; - dev_err(mEp->udc->dev, "request already in queue\n"); + dev_err(mEp->ci->dev, "request already in queue\n"); goto done; } if (req->length > 4 * CI13XXX_PAGE_SIZE) { req->length = 4 * CI13XXX_PAGE_SIZE; retval = -EMSGSIZE; - dev_warn(mEp->udc->dev, "request length truncated\n"); + dev_warn(mEp->ci->dev, "request length truncated\n"); } dbg_queue(_usb_addr(mEp), req, retval); @@ -1231,12 +1231,12 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) dbg_event(_usb_addr(mEp), "DEQUEUE", 0); - hw_ep_flush(mEp->udc, mEp->num, mEp->dir); + hw_ep_flush(mEp->ci, mEp->num, mEp->dir); /* pop request */ list_del_init(&mReq->queue); - usb_gadget_unmap_request(&mEp->udc->gadget, req, mEp->dir); + usb_gadget_unmap_request(&mEp->ci->gadget, req, mEp->dir); req->status = -ECONNRESET; @@ -1278,7 +1278,7 @@ static int ep_set_halt(struct usb_ep *ep, int value) direction = mEp->dir; do { dbg_event(_usb_addr(mEp), "HALT", value); - retval |= hw_ep_set_halt(mEp->udc, mEp->num, mEp->dir, value); + retval |= hw_ep_set_halt(mEp->ci, mEp->num, mEp->dir, value); if (!value) mEp->wedge = 0; @@ -1326,14 +1326,14 @@ static void ep_fifo_flush(struct usb_ep *ep) unsigned long flags; if (ep == NULL) { - dev_err(mEp->udc->dev, "%02X: -EINVAL\n", _usb_addr(mEp)); + dev_err(mEp->ci->dev, "%02X: -EINVAL\n", _usb_addr(mEp)); return; } spin_lock_irqsave(mEp->lock, flags); dbg_event(_usb_addr(mEp), "FFLUSH", 0); - hw_ep_flush(mEp->udc, mEp->num, mEp->dir); + hw_ep_flush(mEp->ci, mEp->num, mEp->dir); spin_unlock_irqrestore(mEp->lock, flags); } @@ -1359,30 +1359,30 @@ static const struct usb_ep_ops usb_ep_ops = { *****************************************************************************/ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) { - struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget); unsigned long flags; int gadget_ready = 0; - if (!(udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS)) + if (!(ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS)) return -EOPNOTSUPP; - spin_lock_irqsave(&udc->lock, flags); - udc->vbus_active = is_active; - if (udc->driver) + spin_lock_irqsave(&ci->lock, flags); + ci->vbus_active = is_active; + if (ci->driver) gadget_ready = 1; - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); if (gadget_ready) { if (is_active) { pm_runtime_get_sync(&_gadget->dev); - hw_device_reset(udc, USBMODE_CM_DC); - hw_device_state(udc, udc->ep0out->qh.dma); + hw_device_reset(ci, USBMODE_CM_DC); + hw_device_state(ci, ci->ep0out->qh.dma); } else { - hw_device_state(udc, 0); - if (udc->platdata->notify_event) - udc->platdata->notify_event(udc, + hw_device_state(ci, 0); + if (ci->platdata->notify_event) + ci->platdata->notify_event(ci, CI13XXX_CONTROLLER_STOPPED_EVENT); - _gadget_stop_activity(&udc->gadget); + _gadget_stop_activity(&ci->gadget); pm_runtime_put_sync(&_gadget->dev); } } @@ -1392,31 +1392,31 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active) static int ci13xxx_wakeup(struct usb_gadget *_gadget) { - struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget); unsigned long flags; int ret = 0; - spin_lock_irqsave(&udc->lock, flags); - if (!udc->remote_wakeup) { + spin_lock_irqsave(&ci->lock, flags); + if (!ci->remote_wakeup) { ret = -EOPNOTSUPP; goto out; } - if (!hw_read(udc, OP_PORTSC, PORTSC_SUSP)) { + if (!hw_read(ci, OP_PORTSC, PORTSC_SUSP)) { ret = -EINVAL; goto out; } - hw_write(udc, OP_PORTSC, PORTSC_FPR, PORTSC_FPR); + hw_write(ci, OP_PORTSC, PORTSC_FPR, PORTSC_FPR); out: - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return ret; } static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned mA) { - struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget); - if (udc->transceiver) - return usb_phy_set_power(udc->transceiver, mA); + if (ci->transceiver) + return usb_phy_set_power(ci->transceiver, mA); return -ENOTSUPP; } @@ -1437,28 +1437,28 @@ static const struct usb_gadget_ops usb_gadget_ops = { .udc_stop = ci13xxx_stop, }; -static int init_eps(struct ci13xxx *udc) +static int init_eps(struct ci13xxx *ci) { int retval = 0, i, j; - for (i = 0; i < udc->hw_ep_max/2; i++) + for (i = 0; i < ci->hw_ep_max/2; i++) for (j = RX; j <= TX; j++) { - int k = i + j * udc->hw_ep_max/2; - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[k]; + int k = i + j * ci->hw_ep_max/2; + struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[k]; scnprintf(mEp->name, sizeof(mEp->name), "ep%i%s", i, (j == TX) ? "in" : "out"); - mEp->udc = udc; - mEp->lock = &udc->lock; - mEp->td_pool = udc->td_pool; + mEp->ci = ci; + mEp->lock = &ci->lock; + mEp->td_pool = ci->td_pool; mEp->ep.name = mEp->name; mEp->ep.ops = &usb_ep_ops; mEp->ep.maxpacket = CTRL_PAYLOAD_MAX; INIT_LIST_HEAD(&mEp->qh.queue); - mEp->qh.ptr = dma_pool_alloc(udc->qh_pool, GFP_KERNEL, + mEp->qh.ptr = dma_pool_alloc(ci->qh_pool, GFP_KERNEL, &mEp->qh.dma); if (mEp->qh.ptr == NULL) retval = -ENOMEM; @@ -1471,14 +1471,14 @@ static int init_eps(struct ci13xxx *udc) */ if (i == 0) { if (j == RX) - udc->ep0out = mEp; + ci->ep0out = mEp; else - udc->ep0in = mEp; + ci->ep0in = mEp; continue; } - list_add_tail(&mEp->ep.ep_list, &udc->gadget.ep_list); + list_add_tail(&mEp->ep.ep_list, &ci->gadget.ep_list); } return retval; @@ -1494,7 +1494,7 @@ static int init_eps(struct ci13xxx *udc) static int ci13xxx_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { - struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(gadget, struct ci13xxx, gadget); unsigned long flags; int retval = -ENOMEM; @@ -1502,35 +1502,35 @@ static int ci13xxx_start(struct usb_gadget *gadget, return -EINVAL; - udc->ep0out->ep.desc = &ctrl_endpt_out_desc; - retval = usb_ep_enable(&udc->ep0out->ep); + ci->ep0out->ep.desc = &ctrl_endpt_out_desc; + retval = usb_ep_enable(&ci->ep0out->ep); if (retval) return retval; - udc->ep0in->ep.desc = &ctrl_endpt_in_desc; - retval = usb_ep_enable(&udc->ep0in->ep); + ci->ep0in->ep.desc = &ctrl_endpt_in_desc; + retval = usb_ep_enable(&ci->ep0in->ep); if (retval) return retval; - spin_lock_irqsave(&udc->lock, flags); - - udc->driver = driver; - pm_runtime_get_sync(&udc->gadget.dev); - if (udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS) { - if (udc->vbus_active) { - if (udc->platdata->flags & CI13XXX_REGS_SHARED) - hw_device_reset(udc, USBMODE_CM_DC); + spin_lock_irqsave(&ci->lock, flags); + + ci->driver = driver; + pm_runtime_get_sync(&ci->gadget.dev); + if (ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) { + if (ci->vbus_active) { + if (ci->platdata->flags & CI13XXX_REGS_SHARED) + hw_device_reset(ci, USBMODE_CM_DC); } else { - pm_runtime_put_sync(&udc->gadget.dev); + pm_runtime_put_sync(&ci->gadget.dev); goto done; } } - retval = hw_device_state(udc, udc->ep0out->qh.dma); + retval = hw_device_state(ci, ci->ep0out->qh.dma); if (retval) - pm_runtime_put_sync(&udc->gadget.dev); + pm_runtime_put_sync(&ci->gadget.dev); done: - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return retval; } @@ -1540,25 +1540,25 @@ static int ci13xxx_start(struct usb_gadget *gadget, static int ci13xxx_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { - struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); + struct ci13xxx *ci = container_of(gadget, struct ci13xxx, gadget); unsigned long flags; - spin_lock_irqsave(&udc->lock, flags); + spin_lock_irqsave(&ci->lock, flags); - if (!(udc->platdata->flags & CI13XXX_PULLUP_ON_VBUS) || - udc->vbus_active) { - hw_device_state(udc, 0); - if (udc->platdata->notify_event) - udc->platdata->notify_event(udc, + if (!(ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) || + ci->vbus_active) { + hw_device_state(ci, 0); + if (ci->platdata->notify_event) + ci->platdata->notify_event(ci, CI13XXX_CONTROLLER_STOPPED_EVENT); - udc->driver = NULL; - spin_unlock_irqrestore(&udc->lock, flags); - _gadget_stop_activity(&udc->gadget); - spin_lock_irqsave(&udc->lock, flags); - pm_runtime_put(&udc->gadget.dev); + ci->driver = NULL; + spin_unlock_irqrestore(&ci->lock, flags); + _gadget_stop_activity(&ci->gadget); + spin_lock_irqsave(&ci->lock, flags); + pm_runtime_put(&ci->gadget.dev); } - spin_unlock_irqrestore(&udc->lock, flags); + spin_unlock_irqrestore(&ci->lock, flags); return 0; } @@ -1567,64 +1567,64 @@ static int ci13xxx_stop(struct usb_gadget *gadget, * BUS block *****************************************************************************/ /** - * udc_irq: udc interrupt handler + * udc_irq: ci interrupt handler * * This function returns IRQ_HANDLED if the IRQ has been handled * It locks access to registers */ -static irqreturn_t udc_irq(struct ci13xxx *udc) +static irqreturn_t udc_irq(struct ci13xxx *ci) { irqreturn_t retval; u32 intr; - if (udc == NULL) + if (ci == NULL) return IRQ_HANDLED; - spin_lock(&udc->lock); + spin_lock(&ci->lock); - if (udc->platdata->flags & CI13XXX_REGS_SHARED) { - if (hw_read(udc, OP_USBMODE, USBMODE_CM) != + if (ci->platdata->flags & CI13XXX_REGS_SHARED) { + if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) { - spin_unlock(&udc->lock); + spin_unlock(&ci->lock); return IRQ_NONE; } } - intr = hw_test_and_clear_intr_active(udc); + intr = hw_test_and_clear_intr_active(ci); dbg_interrupt(intr); if (intr) { /* order defines priority - do NOT change it */ if (USBi_URI & intr) - isr_reset_handler(udc); + isr_reset_handler(ci); if (USBi_PCI & intr) { - udc->gadget.speed = hw_port_is_high_speed(udc) ? + ci->gadget.speed = hw_port_is_high_speed(ci) ? USB_SPEED_HIGH : USB_SPEED_FULL; - if (udc->suspended && udc->driver->resume) { - spin_unlock(&udc->lock); - udc->driver->resume(&udc->gadget); - spin_lock(&udc->lock); - udc->suspended = 0; + if (ci->suspended && ci->driver->resume) { + spin_unlock(&ci->lock); + ci->driver->resume(&ci->gadget); + spin_lock(&ci->lock); + ci->suspended = 0; } } if (USBi_UI & intr) - isr_tr_complete_handler(udc); + isr_tr_complete_handler(ci); if (USBi_SLI & intr) { - if (udc->gadget.speed != USB_SPEED_UNKNOWN && - udc->driver->suspend) { - udc->suspended = 1; - spin_unlock(&udc->lock); - udc->driver->suspend(&udc->gadget); - spin_lock(&udc->lock); + if (ci->gadget.speed != USB_SPEED_UNKNOWN && + ci->driver->suspend) { + ci->suspended = 1; + spin_unlock(&ci->lock); + ci->driver->suspend(&ci->gadget); + spin_lock(&ci->lock); } } retval = IRQ_HANDLED; } else { retval = IRQ_NONE; } - spin_unlock(&udc->lock); + spin_unlock(&ci->lock); return retval; } @@ -1641,109 +1641,109 @@ static void udc_release(struct device *dev) /** * udc_start: initialize gadget role - * @udc: chipidea controller + * @ci: chipidea controller */ -static int udc_start(struct ci13xxx *udc) +static int udc_start(struct ci13xxx *ci) { - struct device *dev = udc->dev; + struct device *dev = ci->dev; int retval = 0; - spin_lock_init(&udc->lock); + spin_lock_init(&ci->lock); - udc->gadget.ops = &usb_gadget_ops; - udc->gadget.speed = USB_SPEED_UNKNOWN; - udc->gadget.max_speed = USB_SPEED_HIGH; - udc->gadget.is_otg = 0; - udc->gadget.name = udc->platdata->name; + ci->gadget.ops = &usb_gadget_ops; + ci->gadget.speed = USB_SPEED_UNKNOWN; + ci->gadget.max_speed = USB_SPEED_HIGH; + ci->gadget.is_otg = 0; + ci->gadget.name = ci->platdata->name; - INIT_LIST_HEAD(&udc->gadget.ep_list); + INIT_LIST_HEAD(&ci->gadget.ep_list); - dev_set_name(&udc->gadget.dev, "gadget"); - udc->gadget.dev.dma_mask = dev->dma_mask; - udc->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask; - udc->gadget.dev.parent = dev; - udc->gadget.dev.release = udc_release; + dev_set_name(&ci->gadget.dev, "gadget"); + ci->gadget.dev.dma_mask = dev->dma_mask; + ci->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask; + ci->gadget.dev.parent = dev; + ci->gadget.dev.release = udc_release; /* alloc resources */ - udc->qh_pool = dma_pool_create("ci13xxx_qh", dev, + ci->qh_pool = dma_pool_create("ci13xxx_qh", dev, sizeof(struct ci13xxx_qh), 64, CI13XXX_PAGE_SIZE); - if (udc->qh_pool == NULL) + if (ci->qh_pool == NULL) return -ENOMEM; - udc->td_pool = dma_pool_create("ci13xxx_td", dev, + ci->td_pool = dma_pool_create("ci13xxx_td", dev, sizeof(struct ci13xxx_td), 64, CI13XXX_PAGE_SIZE); - if (udc->td_pool == NULL) { + if (ci->td_pool == NULL) { retval = -ENOMEM; goto free_qh_pool; } - retval = init_eps(udc); + retval = init_eps(ci); if (retval) goto free_pools; - udc->gadget.ep0 = &udc->ep0in->ep; + ci->gadget.ep0 = &ci->ep0in->ep; - udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); + ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (udc->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) { - if (udc->transceiver == NULL) { + if (ci->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) { + if (ci->transceiver == NULL) { retval = -ENODEV; goto free_pools; } } - if (!(udc->platdata->flags & CI13XXX_REGS_SHARED)) { - retval = hw_device_reset(udc, USBMODE_CM_DC); + if (!(ci->platdata->flags & CI13XXX_REGS_SHARED)) { + retval = hw_device_reset(ci, USBMODE_CM_DC); if (retval) goto put_transceiver; } - retval = device_register(&udc->gadget.dev); + retval = device_register(&ci->gadget.dev); if (retval) { - put_device(&udc->gadget.dev); + put_device(&ci->gadget.dev); goto put_transceiver; } - retval = dbg_create_files(&udc->gadget.dev); + retval = dbg_create_files(&ci->gadget.dev); if (retval) goto unreg_device; - if (!IS_ERR_OR_NULL(udc->transceiver)) { - retval = otg_set_peripheral(udc->transceiver->otg, - &udc->gadget); + if (!IS_ERR_OR_NULL(ci->transceiver)) { + retval = otg_set_peripheral(ci->transceiver->otg, + &ci->gadget); if (retval) goto remove_dbg; } - retval = usb_add_gadget_udc(dev, &udc->gadget); + retval = usb_add_gadget_udc(dev, &ci->gadget); if (retval) goto remove_trans; - pm_runtime_no_callbacks(&udc->gadget.dev); - pm_runtime_enable(&udc->gadget.dev); + pm_runtime_no_callbacks(&ci->gadget.dev); + pm_runtime_enable(&ci->gadget.dev); return retval; remove_trans: - if (!IS_ERR_OR_NULL(udc->transceiver)) { - otg_set_peripheral(udc->transceiver->otg, &udc->gadget); - usb_put_phy(udc->transceiver); + if (!IS_ERR_OR_NULL(ci->transceiver)) { + otg_set_peripheral(ci->transceiver->otg, &ci->gadget); + usb_put_phy(ci->transceiver); } dev_err(dev, "error = %i\n", retval); remove_dbg: - dbg_remove_files(&udc->gadget.dev); + dbg_remove_files(&ci->gadget.dev); unreg_device: - device_unregister(&udc->gadget.dev); + device_unregister(&ci->gadget.dev); put_transceiver: - if (!IS_ERR_OR_NULL(udc->transceiver)) - usb_put_phy(udc->transceiver); + if (!IS_ERR_OR_NULL(ci->transceiver)) + usb_put_phy(ci->transceiver); free_pools: - dma_pool_destroy(udc->td_pool); + dma_pool_destroy(ci->td_pool); free_qh_pool: - dma_pool_destroy(udc->qh_pool); + dma_pool_destroy(ci->qh_pool); return retval; } @@ -1752,32 +1752,32 @@ free_qh_pool: * * No interrupts active, the IRQ has been released */ -static void udc_stop(struct ci13xxx *udc) +static void udc_stop(struct ci13xxx *ci) { int i; - if (udc == NULL) + if (ci == NULL) return; - usb_del_gadget_udc(&udc->gadget); + usb_del_gadget_udc(&ci->gadget); - for (i = 0; i < udc->hw_ep_max; i++) { - struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + for (i = 0; i < ci->hw_ep_max; i++) { + struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[i]; - dma_pool_free(udc->qh_pool, mEp->qh.ptr, mEp->qh.dma); + dma_pool_free(ci->qh_pool, mEp->qh.ptr, mEp->qh.dma); } - dma_pool_destroy(udc->td_pool); - dma_pool_destroy(udc->qh_pool); + dma_pool_destroy(ci->td_pool); + dma_pool_destroy(ci->qh_pool); - if (!IS_ERR_OR_NULL(udc->transceiver)) { - otg_set_peripheral(udc->transceiver->otg, NULL); - usb_put_phy(udc->transceiver); + if (!IS_ERR_OR_NULL(ci->transceiver)) { + otg_set_peripheral(ci->transceiver->otg, NULL); + usb_put_phy(ci->transceiver); } - dbg_remove_files(&udc->gadget.dev); - device_unregister(&udc->gadget.dev); + dbg_remove_files(&ci->gadget.dev); + device_unregister(&ci->gadget.dev); /* my kobject is dynamic, I swear! */ - memset(&udc->gadget, 0, sizeof(udc->gadget)); + memset(&ci->gadget, 0, sizeof(ci->gadget)); } /** diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index d4cf970656fb..d2f7e494f5c0 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -19,7 +19,7 @@ struct ci13xxx_platform_data { #define CI13XXX_CONTROLLER_RESET_EVENT 0 #define CI13XXX_CONTROLLER_STOPPED_EVENT 1 - void (*notify_event) (struct ci13xxx *udc, unsigned event); + void (*notify_event) (struct ci13xxx *ci, unsigned event); }; /* Default offset of capability registers */ -- cgit v1.2.3 From cbc6dc2af39e1395564445fd71cfcc1c70a96277 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sat, 7 Jul 2012 22:56:41 +0800 Subject: USB: Chipidea: add unified ci13xxx_{add,remove}_device for platform drivers Platform drivers do the similar things to add/remove ci13xxx device, so create a unified one. Signed-off-by: Richard Zhao Reviewed-by: Felipe Balbi Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci13xxx_msm.c | 34 +++++++----------------------- drivers/usb/chipidea/ci13xxx_pci.c | 34 ++++++------------------------ drivers/usb/chipidea/core.c | 43 ++++++++++++++++++++++++++++++++++++++ include/linux/usb/chipidea.h | 7 +++++++ 4 files changed, 63 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/drivers/usb/chipidea/ci13xxx_msm.c b/drivers/usb/chipidea/ci13xxx_msm.c index 5a2fe5f9b6c3..b01feb3be92e 100644 --- a/drivers/usb/chipidea/ci13xxx_msm.c +++ b/drivers/usb/chipidea/ci13xxx_msm.c @@ -58,43 +58,23 @@ static struct ci13xxx_platform_data ci13xxx_msm_platdata = { static int __devinit ci13xxx_msm_probe(struct platform_device *pdev) { struct platform_device *plat_ci; - int ret; dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n"); - plat_ci = platform_device_alloc("ci_hdrc", -1); - if (!plat_ci) { - dev_err(&pdev->dev, "can't allocate ci_hdrc platform device\n"); - return -ENOMEM; + plat_ci = ci13xxx_add_device(&pdev->dev, + pdev->resource, pdev->num_resources, + &ci13xxx_msm_platdata); + if (IS_ERR(plat_ci)) { + dev_err(&pdev->dev, "ci13xxx_add_device failed!\n"); + return PTR_ERR(plat_ci); } - ret = platform_device_add_resources(plat_ci, pdev->resource, - pdev->num_resources); - if (ret) { - dev_err(&pdev->dev, "can't add resources to platform device\n"); - goto put_platform; - } - - ret = platform_device_add_data(plat_ci, &ci13xxx_msm_platdata, - sizeof(ci13xxx_msm_platdata)); - if (ret) - goto put_platform; - - ret = platform_device_add(plat_ci); - if (ret) - goto put_platform; - platform_set_drvdata(pdev, plat_ci); pm_runtime_no_callbacks(&pdev->dev); pm_runtime_enable(&pdev->dev); return 0; - -put_platform: - platform_device_put(plat_ci); - - return ret; } static int __devexit ci13xxx_msm_remove(struct platform_device *pdev) @@ -102,7 +82,7 @@ static int __devexit ci13xxx_msm_remove(struct platform_device *pdev) struct platform_device *plat_ci = platform_get_drvdata(pdev); pm_runtime_disable(&pdev->dev); - platform_device_unregister(plat_ci); + ci13xxx_remove_device(plat_ci); return 0; } diff --git a/drivers/usb/chipidea/ci13xxx_pci.c b/drivers/usb/chipidea/ci13xxx_pci.c index cdcac3a0e94d..918e14971f2b 100644 --- a/drivers/usb/chipidea/ci13xxx_pci.c +++ b/drivers/usb/chipidea/ci13xxx_pci.c @@ -75,13 +75,6 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, pci_set_master(pdev); pci_try_set_mwi(pdev); - plat_ci = platform_device_alloc("ci_hdrc", -1); - if (!plat_ci) { - dev_err(&pdev->dev, "can't allocate ci_hdrc platform device\n"); - retval = -ENOMEM; - goto disable_device; - } - memset(res, 0, sizeof(res)); res[0].start = pci_resource_start(pdev, 0); res[0].end = pci_resource_end(pdev, 0); @@ -89,32 +82,17 @@ static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, res[1].start = pdev->irq; res[1].flags = IORESOURCE_IRQ; - retval = platform_device_add_resources(plat_ci, res, nres); - if (retval) { - dev_err(&pdev->dev, "can't add resources to platform device\n"); - goto put_platform; + plat_ci = ci13xxx_add_device(&pdev->dev, res, nres, platdata); + if (IS_ERR(plat_ci)) { + dev_err(&pdev->dev, "ci13xxx_add_device failed!\n"); + retval = PTR_ERR(plat_ci); + goto disable_device; } - retval = platform_device_add_data(plat_ci, platdata, sizeof(*platdata)); - if (retval) - goto put_platform; - - dma_set_coherent_mask(&plat_ci->dev, pdev->dev.coherent_dma_mask); - plat_ci->dev.dma_mask = pdev->dev.dma_mask; - plat_ci->dev.dma_parms = pdev->dev.dma_parms; - plat_ci->dev.parent = &pdev->dev; - pci_set_drvdata(pdev, plat_ci); - retval = platform_device_add(plat_ci); - if (retval) - goto put_platform; - return 0; - put_platform: - pci_set_drvdata(pdev, NULL); - platform_device_put(plat_ci); disable_device: pci_disable_device(pdev); done: @@ -133,7 +111,7 @@ static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev) { struct platform_device *plat_ci = pci_get_drvdata(pdev); - platform_device_unregister(plat_ci); + ci13xxx_remove_device(plat_ci); pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); } diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 9a883bd5e113..8b9d06fd0325 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -332,6 +332,49 @@ static irqreturn_t ci_irq(int irq, void *data) return ci->role == CI_ROLE_END ? ret : ci_role(ci)->irq(ci); } +struct platform_device *ci13xxx_add_device(struct device *dev, + struct resource *res, int nres, + struct ci13xxx_platform_data *platdata) +{ + struct platform_device *pdev; + int ret; + + /* FIXME: find a way to choose id */ + pdev = platform_device_alloc("ci_hdrc", -1); + if (!pdev) + return ERR_PTR(-ENOMEM); + + pdev->dev.parent = dev; + pdev->dev.dma_mask = dev->dma_mask; + pdev->dev.dma_parms = dev->dma_parms; + dma_set_coherent_mask(&pdev->dev, dev->coherent_dma_mask); + + ret = platform_device_add_resources(pdev, res, nres); + if (ret) + goto err; + + ret = platform_device_add_data(pdev, platdata, sizeof(*platdata)); + if (ret) + goto err; + + ret = platform_device_add(pdev); + if (ret) + goto err; + + return pdev; + +err: + platform_device_put(pdev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(ci13xxx_add_device); + +void ci13xxx_remove_device(struct platform_device *pdev) +{ + platform_device_unregister(pdev); +} +EXPORT_SYMBOL_GPL(ci13xxx_remove_device); + static int __devinit ci_hdrc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index d2f7e494f5c0..be078f0bfde2 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -25,4 +25,11 @@ struct ci13xxx_platform_data { /* Default offset of capability registers */ #define DEF_CAPOFFSET 0x100 +/* Add ci13xxx device */ +struct platform_device *ci13xxx_add_device(struct device *dev, + struct resource *res, int nres, + struct ci13xxx_platform_data *platdata); +/* Remove ci13xxx device */ +void ci13xxx_remove_device(struct platform_device *pdev); + #endif -- cgit v1.2.3 From 526c51382d59144a3704970ab111d668e604a10f Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sat, 7 Jul 2012 22:56:44 +0800 Subject: usb: otg: add notify_connect/notify_disconnect callback This let usb phy driver has a chance to change hw settings when connect status change. Signed-off-by: Richard Zhao Acked-by: Felipe Balbi Tested-by: Subodh Nijsure Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/otg.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'include') diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 0cb2ec2e50c0..45824be0a2f9 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -128,6 +128,9 @@ struct usb_phy { int (*set_suspend)(struct usb_phy *x, int suspend); + /* notify phy connect status change */ + int (*notify_connect)(struct usb_phy *x, int port); + int (*notify_disconnect)(struct usb_phy *x, int port); }; @@ -276,6 +279,24 @@ usb_phy_set_suspend(struct usb_phy *x, int suspend) return 0; } +static inline int +usb_phy_notify_connect(struct usb_phy *x, int port) +{ + if (x->notify_connect) + return x->notify_connect(x, port); + else + return 0; +} + +static inline int +usb_phy_notify_disconnect(struct usb_phy *x, int port) +{ + if (x->notify_disconnect) + return x->notify_disconnect(x, port); + else + return 0; +} + static inline int otg_start_srp(struct usb_otg *otg) { -- cgit v1.2.3 From a2c3d6902f6f9916b5376c44baa8c1d08bf92a27 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Sat, 7 Jul 2012 22:56:46 +0800 Subject: usb: chipidea: permit driver bindings pass phy pointer Sometimes, the driver bindings may know what phy they use. For example, when using device tree, the usb controller may have a phandler pointing to usb phy. Signed-off-by: Richard Zhao Reviewed-by: Marek Vasut Acked-by: Felipe Balbi Tested-by: Subodh Nijsure Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 2 ++ drivers/usb/chipidea/core.c | 4 ++++ drivers/usb/chipidea/host.c | 1 + drivers/usb/chipidea/udc.c | 11 +++++++---- include/linux/usb/chipidea.h | 3 +++ 5 files changed, 17 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 9655e3569d4c..d738603a2757 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -160,6 +160,8 @@ struct ci13xxx { struct ci13xxx_platform_data *platdata; int vbus_active; + /* FIXME: some day, we'll not use global phy */ + bool global_phy; struct usb_phy *transceiver; struct usb_hcd *hcd; }; diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 39603d7b7916..1083585fad00 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -419,6 +419,10 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev) ci->dev = dev; ci->platdata = dev->platform_data; + if (ci->platdata->phy) + ci->transceiver = ci->platdata->phy; + else + ci->global_phy = true; ret = hw_device_init(ci, base); if (ret < 0) { diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 4a4fdb8c65f8..ebff9f4f56ec 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -117,6 +117,7 @@ static int host_start(struct ci13xxx *ci) hcd->has_tt = 1; hcd->power_budget = ci->platdata->power_budget; + hcd->phy = ci->transceiver; ehci = hcd_to_ehci(hcd); ehci->caps = ci->hw_bank.cap; diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index ba8284e2a237..c7a032a4f0c5 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1685,7 +1685,8 @@ static int udc_start(struct ci13xxx *ci) ci->gadget.ep0 = &ci->ep0in->ep; - ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); + if (ci->global_phy) + ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); if (ci->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) { if (ci->transceiver == NULL) { @@ -1729,7 +1730,8 @@ static int udc_start(struct ci13xxx *ci) remove_trans: if (!IS_ERR_OR_NULL(ci->transceiver)) { otg_set_peripheral(ci->transceiver->otg, &ci->gadget); - usb_put_phy(ci->transceiver); + if (ci->global_phy) + usb_put_phy(ci->transceiver); } dev_err(dev, "error = %i\n", retval); @@ -1738,7 +1740,7 @@ remove_dbg: unreg_device: device_unregister(&ci->gadget.dev); put_transceiver: - if (!IS_ERR_OR_NULL(ci->transceiver)) + if (!IS_ERR_OR_NULL(ci->transceiver) && ci->global_phy) usb_put_phy(ci->transceiver); free_pools: dma_pool_destroy(ci->td_pool); @@ -1772,7 +1774,8 @@ static void udc_stop(struct ci13xxx *ci) if (!IS_ERR_OR_NULL(ci->transceiver)) { otg_set_peripheral(ci->transceiver->otg, NULL); - usb_put_phy(ci->transceiver); + if (ci->global_phy) + usb_put_phy(ci->transceiver); } dbg_remove_files(&ci->gadget.dev); device_unregister(&ci->gadget.dev); diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index be078f0bfde2..544825dde823 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -5,12 +5,15 @@ #ifndef __LINUX_USB_CHIPIDEA_H #define __LINUX_USB_CHIPIDEA_H +#include + struct ci13xxx; struct ci13xxx_platform_data { const char *name; /* offset of the capability registers */ uintptr_t capoffset; unsigned power_budget; + struct usb_phy *phy; unsigned long flags; #define CI13XXX_REGS_SHARED BIT(0) #define CI13XXX_REQUIRE_TRANSCEIVER BIT(1) -- cgit v1.2.3 From 939546d1a9f47ed169554c711e1e05965b84ffe1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 9 Jul 2012 10:00:00 +0100 Subject: iio: Add callback to check whether a scan mask is valid This is useful for cases where the number of valid scan masks grows exponentially, but it is rather easy to check whether a mask is valid or not programmatically. An example of such a case is a device with multiple ADCs where each ADC has a upstream MUX, which allows to select from a number of physical channels. +-------+ +-------+ | | | | --- Channel 1 | ADC 1 |---| MUX 1 | --- ... | | | | --- Channel M +-------+ +-------+ . . . . . . . . . +-------+ +-------+ | | | | --- Channel M * N + 1 | ADC N |---| MUX N | --- ... | | | | --- Channel M * N + M +-------+ +-------+ The number of necessary scan masks for this case is (M+1)**N - 1, on the other hand it is easy to check whether subsets for each ADC of the scanmask have only one bit set. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 27 ++++++++++++++++++++------- include/linux/iio/iio.h | 4 ++++ 2 files changed, 24 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 3d8d187eef2a..cc5db36fb75a 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -570,6 +570,15 @@ int iio_sw_buffer_preenable(struct iio_dev *indio_dev) } EXPORT_SYMBOL(iio_sw_buffer_preenable); +static bool iio_validate_scan_mask(struct iio_dev *indio_dev, + const unsigned long *mask) +{ + if (!indio_dev->setup_ops->validate_scan_mask) + return true; + + return indio_dev->setup_ops->validate_scan_mask(indio_dev, mask); +} + /** * iio_scan_mask_set() - set particular bit in the scan mask * @buffer: the buffer whose scan mask we are interested in @@ -589,27 +598,31 @@ int iio_scan_mask_set(struct iio_dev *indio_dev, return -ENOMEM; if (!indio_dev->masklength) { WARN_ON("trying to set scanmask prior to registering buffer\n"); - kfree(trialmask); - return -EINVAL; + goto err_invalid_mask; } bitmap_copy(trialmask, buffer->scan_mask, indio_dev->masklength); set_bit(bit, trialmask); + if (!iio_validate_scan_mask(indio_dev, trialmask)) + goto err_invalid_mask; + if (indio_dev->available_scan_masks) { mask = iio_scan_mask_match(indio_dev->available_scan_masks, indio_dev->masklength, trialmask); - if (!mask) { - kfree(trialmask); - return -EINVAL; - } + if (!mask) + goto err_invalid_mask; } bitmap_copy(buffer->scan_mask, trialmask, indio_dev->masklength); kfree(trialmask); return 0; -}; + +err_invalid_mask: + kfree(trialmask); + return -EINVAL; +} EXPORT_SYMBOL_GPL(iio_scan_mask_set); int iio_scan_mask_query(struct iio_dev *indio_dev, diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 2afbb6f01afc..be82936c4089 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -363,12 +363,16 @@ struct iio_info { * @predisable: [DRIVER] function to run prior to marking buffer * disabled * @postdisable: [DRIVER] function to run after marking buffer disabled + * @validate_scan_mask: [DRIVER] function callback to check whether a given + * scan mask is valid for the device. */ struct iio_buffer_setup_ops { int (*preenable)(struct iio_dev *); int (*postenable)(struct iio_dev *); int (*predisable)(struct iio_dev *); int (*postdisable)(struct iio_dev *); + bool (*validate_scan_mask)(struct iio_dev *indio_dev, + const unsigned long *scan_mask); }; /** -- cgit v1.2.3 From 81636632057cc1bece2531220dd5803036f95ea9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 9 Jul 2012 10:00:00 +0100 Subject: iio: Introduce iio_validate_scan_mask_onehot Add a helper function for validating a scan mask for devices where exactly one channel must be selected during sampling. This is a common case among devices which have scan mask restrictions so it makes sense to provide this function in the core. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 16 ++++++++++++++++ include/linux/iio/buffer.h | 3 +++ 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index cc5db36fb75a..8c1dc9a683fb 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -570,6 +570,22 @@ int iio_sw_buffer_preenable(struct iio_dev *indio_dev) } EXPORT_SYMBOL(iio_sw_buffer_preenable); +/** + * iio_validate_scan_mask_onehot() - Validates that exactly one channel is selected + * @indio_dev: the iio device + * @mask: scan mask to be checked + * + * Return true if exactly one bit is set in the scan mask, false otherwise. It + * can be used for devices where only one channel can be active for sampling at + * a time. + */ +bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev, + const unsigned long *mask) +{ + return bitmap_weight(mask, indio_dev->masklength) == 1; +} +EXPORT_SYMBOL_GPL(iio_validate_scan_mask_onehot); + static bool iio_validate_scan_mask(struct iio_dev *indio_dev, const unsigned long *mask) { diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index 2a2b6b4d8d05..8ba516fc2ec6 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -177,6 +177,9 @@ ssize_t iio_buffer_show_enable(struct device *dev, int iio_sw_buffer_preenable(struct iio_dev *indio_dev); +bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev, + const unsigned long *mask); + #else /* CONFIG_IIO_BUFFER */ static inline int iio_buffer_register(struct iio_dev *indio_dev, -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 From a9a741a7e2e6337ae5c030e78827c233c08902a7 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Mon, 30 Apr 2012 18:21:51 +0200 Subject: NFC: Prepare asynchronous error management for driver and shdlc Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- include/net/nfc/hci.h | 2 ++ net/nfc/hci/core.c | 8 ++++++++ net/nfc/hci/shdlc.c | 19 +++++++++++-------- 3 files changed, 21 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index e30e6a869714..d25dd9b99877 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -112,6 +112,8 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev); void nfc_hci_set_clientdata(struct nfc_hci_dev *hdev, void *clientdata); void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev); +void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err); + /* Host IDs */ #define NFC_HCI_HOST_CONTROLLER_ID 0x00 #define NFC_HCI_TERMINAL_HOST_ID 0x01 diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index a8b0b71e8f86..1dc6485343b9 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -717,6 +717,14 @@ void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev) } EXPORT_SYMBOL(nfc_hci_get_clientdata); +void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err) +{ + /* TODO: lower layer has permanent failure. + * complete potential HCI command or send an empty tag discovered event + */ +} +EXPORT_SYMBOL(nfc_hci_driver_failure); + void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb) { struct hcp_packet *packet; diff --git a/net/nfc/hci/shdlc.c b/net/nfc/hci/shdlc.c index 6b836e6242b7..d7c74d152a72 100644 --- a/net/nfc/hci/shdlc.c +++ b/net/nfc/hci/shdlc.c @@ -523,10 +523,6 @@ static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc) r = shdlc->ops->xmit(shdlc, skb); if (r < 0) { - /* - * TODO: Cannot send, shdlc machine is dead, we - * must propagate the information up to HCI. - */ shdlc->hard_fault = r; break; } @@ -590,6 +586,11 @@ static void nfc_shdlc_sm_work(struct work_struct *work) skb_queue_purge(&shdlc->ack_pending_q); break; case SHDLC_CONNECTING: + if (shdlc->hard_fault) { + nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault); + break; + } + if (shdlc->connect_tries++ < 5) r = nfc_shdlc_connect_initiate(shdlc); else @@ -610,6 +611,11 @@ static void nfc_shdlc_sm_work(struct work_struct *work) } nfc_shdlc_handle_rcv_queue(shdlc); + + if (shdlc->hard_fault) { + nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault); + break; + } break; case SHDLC_CONNECTED: nfc_shdlc_handle_rcv_queue(shdlc); @@ -637,10 +643,7 @@ static void nfc_shdlc_sm_work(struct work_struct *work) } if (shdlc->hard_fault) { - /* - * TODO: Handle hard_fault that occured during - * this invocation of the shdlc worker - */ + nfc_hci_driver_failure(shdlc->hdev, shdlc->hard_fault); } break; default: -- cgit v1.2.3 From 456411ca812860d7ba06d3e4013ce1d8b9dbc7cd Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Mon, 11 Jun 2012 13:49:51 +0200 Subject: NFC: Driver failure API This API should be used by drivers, HCI, SHDLC or NCI stacks to report an unrecoverable error. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- include/net/nfc/nfc.h | 2 ++ net/nfc/core.c | 10 ++++++++++ 2 files changed, 12 insertions(+) (limited to 'include') diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 180964b954ab..6431f5e39022 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -204,4 +204,6 @@ int nfc_tm_activated(struct nfc_dev *dev, u32 protocol, u8 comm_mode, int nfc_tm_deactivated(struct nfc_dev *dev); int nfc_tm_data_received(struct nfc_dev *dev, struct sk_buff *skb); +void nfc_driver_failure(struct nfc_dev *dev, int err); + #endif /* __NET_NFC_H */ diff --git a/net/nfc/core.c b/net/nfc/core.c index 4177bb5104b9..32f28326b623 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -651,6 +651,16 @@ int nfc_target_lost(struct nfc_dev *dev, u32 target_idx) } EXPORT_SYMBOL(nfc_target_lost); +void nfc_driver_failure(struct nfc_dev *dev, int err) +{ + /* + * TODO: if polling is active, send empty target_found + * or else do whatever makes sense to let user space + * know this device needs to be closed and reinitialized. + */ +} +EXPORT_SYMBOL(nfc_driver_failure); + static void nfc_release(struct device *d) { struct nfc_dev *dev = to_nfc_dev(d); -- cgit v1.2.3 From a10d595b1074d04446f77161eea165e5809e163c Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 5 Jun 2012 14:42:11 +0200 Subject: NFC: Allow HCI driver to pre-open pipes to some gates Some NFC chips will statically create and open pipes for both standard and proprietary gates. The driver can now pass this information to HCI such that HCI will not attempt to create and open them, but will instead directly use the passed pipe ids. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544_hci.c | 31 ++++++++++++++++++------------- include/net/nfc/hci.h | 17 ++++++++++++++--- net/nfc/hci/command.c | 8 ++++++-- net/nfc/hci/core.c | 23 +++++++++-------------- net/nfc/hci/hci.h | 5 ----- 5 files changed, 47 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index 69df6fecb847..c67b55e224e0 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -108,16 +108,22 @@ enum pn544_state { #define PN544_NFC_WI_MGMT_GATE 0xA1 -static u8 pn544_custom_gates[] = { - PN544_SYS_MGMT_GATE, - PN544_SWP_MGMT_GATE, - PN544_POLLING_LOOP_MGMT_GATE, - PN544_NFC_WI_MGMT_GATE, - PN544_RF_READER_F_GATE, - PN544_RF_READER_JEWEL_GATE, - PN544_RF_READER_ISO15693_GATE, - PN544_RF_READER_NFCIP1_INITIATOR_GATE, - PN544_RF_READER_NFCIP1_TARGET_GATE +static struct nfc_hci_gate pn544_gates[] = { + {NFC_HCI_ADMIN_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_ID_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_LINK_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_RF_READER_B_GATE, NFC_HCI_INVALID_PIPE}, + {NFC_HCI_RF_READER_A_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_SYS_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_SWP_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_POLLING_LOOP_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_NFC_WI_MGMT_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_RF_READER_JEWEL_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_RF_READER_NFCIP1_INITIATOR_GATE, NFC_HCI_INVALID_PIPE}, + {PN544_RF_READER_NFCIP1_TARGET_GATE, NFC_HCI_INVALID_PIPE} }; /* Largest headroom needed for outgoing custom commands */ @@ -849,10 +855,9 @@ static int __devinit pn544_hci_probe(struct i2c_client *client, goto err_rti; } - init_data.gate_count = ARRAY_SIZE(pn544_custom_gates); + init_data.gate_count = ARRAY_SIZE(pn544_gates); - memcpy(init_data.gates, pn544_custom_gates, - ARRAY_SIZE(pn544_custom_gates)); + memcpy(init_data.gates, pn544_gates, sizeof(pn544_gates)); /* * TODO: Session id must include the driver name + some bus addr diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index d25dd9b99877..f5169b04f082 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -44,10 +44,20 @@ struct nfc_hci_ops { struct nfc_target *target); }; -#define NFC_HCI_MAX_CUSTOM_GATES 15 +/* Pipes */ +#define NFC_HCI_INVALID_PIPE 0x80 +#define NFC_HCI_LINK_MGMT_PIPE 0x00 +#define NFC_HCI_ADMIN_PIPE 0x01 + +struct nfc_hci_gate { + u8 gate; + u8 pipe; +}; + +#define NFC_HCI_MAX_CUSTOM_GATES 50 struct nfc_hci_init_data { u8 gate_count; - u8 gates[NFC_HCI_MAX_CUSTOM_GATES]; + struct nfc_hci_gate gates[NFC_HCI_MAX_CUSTOM_GATES]; char session_id[9]; }; @@ -182,7 +192,8 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb); /* connecting to gates and sending hci instructions */ -int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate); +int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate, + u8 pipe); int nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate); int nfc_hci_disconnect_all_gates(struct nfc_hci_dev *hdev); int nfc_hci_get_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx, diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c index 12cd6f3f77ec..46362ef979db 100644 --- a/net/nfc/hci/command.c +++ b/net/nfc/hci/command.c @@ -299,9 +299,9 @@ int nfc_hci_disconnect_all_gates(struct nfc_hci_dev *hdev) } EXPORT_SYMBOL(nfc_hci_disconnect_all_gates); -int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate) +int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate, + u8 pipe) { - u8 pipe = NFC_HCI_INVALID_PIPE; bool pipe_created = false; int r; @@ -310,6 +310,9 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate) if (hdev->gate2pipe[dest_gate] != NFC_HCI_INVALID_PIPE) return -EADDRINUSE; + if (pipe != NFC_HCI_INVALID_PIPE) + goto pipe_is_open; + switch (dest_gate) { case NFC_HCI_LINK_MGMT_GATE: pipe = NFC_HCI_LINK_MGMT_PIPE; @@ -335,6 +338,7 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate) return r; } +pipe_is_open: hdev->gate2pipe[dest_gate] = pipe; return 0; diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index e6b2df3981b6..4ccc518f56eb 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -315,15 +315,15 @@ static void nfc_hci_cmd_timeout(unsigned long data) } static int hci_dev_connect_gates(struct nfc_hci_dev *hdev, u8 gate_count, - u8 gates[]) + struct nfc_hci_gate *gates) { int r; - u8 *p = gates; while (gate_count--) { - r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID, *p); + r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID, + gates->gate, gates->pipe); if (r < 0) return r; - p++; + gates++; } return 0; @@ -333,14 +333,13 @@ static int hci_dev_session_init(struct nfc_hci_dev *hdev) { struct sk_buff *skb = NULL; int r; - u8 hci_gates[] = { /* NFC_HCI_ADMIN_GATE MUST be first */ - NFC_HCI_ADMIN_GATE, NFC_HCI_LOOPBACK_GATE, - NFC_HCI_ID_MGMT_GATE, NFC_HCI_LINK_MGMT_GATE, - NFC_HCI_RF_READER_B_GATE, NFC_HCI_RF_READER_A_GATE - }; + + if (hdev->init_data.gates[0].gate != NFC_HCI_ADMIN_GATE) + return -EPROTO; r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID, - NFC_HCI_ADMIN_GATE); + hdev->init_data.gates[0].gate, + hdev->init_data.gates[0].pipe); if (r < 0) goto exit; @@ -368,10 +367,6 @@ static int hci_dev_session_init(struct nfc_hci_dev *hdev) if (r < 0) goto exit; - r = hci_dev_connect_gates(hdev, sizeof(hci_gates), hci_gates); - if (r < 0) - goto disconnect_all; - r = hci_dev_connect_gates(hdev, hdev->init_data.gate_count, hdev->init_data.gates); if (r < 0) diff --git a/net/nfc/hci/hci.h b/net/nfc/hci/hci.h index d3cde075ba60..fa9a21e92239 100644 --- a/net/nfc/hci/hci.h +++ b/net/nfc/hci/hci.h @@ -132,9 +132,4 @@ void nfc_hci_hcp_message_rx(struct nfc_hci_dev *hdev, u8 pipe, u8 type, #define NFC_HCI_ANY_E_REG_ACCESS_DENIED 0x0a #define NFC_HCI_ANY_E_PIPE_ACCESS_DENIED 0x0b -/* Pipes */ -#define NFC_HCI_INVALID_PIPE 0x80 -#define NFC_HCI_LINK_MGMT_PIPE 0x00 -#define NFC_HCI_ADMIN_PIPE 0x01 - #endif /* __LOCAL_HCI_H */ -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 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') 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 From 2a51da04fef56ec83f790bf0746e90fe40215a92 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 9 Jul 2012 19:33:14 +0100 Subject: mfd: Add support for multiple arizona PDM speaker outputs The registers have stride 2 so we can write the loop properly now. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-core.c | 5 ++--- include/linux/mfd/arizona/pdata.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index ffa011f4677e..b35680dcd8c1 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -462,18 +462,17 @@ int __devinit arizona_dev_init(struct arizona *arizona) ARIZONA_OUT1_MONO, val); } - BUILD_BUG_ON(ARIZONA_MAX_PDM_SPK > 1); for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) { if (arizona->pdata.spk_mute[i]) regmap_update_bits(arizona->regmap, - ARIZONA_PDM_SPK1_CTRL_1, + ARIZONA_PDM_SPK1_CTRL_1 + (i * 2), ARIZONA_SPK1_MUTE_ENDIAN_MASK | ARIZONA_SPK1_MUTE_SEQ1_MASK, arizona->pdata.spk_mute[i]); if (arizona->pdata.spk_fmt[i]) regmap_update_bits(arizona->regmap, - ARIZONA_PDM_SPK1_CTRL_2, + ARIZONA_PDM_SPK1_CTRL_2 + (i * 2), ARIZONA_SPK1_FMT_MASK, arizona->pdata.spk_fmt[i]); } diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index fa2cb9885d62..68ff91aa3888 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -62,7 +62,7 @@ #define ARIZONA_MAX_OUTPUT 5 -#define ARIZONA_MAX_PDM_SPK 1 +#define ARIZONA_MAX_PDM_SPK 2 struct regulator_init_data; -- cgit v1.2.3 From 1faedca9c7bfd3055204b9d10017ce77ad03fc72 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 9 Jul 2012 19:33:15 +0100 Subject: mfd: Add even more arizona register definitions A few more registers used on newer devices. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- include/linux/mfd/arizona/registers.h | 211 +++++++++++++++++++++++++++++++++- 1 file changed, 210 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 8f49106d7bda..7671a287dfee 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -145,7 +145,7 @@ #define ARIZONA_IN3R_CONTROL 0x324 #define ARIZONA_ADC_DIGITAL_VOLUME_3R 0x325 #define ARIZONA_DMIC3R_CONTROL 0x326 -#define ARIZONA_IN4_CONTROL 0x328 +#define ARIZONA_IN4L_CONTROL 0x328 #define ARIZONA_ADC_DIGITAL_VOLUME_4L 0x329 #define ARIZONA_DMIC4L_CONTROL 0x32A #define ARIZONA_ADC_DIGITAL_VOLUME_4R 0x32D @@ -2129,6 +2129,14 @@ /* * R768 (0x300) - Input Enables */ +#define ARIZONA_IN4L_ENA 0x0080 /* IN4L_ENA */ +#define ARIZONA_IN4L_ENA_MASK 0x0080 /* IN4L_ENA */ +#define ARIZONA_IN4L_ENA_SHIFT 7 /* IN4L_ENA */ +#define ARIZONA_IN4L_ENA_WIDTH 1 /* IN4L_ENA */ +#define ARIZONA_IN4R_ENA 0x0040 /* IN4R_ENA */ +#define ARIZONA_IN4R_ENA_MASK 0x0040 /* IN4R_ENA */ +#define ARIZONA_IN4R_ENA_SHIFT 6 /* IN4R_ENA */ +#define ARIZONA_IN4R_ENA_WIDTH 1 /* IN4R_ENA */ #define ARIZONA_IN3L_ENA 0x0020 /* IN3L_ENA */ #define ARIZONA_IN3L_ENA_MASK 0x0020 /* IN3L_ENA */ #define ARIZONA_IN3L_ENA_SHIFT 5 /* IN3L_ENA */ @@ -2372,9 +2380,71 @@ #define ARIZONA_IN3_DMICR_DLY_SHIFT 0 /* IN3_DMICR_DLY - [5:0] */ #define ARIZONA_IN3_DMICR_DLY_WIDTH 6 /* IN3_DMICR_DLY - [5:0] */ +/* + * R808 (0x328) - IN4 Control + */ +#define ARIZONA_IN4_OSR_MASK 0x6000 /* IN4_OSR - [14:13] */ +#define ARIZONA_IN4_OSR_SHIFT 13 /* IN4_OSR - [14:13] */ +#define ARIZONA_IN4_OSR_WIDTH 2 /* IN4_OSR - [14:13] */ +#define ARIZONA_IN4_DMIC_SUP_MASK 0x1800 /* IN4_DMIC_SUP - [12:11] */ +#define ARIZONA_IN4_DMIC_SUP_SHIFT 11 /* IN4_DMIC_SUP - [12:11] */ +#define ARIZONA_IN4_DMIC_SUP_WIDTH 2 /* IN4_DMIC_SUP - [12:11] */ + +/* + * R809 (0x329) - ADC Digital Volume 4L + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN4L_MUTE 0x0100 /* IN4L_MUTE */ +#define ARIZONA_IN4L_MUTE_MASK 0x0100 /* IN4L_MUTE */ +#define ARIZONA_IN4L_MUTE_SHIFT 8 /* IN4L_MUTE */ +#define ARIZONA_IN4L_MUTE_WIDTH 1 /* IN4L_MUTE */ +#define ARIZONA_IN4L_DIG_VOL_MASK 0x00FF /* IN4L_DIG_VOL - [7:0] */ +#define ARIZONA_IN4L_DIG_VOL_SHIFT 0 /* IN4L_DIG_VOL - [7:0] */ +#define ARIZONA_IN4L_DIG_VOL_WIDTH 8 /* IN4L_DIG_VOL - [7:0] */ + +/* + * R810 (0x32A) - DMIC4L Control + */ +#define ARIZONA_IN4L_DMIC_DLY_MASK 0x003F /* IN4L_DMIC_DLY - [5:0] */ +#define ARIZONA_IN4L_DMIC_DLY_SHIFT 0 /* IN4L_DMIC_DLY - [5:0] */ +#define ARIZONA_IN4L_DMIC_DLY_WIDTH 6 /* IN4L_DMIC_DLY - [5:0] */ + +/* + * R813 (0x32D) - ADC Digital Volume 4R + */ +#define ARIZONA_IN_VU 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_MASK 0x0200 /* IN_VU */ +#define ARIZONA_IN_VU_SHIFT 9 /* IN_VU */ +#define ARIZONA_IN_VU_WIDTH 1 /* IN_VU */ +#define ARIZONA_IN4R_MUTE 0x0100 /* IN4R_MUTE */ +#define ARIZONA_IN4R_MUTE_MASK 0x0100 /* IN4R_MUTE */ +#define ARIZONA_IN4R_MUTE_SHIFT 8 /* IN4R_MUTE */ +#define ARIZONA_IN4R_MUTE_WIDTH 1 /* IN4R_MUTE */ +#define ARIZONA_IN4R_DIG_VOL_MASK 0x00FF /* IN4R_DIG_VOL - [7:0] */ +#define ARIZONA_IN4R_DIG_VOL_SHIFT 0 /* IN4R_DIG_VOL - [7:0] */ +#define ARIZONA_IN4R_DIG_VOL_WIDTH 8 /* IN4R_DIG_VOL - [7:0] */ + +/* + * R814 (0x32E) - DMIC4R Control + */ +#define ARIZONA_IN4R_DMIC_DLY_MASK 0x003F /* IN4R_DMIC_DLY - [5:0] */ +#define ARIZONA_IN4R_DMIC_DLY_SHIFT 0 /* IN4R_DMIC_DLY - [5:0] */ +#define ARIZONA_IN4R_DMIC_DLY_WIDTH 6 /* IN4R_DMIC_DLY - [5:0] */ + /* * R1024 (0x400) - Output Enables 1 */ +#define ARIZONA_OUT6L_ENA 0x0800 /* OUT6L_ENA */ +#define ARIZONA_OUT6L_ENA_MASK 0x0800 /* OUT6L_ENA */ +#define ARIZONA_OUT6L_ENA_SHIFT 11 /* OUT6L_ENA */ +#define ARIZONA_OUT6L_ENA_WIDTH 1 /* OUT6L_ENA */ +#define ARIZONA_OUT6R_ENA 0x0400 /* OUT6R_ENA */ +#define ARIZONA_OUT6R_ENA_MASK 0x0400 /* OUT6R_ENA */ +#define ARIZONA_OUT6R_ENA_SHIFT 10 /* OUT6R_ENA */ +#define ARIZONA_OUT6R_ENA_WIDTH 1 /* OUT6R_ENA */ #define ARIZONA_OUT5L_ENA 0x0200 /* OUT5L_ENA */ #define ARIZONA_OUT5L_ENA_MASK 0x0200 /* OUT5L_ENA */ #define ARIZONA_OUT5L_ENA_SHIFT 9 /* OUT5L_ENA */ @@ -2876,6 +2946,82 @@ #define ARIZONA_OUT5R_NGATE_SRC_SHIFT 0 /* OUT5R_NGATE_SRC - [11:0] */ #define ARIZONA_OUT5R_NGATE_SRC_WIDTH 12 /* OUT5R_NGATE_SRC - [11:0] */ +/* + * R1080 (0x438) - Output Path Config 6L + */ +#define ARIZONA_OUT6_OSR 0x2000 /* OUT6_OSR */ +#define ARIZONA_OUT6_OSR_MASK 0x2000 /* OUT6_OSR */ +#define ARIZONA_OUT6_OSR_SHIFT 13 /* OUT6_OSR */ +#define ARIZONA_OUT6_OSR_WIDTH 1 /* OUT6_OSR */ +#define ARIZONA_OUT6L_ANC_SRC_MASK 0x0C00 /* OUT6L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT6L_ANC_SRC_SHIFT 10 /* OUT6L_ANC_SRC - [11:10] */ +#define ARIZONA_OUT6L_ANC_SRC_WIDTH 2 /* OUT6L_ANC_SRC - [11:10] */ + +/* + * R1081 (0x439) - DAC Digital Volume 6L + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT6L_MUTE 0x0100 /* OUT6L_MUTE */ +#define ARIZONA_OUT6L_MUTE_MASK 0x0100 /* OUT6L_MUTE */ +#define ARIZONA_OUT6L_MUTE_SHIFT 8 /* OUT6L_MUTE */ +#define ARIZONA_OUT6L_MUTE_WIDTH 1 /* OUT6L_MUTE */ +#define ARIZONA_OUT6L_VOL_MASK 0x00FF /* OUT6L_VOL - [7:0] */ +#define ARIZONA_OUT6L_VOL_SHIFT 0 /* OUT6L_VOL - [7:0] */ +#define ARIZONA_OUT6L_VOL_WIDTH 8 /* OUT6L_VOL - [7:0] */ + +/* + * R1082 (0x43A) - DAC Volume Limit 6L + */ +#define ARIZONA_OUT6L_VOL_LIM_MASK 0x00FF /* OUT6L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT6L_VOL_LIM_SHIFT 0 /* OUT6L_VOL_LIM - [7:0] */ +#define ARIZONA_OUT6L_VOL_LIM_WIDTH 8 /* OUT6L_VOL_LIM - [7:0] */ + +/* + * R1083 (0x43B) - Noise Gate Select 6L + */ +#define ARIZONA_OUT6L_NGATE_SRC_MASK 0x0FFF /* OUT6L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT6L_NGATE_SRC_SHIFT 0 /* OUT6L_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT6L_NGATE_SRC_WIDTH 12 /* OUT6L_NGATE_SRC - [11:0] */ + +/* + * R1084 (0x43C) - Output Path Config 6R + */ +#define ARIZONA_OUT6R_ANC_SRC_MASK 0x0C00 /* OUT6R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT6R_ANC_SRC_SHIFT 10 /* OUT6R_ANC_SRC - [11:10] */ +#define ARIZONA_OUT6R_ANC_SRC_WIDTH 2 /* OUT6R_ANC_SRC - [11:10] */ + +/* + * R1085 (0x43D) - DAC Digital Volume 6R + */ +#define ARIZONA_OUT_VU 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define ARIZONA_OUT_VU_SHIFT 9 /* OUT_VU */ +#define ARIZONA_OUT_VU_WIDTH 1 /* OUT_VU */ +#define ARIZONA_OUT6R_MUTE 0x0100 /* OUT6R_MUTE */ +#define ARIZONA_OUT6R_MUTE_MASK 0x0100 /* OUT6R_MUTE */ +#define ARIZONA_OUT6R_MUTE_SHIFT 8 /* OUT6R_MUTE */ +#define ARIZONA_OUT6R_MUTE_WIDTH 1 /* OUT6R_MUTE */ +#define ARIZONA_OUT6R_VOL_MASK 0x00FF /* OUT6R_VOL - [7:0] */ +#define ARIZONA_OUT6R_VOL_SHIFT 0 /* OUT6R_VOL - [7:0] */ +#define ARIZONA_OUT6R_VOL_WIDTH 8 /* OUT6R_VOL - [7:0] */ + +/* + * R1086 (0x43E) - DAC Volume Limit 6R + */ +#define ARIZONA_OUT6R_VOL_LIM_MASK 0x00FF /* OUT6R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT6R_VOL_LIM_SHIFT 0 /* OUT6R_VOL_LIM - [7:0] */ +#define ARIZONA_OUT6R_VOL_LIM_WIDTH 8 /* OUT6R_VOL_LIM - [7:0] */ + +/* + * R1087 (0x43F) - Noise Gate Select 6R + */ +#define ARIZONA_OUT6R_NGATE_SRC_MASK 0x0FFF /* OUT6R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT6R_NGATE_SRC_SHIFT 0 /* OUT6R_NGATE_SRC - [11:0] */ +#define ARIZONA_OUT6R_NGATE_SRC_WIDTH 12 /* OUT6R_NGATE_SRC - [11:0] */ + /* * R1104 (0x450) - DAC AEC Control 1 */ @@ -2932,6 +3078,33 @@ #define ARIZONA_SPK1_FMT_SHIFT 0 /* SPK1_FMT */ #define ARIZONA_SPK1_FMT_WIDTH 1 /* SPK1_FMT */ +/* + * R1170 (0x492) - PDM SPK2 CTRL 1 + */ +#define ARIZONA_SPK2R_MUTE 0x2000 /* SPK2R_MUTE */ +#define ARIZONA_SPK2R_MUTE_MASK 0x2000 /* SPK2R_MUTE */ +#define ARIZONA_SPK2R_MUTE_SHIFT 13 /* SPK2R_MUTE */ +#define ARIZONA_SPK2R_MUTE_WIDTH 1 /* SPK2R_MUTE */ +#define ARIZONA_SPK2L_MUTE 0x1000 /* SPK2L_MUTE */ +#define ARIZONA_SPK2L_MUTE_MASK 0x1000 /* SPK2L_MUTE */ +#define ARIZONA_SPK2L_MUTE_SHIFT 12 /* SPK2L_MUTE */ +#define ARIZONA_SPK2L_MUTE_WIDTH 1 /* SPK2L_MUTE */ +#define ARIZONA_SPK2_MUTE_ENDIAN 0x0100 /* SPK2_MUTE_ENDIAN */ +#define ARIZONA_SPK2_MUTE_ENDIAN_MASK 0x0100 /* SPK2_MUTE_ENDIAN */ +#define ARIZONA_SPK2_MUTE_ENDIAN_SHIFT 8 /* SPK2_MUTE_ENDIAN */ +#define ARIZONA_SPK2_MUTE_ENDIAN_WIDTH 1 /* SPK2_MUTE_ENDIAN */ +#define ARIZONA_SPK2_MUTE_SEQ_MASK 0x00FF /* SPK2_MUTE_SEQ - [7:0] */ +#define ARIZONA_SPK2_MUTE_SEQ_SHIFT 0 /* SPK2_MUTE_SEQ - [7:0] */ +#define ARIZONA_SPK2_MUTE_SEQ_WIDTH 8 /* SPK2_MUTE_SEQ - [7:0] */ + +/* + * R1171 (0x493) - PDM SPK2 CTRL 2 + */ +#define ARIZONA_SPK2_FMT 0x0001 /* SPK2_FMT */ +#define ARIZONA_SPK2_FMT_MASK 0x0001 /* SPK2_FMT */ +#define ARIZONA_SPK2_FMT_SHIFT 0 /* SPK2_FMT */ +#define ARIZONA_SPK2_FMT_WIDTH 1 /* SPK2_FMT */ + /* * R1244 (0x4DC) - DAC comp 1 */ @@ -4028,10 +4201,46 @@ /* * R3329 (0xD01) - Interrupt Status 2 */ +#define ARIZONA_DSP4_RAM_RDY_EINT1 0x0800 /* DSP4_RAM_RDY_EINT1 */ +#define ARIZONA_DSP4_RAM_RDY_EINT1_MASK 0x0800 /* DSP4_RAM_RDY_EINT1 */ +#define ARIZONA_DSP4_RAM_RDY_EINT1_SHIFT 11 /* DSP4_RAM_RDY_EINT1 */ +#define ARIZONA_DSP4_RAM_RDY_EINT1_WIDTH 1 /* DSP4_RAM_RDY_EINT1 */ +#define ARIZONA_DSP3_RAM_RDY_EINT1 0x0400 /* DSP3_RAM_RDY_EINT1 */ +#define ARIZONA_DSP3_RAM_RDY_EINT1_MASK 0x0400 /* DSP3_RAM_RDY_EINT1 */ +#define ARIZONA_DSP3_RAM_RDY_EINT1_SHIFT 10 /* DSP3_RAM_RDY_EINT1 */ +#define ARIZONA_DSP3_RAM_RDY_EINT1_WIDTH 1 /* DSP3_RAM_RDY_EINT1 */ +#define ARIZONA_DSP2_RAM_RDY_EINT1 0x0200 /* DSP2_RAM_RDY_EINT1 */ +#define ARIZONA_DSP2_RAM_RDY_EINT1_MASK 0x0200 /* DSP2_RAM_RDY_EINT1 */ +#define ARIZONA_DSP2_RAM_RDY_EINT1_SHIFT 9 /* DSP2_RAM_RDY_EINT1 */ +#define ARIZONA_DSP2_RAM_RDY_EINT1_WIDTH 1 /* DSP2_RAM_RDY_EINT1 */ #define ARIZONA_DSP1_RAM_RDY_EINT1 0x0100 /* DSP1_RAM_RDY_EINT1 */ #define ARIZONA_DSP1_RAM_RDY_EINT1_MASK 0x0100 /* DSP1_RAM_RDY_EINT1 */ #define ARIZONA_DSP1_RAM_RDY_EINT1_SHIFT 8 /* DSP1_RAM_RDY_EINT1 */ #define ARIZONA_DSP1_RAM_RDY_EINT1_WIDTH 1 /* DSP1_RAM_RDY_EINT1 */ +#define ARIZONA_DSP_IRQ8_EINT1 0x0080 /* DSP_IRQ8_EINT1 */ +#define ARIZONA_DSP_IRQ8_EINT1_MASK 0x0080 /* DSP_IRQ8_EINT1 */ +#define ARIZONA_DSP_IRQ8_EINT1_SHIFT 7 /* DSP_IRQ8_EINT1 */ +#define ARIZONA_DSP_IRQ8_EINT1_WIDTH 1 /* DSP_IRQ8_EINT1 */ +#define ARIZONA_DSP_IRQ7_EINT1 0x0040 /* DSP_IRQ7_EINT1 */ +#define ARIZONA_DSP_IRQ7_EINT1_MASK 0x0040 /* DSP_IRQ7_EINT1 */ +#define ARIZONA_DSP_IRQ7_EINT1_SHIFT 6 /* DSP_IRQ7_EINT1 */ +#define ARIZONA_DSP_IRQ7_EINT1_WIDTH 1 /* DSP_IRQ7_EINT1 */ +#define ARIZONA_DSP_IRQ6_EINT1 0x0020 /* DSP_IRQ6_EINT1 */ +#define ARIZONA_DSP_IRQ6_EINT1_MASK 0x0020 /* DSP_IRQ6_EINT1 */ +#define ARIZONA_DSP_IRQ6_EINT1_SHIFT 5 /* DSP_IRQ6_EINT1 */ +#define ARIZONA_DSP_IRQ6_EINT1_WIDTH 1 /* DSP_IRQ6_EINT1 */ +#define ARIZONA_DSP_IRQ5_EINT1 0x0010 /* DSP_IRQ5_EINT1 */ +#define ARIZONA_DSP_IRQ5_EINT1_MASK 0x0010 /* DSP_IRQ5_EINT1 */ +#define ARIZONA_DSP_IRQ5_EINT1_SHIFT 4 /* DSP_IRQ5_EINT1 */ +#define ARIZONA_DSP_IRQ5_EINT1_WIDTH 1 /* DSP_IRQ5_EINT1 */ +#define ARIZONA_DSP_IRQ4_EINT1 0x0008 /* DSP_IRQ4_EINT1 */ +#define ARIZONA_DSP_IRQ4_EINT1_MASK 0x0008 /* DSP_IRQ4_EINT1 */ +#define ARIZONA_DSP_IRQ4_EINT1_SHIFT 3 /* DSP_IRQ4_EINT1 */ +#define ARIZONA_DSP_IRQ4_EINT1_WIDTH 1 /* DSP_IRQ4_EINT1 */ +#define ARIZONA_DSP_IRQ3_EINT1 0x0004 /* DSP_IRQ3_EINT1 */ +#define ARIZONA_DSP_IRQ3_EINT1_MASK 0x0004 /* DSP_IRQ3_EINT1 */ +#define ARIZONA_DSP_IRQ3_EINT1_SHIFT 2 /* DSP_IRQ3_EINT1 */ +#define ARIZONA_DSP_IRQ3_EINT1_WIDTH 1 /* DSP_IRQ3_EINT1 */ #define ARIZONA_DSP_IRQ2_EINT1 0x0002 /* DSP_IRQ2_EINT1 */ #define ARIZONA_DSP_IRQ2_EINT1_MASK 0x0002 /* DSP_IRQ2_EINT1 */ #define ARIZONA_DSP_IRQ2_EINT1_SHIFT 1 /* DSP_IRQ2_EINT1 */ -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 From 21cd1fab058671313f7c178b640999fcd0d8de21 Mon Sep 17 00:00:00 2001 From: Jon Brenner Date: Wed, 16 May 2012 10:46:42 -0500 Subject: IIO channel type and modifiers for CCT and RGBC data Add iio channel type and modifiers for Correlated Color Temperature (CCT) and RGBC (red/green/blue/clear) data. Add CCT and RGBC descriptions to documentation. Changes: Revised/condensed RGBC descriptions. Merge and trivial fix done by Jonathan Cameron. Signed-off-by: Jon Brenner Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 5 +++++ .../staging/iio/Documentation/sysfs-bus-iio-light | 23 ++++++++++++++++++++++ include/linux/iio/types.h | 5 +++++ 3 files changed, 33 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index bb3c692e49b8..2ec266ef41a3 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -64,6 +64,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_TIMESTAMP] = "timestamp", [IIO_CAPACITANCE] = "capacitance", [IIO_ALTVOLTAGE] = "altvoltage", + [IIO_CCT] = "cct", }; static const char * const iio_modifier_names[] = { @@ -74,6 +75,10 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_SUM_SQUARED_X_Y_Z] = "x^2+y^2+z^2", [IIO_MOD_LIGHT_BOTH] = "both", [IIO_MOD_LIGHT_IR] = "ir", + [IIO_MOD_LIGHT_CLEAR] = "clear", + [IIO_MOD_LIGHT_RED] = "red", + [IIO_MOD_LIGHT_GREEN] = "green", + [IIO_MOD_LIGHT_BLUE] = "blue", }; /* relies on pairs of these shared then separate */ diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light b/drivers/staging/iio/Documentation/sysfs-bus-iio-light index d52be0385dc4..a28919da13e3 100644 --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light @@ -82,3 +82,26 @@ Contact: linux-iio@vger.kernel.org Description: This property gets/sets the table of coefficients used in calculating illuminance in lux. + +What: /sys/bus/iio/devices/device[n]/in_intensity_clear[_input|_raw] +What: /sys/bus/iio/devices/device[n]/in_intensity_red[_input|_raw] +What: /sys/bus/iio/devices/device[n]/in_intensity_green[_input|_raw] +What: /sys/bus/iio/devices/device[n]/in_intensity_blue[_input|_raw] +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + This property is supported by sensors that have a RGBC + sensing mode. This value should be the output from a reading + and if expressed in SI units, should include _input. If this + value is not in SI units (irradiance, uW/mm^2), then it should + include _raw. + +What: /sys/bus/iio/devices/device[n]/in_cct0[_input|_raw] +KernelVersion: 3.4.0 +Contact: linux-iio@vger.kernel.org +Description: + This should return the correlated color temperature from the + light sensor. If it comes back in SI units, it should also + include _input else it should include _raw to signify it is not + in SI units. + diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index e25040173346..44e397705d7f 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -27,6 +27,7 @@ enum iio_chan_type { IIO_TIMESTAMP, IIO_CAPACITANCE, IIO_ALTVOLTAGE, + IIO_CCT, }; enum iio_modifier { @@ -46,6 +47,10 @@ enum iio_modifier { IIO_MOD_LIGHT_IR, IIO_MOD_ROOT_SUM_SQUARED_X_Y, IIO_MOD_SUM_SQUARED_X_Y_Z, + IIO_MOD_LIGHT_CLEAR, + IIO_MOD_LIGHT_RED, + IIO_MOD_LIGHT_GREEN, + IIO_MOD_LIGHT_BLUE, }; #define IIO_VAL_INT 1 -- cgit v1.2.3 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') 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 From e044a651b9b7b1b33d8b7fdb2bb27e443f392083 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 10 Jul 2012 18:05:28 -0700 Subject: ipv4: Fix crashes in fib_rules_tclass(). All paths assume, when CONFIG_IP_MULTIPLE_TABLES is enabled, that any successful call to fib_lookup() will initialize the fib_result->r value to something. We violated that expectation in the new fib_lookup() fast path. Reported-by: Or Gerlitz Tested-by: Eric Dumazet Tested-by: Greg Rose Signed-off-by: David S. Miller --- include/net/ip_fib.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 539c6721f810..000c4674e18e 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -230,6 +230,7 @@ static inline int fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res) { if (!net->ipv4.fib_has_custom_rules) { + res->r = NULL; if (net->ipv4.fib_local && !fib_table_lookup(net->ipv4.fib_local, flp, res, FIB_LOOKUP_NOREF)) -- cgit v1.2.3 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') 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 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') 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 From 815e972110052e8da68b5b5298ca2cd69cb7c3c0 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 1 Jul 2012 19:19:43 +0200 Subject: ARM: AT91SAM9G45: add crypto peripherals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nicolas Royer Acked-by: Nicolas Ferre Acked-by: Eric Bénard Tested-by: Eric Bénard Signed-off-by: Herbert Xu --- arch/arm/mach-at91/at91sam9g45.c | 13 ++- arch/arm/mach-at91/at91sam9g45_devices.c | 128 ++++++++++++++++++++++++++ arch/arm/mach-at91/include/mach/at91sam9g45.h | 2 + include/linux/platform_data/atmel-aes.h | 22 +++++ 4 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 include/linux/platform_data/atmel-aes.h (limited to 'include') diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index 4792682d52b9..da6dc0f29657 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -182,6 +182,13 @@ static struct clk adc_op_clk = { .rate_hz = 13200000, }; +/* AES/TDES/SHA clock - Only for sam9m11/sam9g56 */ +static struct clk aestdessha_clk = { + .name = "aestdessha_clk", + .pmc_mask = 1 << AT91SAM9G45_ID_AESTDESSHA, + .type = CLK_TYPE_PERIPHERAL, +}; + static struct clk *periph_clocks[] __initdata = { &pioA_clk, &pioB_clk, @@ -211,6 +218,7 @@ static struct clk *periph_clocks[] __initdata = { &udphs_clk, &mmc1_clk, &adc_op_clk, + &aestdessha_clk, // irq0 }; @@ -231,6 +239,9 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk), CLKDEV_CON_DEV_ID(NULL, "atmel-trng", &trng_clk), + CLKDEV_CON_DEV_ID(NULL, "atmel_sha", &aestdessha_clk), + CLKDEV_CON_DEV_ID(NULL, "atmel_tdes", &aestdessha_clk), + CLKDEV_CON_DEV_ID(NULL, "atmel_aes", &aestdessha_clk), /* more usart lookup table for DT entries */ CLKDEV_CON_DEV_ID("usart", "ffffee00.serial", &mck), CLKDEV_CON_DEV_ID("usart", "fff8c000.serial", &usart0_clk), @@ -387,7 +398,7 @@ static unsigned int at91sam9g45_default_irq_priority[NR_AIC_IRQS] __initdata = { 3, /* Ethernet */ 0, /* Image Sensor Interface */ 2, /* USB Device High speed port */ - 0, + 0, /* AESTDESSHA Crypto HW Accelerators */ 0, /* Multimedia Card Interface 1 */ 0, 0, /* Advanced Interrupt Controller (IRQ0) */ diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c index 933fc9afe7d0..7102f62b64ef 100644 --- a/arch/arm/mach-at91/at91sam9g45_devices.c +++ b/arch/arm/mach-at91/at91sam9g45_devices.c @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -1830,6 +1831,130 @@ void __init at91_register_uart(unsigned id, unsigned portnr, unsigned pins) {} void __init at91_add_device_serial(void) {} #endif +/* -------------------------------------------------------------------- + * SHA1/SHA256 + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_CRYPTO_DEV_ATMEL_SHA) || defined(CONFIG_CRYPTO_DEV_ATMEL_SHA_MODULE) +static struct resource sha_resources[] = { + { + .start = AT91SAM9G45_BASE_SHA, + .end = AT91SAM9G45_BASE_SHA + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9G45_ID_AESTDESSHA, + .end = AT91SAM9G45_ID_AESTDESSHA, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91sam9g45_sha_device = { + .name = "atmel_sha", + .id = -1, + .resource = sha_resources, + .num_resources = ARRAY_SIZE(sha_resources), +}; + +static void __init at91_add_device_sha(void) +{ + platform_device_register(&at91sam9g45_sha_device); +} +#else +static void __init at91_add_device_sha(void) {} +#endif + +/* -------------------------------------------------------------------- + * DES/TDES + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_CRYPTO_DEV_ATMEL_TDES) || defined(CONFIG_CRYPTO_DEV_ATMEL_TDES_MODULE) +static struct resource tdes_resources[] = { + [0] = { + .start = AT91SAM9G45_BASE_TDES, + .end = AT91SAM9G45_BASE_TDES + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9G45_ID_AESTDESSHA, + .end = AT91SAM9G45_ID_AESTDESSHA, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91sam9g45_tdes_device = { + .name = "atmel_tdes", + .id = -1, + .resource = tdes_resources, + .num_resources = ARRAY_SIZE(tdes_resources), +}; + +static void __init at91_add_device_tdes(void) +{ + platform_device_register(&at91sam9g45_tdes_device); +} +#else +static void __init at91_add_device_tdes(void) {} +#endif + +/* -------------------------------------------------------------------- + * AES + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_CRYPTO_DEV_ATMEL_AES) || defined(CONFIG_CRYPTO_DEV_ATMEL_AES_MODULE) +static struct aes_platform_data aes_data; +static u64 aes_dmamask = DMA_BIT_MASK(32); + +static struct resource aes_resources[] = { + [0] = { + .start = AT91SAM9G45_BASE_AES, + .end = AT91SAM9G45_BASE_AES + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9G45_ID_AESTDESSHA, + .end = AT91SAM9G45_ID_AESTDESSHA, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91sam9g45_aes_device = { + .name = "atmel_aes", + .id = -1, + .dev = { + .dma_mask = &aes_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = &aes_data, + }, + .resource = aes_resources, + .num_resources = ARRAY_SIZE(aes_resources), +}; + +static void __init at91_add_device_aes(void) +{ + struct at_dma_slave *atslave; + struct aes_dma_data *alt_atslave; + + alt_atslave = kzalloc(sizeof(struct aes_dma_data), GFP_KERNEL); + + /* DMA TX slave channel configuration */ + atslave = &alt_atslave->txdata; + atslave->dma_dev = &at_hdmac_device.dev; + atslave->cfg = ATC_FIFOCFG_ENOUGHSPACE | ATC_SRC_H2SEL_HW | + ATC_SRC_PER(AT_DMA_ID_AES_RX); + + /* DMA RX slave channel configuration */ + atslave = &alt_atslave->rxdata; + atslave->dma_dev = &at_hdmac_device.dev; + atslave->cfg = ATC_FIFOCFG_ENOUGHSPACE | ATC_DST_H2SEL_HW | + ATC_DST_PER(AT_DMA_ID_AES_TX); + + aes_data.dma_slave = alt_atslave; + platform_device_register(&at91sam9g45_aes_device); +} +#else +static void __init at91_add_device_aes(void) {} +#endif /* -------------------------------------------------------------------- */ /* @@ -1847,6 +1972,9 @@ static int __init at91_add_standard_devices(void) at91_add_device_trng(); at91_add_device_watchdog(); at91_add_device_tc(); + at91_add_device_sha(); + at91_add_device_tdes(); + at91_add_device_aes(); return 0; } diff --git a/arch/arm/mach-at91/include/mach/at91sam9g45.h b/arch/arm/mach-at91/include/mach/at91sam9g45.h index 3a4da24d5911..8eba1021f533 100644 --- a/arch/arm/mach-at91/include/mach/at91sam9g45.h +++ b/arch/arm/mach-at91/include/mach/at91sam9g45.h @@ -136,6 +136,8 @@ #define AT_DMA_ID_SSC1_RX 8 #define AT_DMA_ID_AC97_TX 9 #define AT_DMA_ID_AC97_RX 10 +#define AT_DMA_ID_AES_TX 11 +#define AT_DMA_ID_AES_RX 12 #define AT_DMA_ID_MCI1 13 #endif diff --git a/include/linux/platform_data/atmel-aes.h b/include/linux/platform_data/atmel-aes.h new file mode 100644 index 000000000000..e7a1949bad26 --- /dev/null +++ b/include/linux/platform_data/atmel-aes.h @@ -0,0 +1,22 @@ +#ifndef __LINUX_ATMEL_AES_H +#define __LINUX_ATMEL_AES_H + +#include + +/** + * struct aes_dma_data - DMA data for AES + */ +struct aes_dma_data { + struct at_dma_slave txdata; + struct at_dma_slave rxdata; +}; + +/** + * struct aes_platform_data - board-specific AES configuration + * @dma_slave: DMA slave interface to use in data transfers. + */ +struct aes_platform_data { + struct aes_dma_data *dma_slave; +}; + +#endif /* __LINUX_ATMEL_AES_H */ -- cgit v1.2.3 From 4aabd8ef8c43677cfee3e1e36c5a79edddb41942 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 9 Jul 2012 16:07:30 -0700 Subject: tcp: Move dynamnic metrics handling into seperate file. Signed-off-by: David S. Miller --- include/net/tcp.h | 4 ++ net/ipv4/Makefile | 2 +- net/ipv4/tcp_input.c | 188 +----------------------------------------------- net/ipv4/tcp_metrics.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 199 insertions(+), 187 deletions(-) create mode 100644 net/ipv4/tcp_metrics.c (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 53fb7d814170..98ca797001a2 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -388,6 +388,8 @@ extern void tcp_enter_frto(struct sock *sk); extern void tcp_enter_loss(struct sock *sk, int how); extern void tcp_clear_retrans(struct tcp_sock *tp); extern void tcp_update_metrics(struct sock *sk); +extern void tcp_init_metrics(struct sock *sk); +extern void tcp_disable_fack(struct tcp_sock *tp); extern void tcp_close(struct sock *sk, long timeout); extern void tcp_init_sock(struct sock *sk); extern unsigned int tcp_poll(struct file * file, struct socket *sock, @@ -556,6 +558,8 @@ static inline u32 __tcp_set_rto(const struct tcp_sock *tp) return (tp->srtt >> 3) + tp->rttvar; } +extern void tcp_set_rto(struct sock *sk); + static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd) { tp->pred_flags = htonl((tp->tcp_header_len << 26) | diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index ff75d3bbcd6a..5a23e8b37106 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -7,7 +7,7 @@ obj-y := route.o inetpeer.o protocol.o \ ip_output.o ip_sockglue.o inet_hashtables.o \ inet_timewait_sock.o inet_connection_sock.o \ tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o \ - tcp_minisocks.o tcp_cong.o \ + tcp_minisocks.o tcp_cong.o tcp_metrics.o \ datagram.o raw.o udp.o udplite.o \ arp.o icmp.o devinet.o af_inet.o igmp.o \ fib_frontend.o fib_semantics.o fib_trie.o \ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index ca0d0e7c9778..055ac49b8b40 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -93,7 +93,6 @@ int sysctl_tcp_rfc1337 __read_mostly; int sysctl_tcp_max_orphans __read_mostly = NR_FILE; int sysctl_tcp_frto __read_mostly = 2; int sysctl_tcp_frto_response __read_mostly; -int sysctl_tcp_nometrics_save __read_mostly; int sysctl_tcp_thin_dupack __read_mostly; @@ -701,7 +700,7 @@ static void tcp_rtt_estimator(struct sock *sk, const __u32 mrtt) /* Calculate rto without backoff. This is the second half of Van Jacobson's * routine referred to above. */ -static inline void tcp_set_rto(struct sock *sk) +void tcp_set_rto(struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); /* Old crap is replaced with new one. 8) @@ -728,109 +727,6 @@ static inline void tcp_set_rto(struct sock *sk) tcp_bound_rto(sk); } -/* Save metrics learned by this TCP session. - This function is called only, when TCP finishes successfully - i.e. when it enters TIME-WAIT or goes from LAST-ACK to CLOSE. - */ -void tcp_update_metrics(struct sock *sk) -{ - struct tcp_sock *tp = tcp_sk(sk); - struct dst_entry *dst = __sk_dst_get(sk); - - if (sysctl_tcp_nometrics_save) - return; - - if (dst && (dst->flags & DST_HOST)) { - const struct inet_connection_sock *icsk = inet_csk(sk); - int m; - unsigned long rtt; - - dst_confirm(dst); - - if (icsk->icsk_backoff || !tp->srtt) { - /* This session failed to estimate rtt. Why? - * Probably, no packets returned in time. - * Reset our results. - */ - if (!(dst_metric_locked(dst, RTAX_RTT))) - dst_metric_set(dst, RTAX_RTT, 0); - return; - } - - rtt = dst_metric_rtt(dst, RTAX_RTT); - m = rtt - tp->srtt; - - /* If newly calculated rtt larger than stored one, - * store new one. Otherwise, use EWMA. Remember, - * rtt overestimation is always better than underestimation. - */ - if (!(dst_metric_locked(dst, RTAX_RTT))) { - if (m <= 0) - set_dst_metric_rtt(dst, RTAX_RTT, tp->srtt); - else - set_dst_metric_rtt(dst, RTAX_RTT, rtt - (m >> 3)); - } - - if (!(dst_metric_locked(dst, RTAX_RTTVAR))) { - unsigned long var; - if (m < 0) - m = -m; - - /* Scale deviation to rttvar fixed point */ - m >>= 1; - if (m < tp->mdev) - m = tp->mdev; - - var = dst_metric_rtt(dst, RTAX_RTTVAR); - if (m >= var) - var = m; - else - var -= (var - m) >> 2; - - set_dst_metric_rtt(dst, RTAX_RTTVAR, var); - } - - if (tcp_in_initial_slowstart(tp)) { - /* Slow start still did not finish. */ - if (dst_metric(dst, RTAX_SSTHRESH) && - !dst_metric_locked(dst, RTAX_SSTHRESH) && - (tp->snd_cwnd >> 1) > dst_metric(dst, RTAX_SSTHRESH)) - dst_metric_set(dst, RTAX_SSTHRESH, tp->snd_cwnd >> 1); - if (!dst_metric_locked(dst, RTAX_CWND) && - tp->snd_cwnd > dst_metric(dst, RTAX_CWND)) - dst_metric_set(dst, RTAX_CWND, tp->snd_cwnd); - } else if (tp->snd_cwnd > tp->snd_ssthresh && - icsk->icsk_ca_state == TCP_CA_Open) { - /* Cong. avoidance phase, cwnd is reliable. */ - if (!dst_metric_locked(dst, RTAX_SSTHRESH)) - dst_metric_set(dst, RTAX_SSTHRESH, - max(tp->snd_cwnd >> 1, tp->snd_ssthresh)); - if (!dst_metric_locked(dst, RTAX_CWND)) - dst_metric_set(dst, RTAX_CWND, - (dst_metric(dst, RTAX_CWND) + - tp->snd_cwnd) >> 1); - } else { - /* Else slow start did not finish, cwnd is non-sense, - ssthresh may be also invalid. - */ - if (!dst_metric_locked(dst, RTAX_CWND)) - dst_metric_set(dst, RTAX_CWND, - (dst_metric(dst, RTAX_CWND) + - tp->snd_ssthresh) >> 1); - if (dst_metric(dst, RTAX_SSTHRESH) && - !dst_metric_locked(dst, RTAX_SSTHRESH) && - tp->snd_ssthresh > dst_metric(dst, RTAX_SSTHRESH)) - dst_metric_set(dst, RTAX_SSTHRESH, tp->snd_ssthresh); - } - - if (!dst_metric_locked(dst, RTAX_REORDERING)) { - if (dst_metric(dst, RTAX_REORDERING) < tp->reordering && - tp->reordering != sysctl_tcp_reordering) - dst_metric_set(dst, RTAX_REORDERING, tp->reordering); - } - } -} - __u32 tcp_init_cwnd(const struct tcp_sock *tp, const struct dst_entry *dst) { __u32 cwnd = (dst ? dst_metric(dst, RTAX_INITCWND) : 0); @@ -867,7 +763,7 @@ void tcp_enter_cwr(struct sock *sk, const int set_ssthresh) * Packet counting of FACK is based on in-order assumptions, therefore TCP * disables it when reordering is detected */ -static void tcp_disable_fack(struct tcp_sock *tp) +void tcp_disable_fack(struct tcp_sock *tp) { /* RFC3517 uses different metric in lost marker => reset on change */ if (tcp_is_fack(tp)) @@ -881,86 +777,6 @@ static void tcp_dsack_seen(struct tcp_sock *tp) tp->rx_opt.sack_ok |= TCP_DSACK_SEEN; } -/* Initialize metrics on socket. */ - -static void tcp_init_metrics(struct sock *sk) -{ - struct tcp_sock *tp = tcp_sk(sk); - struct dst_entry *dst = __sk_dst_get(sk); - - if (dst == NULL) - goto reset; - - dst_confirm(dst); - - if (dst_metric_locked(dst, RTAX_CWND)) - tp->snd_cwnd_clamp = dst_metric(dst, RTAX_CWND); - if (dst_metric(dst, RTAX_SSTHRESH)) { - tp->snd_ssthresh = dst_metric(dst, RTAX_SSTHRESH); - if (tp->snd_ssthresh > tp->snd_cwnd_clamp) - tp->snd_ssthresh = tp->snd_cwnd_clamp; - } else { - /* ssthresh may have been reduced unnecessarily during. - * 3WHS. Restore it back to its initial default. - */ - tp->snd_ssthresh = TCP_INFINITE_SSTHRESH; - } - if (dst_metric(dst, RTAX_REORDERING) && - tp->reordering != dst_metric(dst, RTAX_REORDERING)) { - tcp_disable_fack(tp); - tcp_disable_early_retrans(tp); - tp->reordering = dst_metric(dst, RTAX_REORDERING); - } - - if (dst_metric(dst, RTAX_RTT) == 0 || tp->srtt == 0) - goto reset; - - /* Initial rtt is determined from SYN,SYN-ACK. - * The segment is small and rtt may appear much - * less than real one. Use per-dst memory - * to make it more realistic. - * - * A bit of theory. RTT is time passed after "normal" sized packet - * is sent until it is ACKed. In normal circumstances sending small - * packets force peer to delay ACKs and calculation is correct too. - * The algorithm is adaptive and, provided we follow specs, it - * NEVER underestimate RTT. BUT! If peer tries to make some clever - * tricks sort of "quick acks" for time long enough to decrease RTT - * to low value, and then abruptly stops to do it and starts to delay - * ACKs, wait for troubles. - */ - if (dst_metric_rtt(dst, RTAX_RTT) > tp->srtt) { - tp->srtt = dst_metric_rtt(dst, RTAX_RTT); - tp->rtt_seq = tp->snd_nxt; - } - if (dst_metric_rtt(dst, RTAX_RTTVAR) > tp->mdev) { - tp->mdev = dst_metric_rtt(dst, RTAX_RTTVAR); - tp->mdev_max = tp->rttvar = max(tp->mdev, tcp_rto_min(sk)); - } - tcp_set_rto(sk); -reset: - if (tp->srtt == 0) { - /* RFC6298: 5.7 We've failed to get a valid RTT sample from - * 3WHS. This is most likely due to retransmission, - * including spurious one. Reset the RTO back to 3secs - * from the more aggressive 1sec to avoid more spurious - * retransmission. - */ - tp->mdev = tp->mdev_max = tp->rttvar = TCP_TIMEOUT_FALLBACK; - inet_csk(sk)->icsk_rto = TCP_TIMEOUT_FALLBACK; - } - /* Cut cwnd down to 1 per RFC5681 if SYN or SYN-ACK has been - * retransmitted. In light of RFC6298 more aggressive 1sec - * initRTO, we only reset cwnd when more than 1 SYN/SYN-ACK - * retransmission has occurred. - */ - if (tp->total_retrans > 1) - tp->snd_cwnd = 1; - else - tp->snd_cwnd = tcp_init_cwnd(tp, dst); - tp->snd_cwnd_stamp = tcp_time_stamp; -} - static void tcp_update_reordering(struct sock *sk, const int metric, const int ts) { diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c new file mode 100644 index 000000000000..2793ecf928d3 --- /dev/null +++ b/net/ipv4/tcp_metrics.c @@ -0,0 +1,192 @@ +#include +#include + +#include +#include +#include +#include + +int sysctl_tcp_nometrics_save __read_mostly; + +/* Save metrics learned by this TCP session. This function is called + * only, when TCP finishes successfully i.e. when it enters TIME-WAIT + * or goes from LAST-ACK to CLOSE. + */ +void tcp_update_metrics(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct dst_entry *dst = __sk_dst_get(sk); + + if (sysctl_tcp_nometrics_save) + return; + + if (dst && (dst->flags & DST_HOST)) { + const struct inet_connection_sock *icsk = inet_csk(sk); + int m; + unsigned long rtt; + + dst_confirm(dst); + + if (icsk->icsk_backoff || !tp->srtt) { + /* This session failed to estimate rtt. Why? + * Probably, no packets returned in time. + * Reset our results. + */ + if (!(dst_metric_locked(dst, RTAX_RTT))) + dst_metric_set(dst, RTAX_RTT, 0); + return; + } + + rtt = dst_metric_rtt(dst, RTAX_RTT); + m = rtt - tp->srtt; + + /* If newly calculated rtt larger than stored one, + * store new one. Otherwise, use EWMA. Remember, + * rtt overestimation is always better than underestimation. + */ + if (!(dst_metric_locked(dst, RTAX_RTT))) { + if (m <= 0) + set_dst_metric_rtt(dst, RTAX_RTT, tp->srtt); + else + set_dst_metric_rtt(dst, RTAX_RTT, rtt - (m >> 3)); + } + + if (!(dst_metric_locked(dst, RTAX_RTTVAR))) { + unsigned long var; + if (m < 0) + m = -m; + + /* Scale deviation to rttvar fixed point */ + m >>= 1; + if (m < tp->mdev) + m = tp->mdev; + + var = dst_metric_rtt(dst, RTAX_RTTVAR); + if (m >= var) + var = m; + else + var -= (var - m) >> 2; + + set_dst_metric_rtt(dst, RTAX_RTTVAR, var); + } + + if (tcp_in_initial_slowstart(tp)) { + /* Slow start still did not finish. */ + if (dst_metric(dst, RTAX_SSTHRESH) && + !dst_metric_locked(dst, RTAX_SSTHRESH) && + (tp->snd_cwnd >> 1) > dst_metric(dst, RTAX_SSTHRESH)) + dst_metric_set(dst, RTAX_SSTHRESH, tp->snd_cwnd >> 1); + if (!dst_metric_locked(dst, RTAX_CWND) && + tp->snd_cwnd > dst_metric(dst, RTAX_CWND)) + dst_metric_set(dst, RTAX_CWND, tp->snd_cwnd); + } else if (tp->snd_cwnd > tp->snd_ssthresh && + icsk->icsk_ca_state == TCP_CA_Open) { + /* Cong. avoidance phase, cwnd is reliable. */ + if (!dst_metric_locked(dst, RTAX_SSTHRESH)) + dst_metric_set(dst, RTAX_SSTHRESH, + max(tp->snd_cwnd >> 1, tp->snd_ssthresh)); + if (!dst_metric_locked(dst, RTAX_CWND)) + dst_metric_set(dst, RTAX_CWND, + (dst_metric(dst, RTAX_CWND) + + tp->snd_cwnd) >> 1); + } else { + /* Else slow start did not finish, cwnd is non-sense, + ssthresh may be also invalid. + */ + if (!dst_metric_locked(dst, RTAX_CWND)) + dst_metric_set(dst, RTAX_CWND, + (dst_metric(dst, RTAX_CWND) + + tp->snd_ssthresh) >> 1); + if (dst_metric(dst, RTAX_SSTHRESH) && + !dst_metric_locked(dst, RTAX_SSTHRESH) && + tp->snd_ssthresh > dst_metric(dst, RTAX_SSTHRESH)) + dst_metric_set(dst, RTAX_SSTHRESH, tp->snd_ssthresh); + } + + if (!dst_metric_locked(dst, RTAX_REORDERING)) { + if (dst_metric(dst, RTAX_REORDERING) < tp->reordering && + tp->reordering != sysctl_tcp_reordering) + dst_metric_set(dst, RTAX_REORDERING, tp->reordering); + } + } +} + +/* Initialize metrics on socket. */ + +void tcp_init_metrics(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct dst_entry *dst = __sk_dst_get(sk); + + if (dst == NULL) + goto reset; + + dst_confirm(dst); + + if (dst_metric_locked(dst, RTAX_CWND)) + tp->snd_cwnd_clamp = dst_metric(dst, RTAX_CWND); + if (dst_metric(dst, RTAX_SSTHRESH)) { + tp->snd_ssthresh = dst_metric(dst, RTAX_SSTHRESH); + if (tp->snd_ssthresh > tp->snd_cwnd_clamp) + tp->snd_ssthresh = tp->snd_cwnd_clamp; + } else { + /* ssthresh may have been reduced unnecessarily during. + * 3WHS. Restore it back to its initial default. + */ + tp->snd_ssthresh = TCP_INFINITE_SSTHRESH; + } + if (dst_metric(dst, RTAX_REORDERING) && + tp->reordering != dst_metric(dst, RTAX_REORDERING)) { + tcp_disable_fack(tp); + tcp_disable_early_retrans(tp); + tp->reordering = dst_metric(dst, RTAX_REORDERING); + } + + if (dst_metric(dst, RTAX_RTT) == 0 || tp->srtt == 0) + goto reset; + + /* Initial rtt is determined from SYN,SYN-ACK. + * The segment is small and rtt may appear much + * less than real one. Use per-dst memory + * to make it more realistic. + * + * A bit of theory. RTT is time passed after "normal" sized packet + * is sent until it is ACKed. In normal circumstances sending small + * packets force peer to delay ACKs and calculation is correct too. + * The algorithm is adaptive and, provided we follow specs, it + * NEVER underestimate RTT. BUT! If peer tries to make some clever + * tricks sort of "quick acks" for time long enough to decrease RTT + * to low value, and then abruptly stops to do it and starts to delay + * ACKs, wait for troubles. + */ + if (dst_metric_rtt(dst, RTAX_RTT) > tp->srtt) { + tp->srtt = dst_metric_rtt(dst, RTAX_RTT); + tp->rtt_seq = tp->snd_nxt; + } + if (dst_metric_rtt(dst, RTAX_RTTVAR) > tp->mdev) { + tp->mdev = dst_metric_rtt(dst, RTAX_RTTVAR); + tp->mdev_max = tp->rttvar = max(tp->mdev, tcp_rto_min(sk)); + } + tcp_set_rto(sk); +reset: + if (tp->srtt == 0) { + /* RFC6298: 5.7 We've failed to get a valid RTT sample from + * 3WHS. This is most likely due to retransmission, + * including spurious one. Reset the RTO back to 3secs + * from the more aggressive 1sec to avoid more spurious + * retransmission. + */ + tp->mdev = tp->mdev_max = tp->rttvar = TCP_TIMEOUT_FALLBACK; + inet_csk(sk)->icsk_rto = TCP_TIMEOUT_FALLBACK; + } + /* Cut cwnd down to 1 per RFC5681 if SYN or SYN-ACK has been + * retransmitted. In light of RFC6298 more aggressive 1sec + * initRTO, we only reset cwnd when more than 1 SYN/SYN-ACK + * retransmission has occurred. + */ + if (tp->total_retrans > 1) + tp->snd_cwnd = 1; + else + tp->snd_cwnd = tcp_init_cwnd(tp, dst); + tp->snd_cwnd_stamp = tcp_time_stamp; +} -- cgit v1.2.3 From ab92bb2f679d66c7e12a6b1c0cdd76fe308f6546 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 9 Jul 2012 16:19:30 -0700 Subject: tcp: Abstract back handling peer aliveness test into helper function. Signed-off-by: David S. Miller --- include/net/tcp.h | 1 + net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/tcp_metrics.c | 10 ++++++++++ net/ipv6/tcp_ipv6.c | 2 +- 4 files changed, 13 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 98ca797001a2..5478356ea8c5 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -389,6 +389,7 @@ extern void tcp_enter_loss(struct sock *sk, int how); extern void tcp_clear_retrans(struct tcp_sock *tp); extern void tcp_update_metrics(struct sock *sk); extern void tcp_init_metrics(struct sock *sk); +extern bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst); extern void tcp_disable_fack(struct tcp_sock *tp); extern void tcp_close(struct sock *sk, long timeout); extern void tcp_init_sock(struct sock *sk); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 64568fa21d05..e9312a8f33a1 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1405,7 +1405,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) < (sysctl_max_syn_backlog >> 2)) && (!peer || !peer->tcp_ts_stamp) && - (!dst || !dst_metric(dst, RTAX_RTT))) { + !tcp_peer_is_proven(req, dst)) { /* Without syncookies last quarter of * backlog is filled with destinations, * proven to be alive. diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 2793ecf928d3..9afe703c85cc 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -1,7 +1,9 @@ +#include #include #include #include +#include #include #include #include @@ -190,3 +192,11 @@ reset: tp->snd_cwnd = tcp_init_cwnd(tp, dst); tp->snd_cwnd_stamp = tcp_time_stamp; } + +bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst) +{ + if (!dst) + return false; + return dst_metric(dst, RTAX_RTT) ? true : false; +} +EXPORT_SYMBOL_GPL(tcp_peer_is_proven); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 6cc67ed6c2e6..75d179555c28 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1177,7 +1177,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) < (sysctl_max_syn_backlog >> 2)) && (!peer || !peer->tcp_ts_stamp) && - (!dst || !dst_metric(dst, RTAX_RTT))) { + !tcp_peer_is_proven(req, dst)) { /* Without syncookies last quarter of * backlog is filled with destinations, * proven to be alive. -- cgit v1.2.3 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') 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 From 51c5d0c4b169bf762f09e0d5b283a7f0b2a45739 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 10 Jul 2012 00:49:14 -0700 Subject: tcp: Maintain dynamic metrics in local cache. Maintain a local hash table of TCP dynamic metrics blobs. Computed TCP metrics are no longer maintained in the route metrics. The table uses RCU and an extremely simple hash so that it has low latency and low overhead. A simple hash is legitimate because we only make metrics blobs for fully established connections. Some tweaking of the default hash table sizes, metric timeouts, and the hash chain length limit certainly could use some tweaking. But the basic design seems sound. With help from Eric Dumazet and Joe Perches. Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 3 + include/net/tcp.h | 1 + net/ipv4/tcp.c | 2 + net/ipv4/tcp_metrics.c | 555 +++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 468 insertions(+), 93 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 599e48fa97cb..2e089a99d603 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -7,6 +7,7 @@ #include +struct tcpm_hash_bucket; struct ctl_table_header; struct ipv4_devconf; struct fib_rules_ops; @@ -39,6 +40,8 @@ struct netns_ipv4 { struct sock **icmp_sk; struct sock *tcp_sock; struct inet_peer_base *peers; + struct tcpm_hash_bucket *tcp_metrics_hash; + unsigned int tcp_metrics_hash_mask; struct netns_frags frags; #ifdef CONFIG_NETFILTER struct xt_table *iptable_filter; diff --git a/include/net/tcp.h b/include/net/tcp.h index 5478356ea8c5..0900d63d1627 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -389,6 +389,7 @@ extern void tcp_enter_loss(struct sock *sk, int how); extern void tcp_clear_retrans(struct tcp_sock *tp); extern void tcp_update_metrics(struct sock *sk); extern void tcp_init_metrics(struct sock *sk); +extern void tcp_metrics_init(void); extern bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst); extern void tcp_disable_fack(struct tcp_sock *tp); extern void tcp_close(struct sock *sk, long timeout); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 3ba605f60e4e..29aa0c800cd0 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3563,6 +3563,8 @@ void __init tcp_init(void) pr_info("Hash tables configured (established %u bind %u)\n", tcp_hashinfo.ehash_mask + 1, tcp_hashinfo.bhash_size); + tcp_metrics_init(); + tcp_register_congestion_control(&tcp_reno); memset(&tcp_secret_one.secrets[0], 0, sizeof(tcp_secret_one.secrets)); diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 9afe703c85cc..56223bab251b 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -1,134 +1,431 @@ +#include +#include +#include +#include #include #include +#include +#include #include #include +#include #include +#include #include +#include #include #include int sysctl_tcp_nometrics_save __read_mostly; +enum tcp_metric_index { + TCP_METRIC_RTT, + TCP_METRIC_RTTVAR, + TCP_METRIC_SSTHRESH, + TCP_METRIC_CWND, + TCP_METRIC_REORDERING, + + /* Always last. */ + TCP_METRIC_MAX, +}; + +struct tcp_metrics_block { + struct tcp_metrics_block __rcu *tcpm_next; + struct inetpeer_addr tcpm_addr; + unsigned long tcpm_stamp; + u32 tcpm_lock; + u32 tcpm_vals[TCP_METRIC_MAX]; +}; + +static bool tcp_metric_locked(struct tcp_metrics_block *tm, + enum tcp_metric_index idx) +{ + return tm->tcpm_lock & (1 << idx); +} + +static u32 tcp_metric_get(struct tcp_metrics_block *tm, + enum tcp_metric_index idx) +{ + return tm->tcpm_vals[idx]; +} + +static u32 tcp_metric_get_jiffies(struct tcp_metrics_block *tm, + enum tcp_metric_index idx) +{ + return msecs_to_jiffies(tm->tcpm_vals[idx]); +} + +static void tcp_metric_set(struct tcp_metrics_block *tm, + enum tcp_metric_index idx, + u32 val) +{ + tm->tcpm_vals[idx] = val; +} + +static void tcp_metric_set_msecs(struct tcp_metrics_block *tm, + enum tcp_metric_index idx, + u32 val) +{ + tm->tcpm_vals[idx] = jiffies_to_msecs(val); +} + +static bool addr_same(const struct inetpeer_addr *a, + const struct inetpeer_addr *b) +{ + const struct in6_addr *a6, *b6; + + if (a->family != b->family) + return false; + if (a->family == AF_INET) + return a->addr.a4 == b->addr.a4; + + a6 = (const struct in6_addr *) &a->addr.a6[0]; + b6 = (const struct in6_addr *) &b->addr.a6[0]; + + return ipv6_addr_equal(a6, b6); +} + +struct tcpm_hash_bucket { + struct tcp_metrics_block __rcu *chain; +}; + +static DEFINE_SPINLOCK(tcp_metrics_lock); + +static void tcpm_suck_dst(struct tcp_metrics_block *tm, struct dst_entry *dst) +{ + u32 val; + + val = 0; + if (dst_metric_locked(dst, RTAX_RTT)) + val |= 1 << TCP_METRIC_RTT; + if (dst_metric_locked(dst, RTAX_RTTVAR)) + val |= 1 << TCP_METRIC_RTTVAR; + if (dst_metric_locked(dst, RTAX_SSTHRESH)) + val |= 1 << TCP_METRIC_SSTHRESH; + if (dst_metric_locked(dst, RTAX_CWND)) + val |= 1 << TCP_METRIC_CWND; + if (dst_metric_locked(dst, RTAX_REORDERING)) + val |= 1 << TCP_METRIC_REORDERING; + tm->tcpm_lock = val; + + tm->tcpm_vals[TCP_METRIC_RTT] = dst_metric_raw(dst, RTAX_RTT); + tm->tcpm_vals[TCP_METRIC_RTTVAR] = dst_metric_raw(dst, RTAX_RTTVAR); + tm->tcpm_vals[TCP_METRIC_SSTHRESH] = dst_metric_raw(dst, RTAX_SSTHRESH); + tm->tcpm_vals[TCP_METRIC_CWND] = dst_metric_raw(dst, RTAX_CWND); + tm->tcpm_vals[TCP_METRIC_REORDERING] = dst_metric_raw(dst, RTAX_REORDERING); +} + +static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, + struct inetpeer_addr *addr, + unsigned int hash, + bool reclaim) +{ + struct tcp_metrics_block *tm; + struct net *net; + + spin_lock_bh(&tcp_metrics_lock); + net = dev_net(dst->dev); + if (unlikely(reclaim)) { + struct tcp_metrics_block *oldest; + + oldest = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); + for (tm = rcu_dereference(oldest->tcpm_next); tm; + tm = rcu_dereference(tm->tcpm_next)) { + if (time_before(tm->tcpm_stamp, oldest->tcpm_stamp)) + oldest = tm; + } + tm = oldest; + } else { + tm = kmalloc(sizeof(*tm), GFP_ATOMIC); + if (!tm) + goto out_unlock; + } + tm->tcpm_addr = *addr; + tm->tcpm_stamp = jiffies; + + tcpm_suck_dst(tm, dst); + + if (likely(!reclaim)) { + tm->tcpm_next = net->ipv4.tcp_metrics_hash[hash].chain; + rcu_assign_pointer(net->ipv4.tcp_metrics_hash[hash].chain, tm); + } + +out_unlock: + spin_unlock_bh(&tcp_metrics_lock); + return tm; +} + +#define TCP_METRICS_TIMEOUT (60 * 60 * HZ) + +static void tcpm_check_stamp(struct tcp_metrics_block *tm, struct dst_entry *dst) +{ + if (tm && unlikely(time_after(jiffies, tm->tcpm_stamp + TCP_METRICS_TIMEOUT))) + tcpm_suck_dst(tm, dst); +} + +#define TCP_METRICS_RECLAIM_DEPTH 5 +#define TCP_METRICS_RECLAIM_PTR (struct tcp_metrics_block *) 0x1UL + +static struct tcp_metrics_block *tcp_get_encode(struct tcp_metrics_block *tm, int depth) +{ + if (tm) + return tm; + if (depth > TCP_METRICS_RECLAIM_DEPTH) + return TCP_METRICS_RECLAIM_PTR; + return NULL; +} + +static struct tcp_metrics_block *__tcp_get_metrics(const struct inetpeer_addr *addr, + struct net *net, unsigned int hash) +{ + struct tcp_metrics_block *tm; + int depth = 0; + + for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; + tm = rcu_dereference(tm->tcpm_next)) { + if (addr_same(&tm->tcpm_addr, addr)) + break; + depth++; + } + return tcp_get_encode(tm, depth); +} + +static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, + struct dst_entry *dst) +{ + struct tcp_metrics_block *tm; + struct inetpeer_addr addr; + unsigned int hash; + struct net *net; + + addr.family = req->rsk_ops->family; + switch (addr.family) { + case AF_INET: + addr.addr.a4 = inet_rsk(req)->rmt_addr; + hash = (__force unsigned int) addr.addr.a4; + break; + case AF_INET6: + *(struct in6_addr *)addr.addr.a6 = inet6_rsk(req)->rmt_addr; + hash = ((__force unsigned int) addr.addr.a6[0] ^ + (__force unsigned int) addr.addr.a6[1] ^ + (__force unsigned int) addr.addr.a6[2] ^ + (__force unsigned int) addr.addr.a6[3]); + break; + default: + return NULL; + } + + hash ^= (hash >> 24) ^ (hash >> 16) ^ (hash >> 8); + + net = dev_net(dst->dev); + hash &= net->ipv4.tcp_metrics_hash_mask; + + for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; + tm = rcu_dereference(tm->tcpm_next)) { + if (addr_same(&tm->tcpm_addr, &addr)) + break; + } + tcpm_check_stamp(tm, dst); + return tm; +} + +static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, + struct dst_entry *dst, + bool create) +{ + struct tcp_metrics_block *tm; + struct inetpeer_addr addr; + unsigned int hash; + struct net *net; + bool reclaim; + + addr.family = sk->sk_family; + switch (addr.family) { + case AF_INET: + addr.addr.a4 = inet_sk(sk)->inet_daddr; + hash = (__force unsigned int) addr.addr.a4; + break; + case AF_INET6: + *(struct in6_addr *)addr.addr.a6 = inet6_sk(sk)->daddr; + hash = ((__force unsigned int) addr.addr.a6[0] ^ + (__force unsigned int) addr.addr.a6[1] ^ + (__force unsigned int) addr.addr.a6[2] ^ + (__force unsigned int) addr.addr.a6[3]); + break; + default: + return NULL; + } + + hash ^= (hash >> 24) ^ (hash >> 16) ^ (hash >> 8); + + net = dev_net(dst->dev); + hash &= net->ipv4.tcp_metrics_hash_mask; + + tm = __tcp_get_metrics(&addr, net, hash); + reclaim = false; + if (tm == TCP_METRICS_RECLAIM_PTR) { + reclaim = true; + tm = NULL; + } + if (!tm && create) + tm = tcpm_new(dst, &addr, hash, reclaim); + else + tcpm_check_stamp(tm, dst); + + return tm; +} + /* Save metrics learned by this TCP session. This function is called * only, when TCP finishes successfully i.e. when it enters TIME-WAIT * or goes from LAST-ACK to CLOSE. */ void tcp_update_metrics(struct sock *sk) { - struct tcp_sock *tp = tcp_sk(sk); + const struct inet_connection_sock *icsk = inet_csk(sk); struct dst_entry *dst = __sk_dst_get(sk); + struct tcp_sock *tp = tcp_sk(sk); + struct tcp_metrics_block *tm; + unsigned long rtt; + u32 val; + int m; - if (sysctl_tcp_nometrics_save) + if (sysctl_tcp_nometrics_save || !dst) return; - if (dst && (dst->flags & DST_HOST)) { - const struct inet_connection_sock *icsk = inet_csk(sk); - int m; - unsigned long rtt; - + if (dst->flags & DST_HOST) dst_confirm(dst); - if (icsk->icsk_backoff || !tp->srtt) { - /* This session failed to estimate rtt. Why? - * Probably, no packets returned in time. - * Reset our results. - */ - if (!(dst_metric_locked(dst, RTAX_RTT))) - dst_metric_set(dst, RTAX_RTT, 0); - return; - } + rcu_read_lock(); + if (icsk->icsk_backoff || !tp->srtt) { + /* This session failed to estimate rtt. Why? + * Probably, no packets returned in time. Reset our + * results. + */ + tm = tcp_get_metrics(sk, dst, false); + if (tm && !tcp_metric_locked(tm, TCP_METRIC_RTT)) + tcp_metric_set(tm, TCP_METRIC_RTT, 0); + goto out_unlock; + } else + tm = tcp_get_metrics(sk, dst, true); - rtt = dst_metric_rtt(dst, RTAX_RTT); - m = rtt - tp->srtt; + if (!tm) + goto out_unlock; - /* If newly calculated rtt larger than stored one, - * store new one. Otherwise, use EWMA. Remember, - * rtt overestimation is always better than underestimation. - */ - if (!(dst_metric_locked(dst, RTAX_RTT))) { - if (m <= 0) - set_dst_metric_rtt(dst, RTAX_RTT, tp->srtt); - else - set_dst_metric_rtt(dst, RTAX_RTT, rtt - (m >> 3)); - } + rtt = tcp_metric_get_jiffies(tm, TCP_METRIC_RTT); + m = rtt - tp->srtt; - if (!(dst_metric_locked(dst, RTAX_RTTVAR))) { - unsigned long var; - if (m < 0) - m = -m; + /* If newly calculated rtt larger than stored one, store new + * one. Otherwise, use EWMA. Remember, rtt overestimation is + * always better than underestimation. + */ + if (!tcp_metric_locked(tm, TCP_METRIC_RTT)) { + if (m <= 0) + rtt = tp->srtt; + else + rtt -= (m >> 3); + tcp_metric_set_msecs(tm, TCP_METRIC_RTT, rtt); + } - /* Scale deviation to rttvar fixed point */ - m >>= 1; - if (m < tp->mdev) - m = tp->mdev; + if (!tcp_metric_locked(tm, TCP_METRIC_RTTVAR)) { + unsigned long var; - var = dst_metric_rtt(dst, RTAX_RTTVAR); - if (m >= var) - var = m; - else - var -= (var - m) >> 2; + if (m < 0) + m = -m; - set_dst_metric_rtt(dst, RTAX_RTTVAR, var); - } + /* Scale deviation to rttvar fixed point */ + m >>= 1; + if (m < tp->mdev) + m = tp->mdev; - if (tcp_in_initial_slowstart(tp)) { - /* Slow start still did not finish. */ - if (dst_metric(dst, RTAX_SSTHRESH) && - !dst_metric_locked(dst, RTAX_SSTHRESH) && - (tp->snd_cwnd >> 1) > dst_metric(dst, RTAX_SSTHRESH)) - dst_metric_set(dst, RTAX_SSTHRESH, tp->snd_cwnd >> 1); - if (!dst_metric_locked(dst, RTAX_CWND) && - tp->snd_cwnd > dst_metric(dst, RTAX_CWND)) - dst_metric_set(dst, RTAX_CWND, tp->snd_cwnd); - } else if (tp->snd_cwnd > tp->snd_ssthresh && - icsk->icsk_ca_state == TCP_CA_Open) { - /* Cong. avoidance phase, cwnd is reliable. */ - if (!dst_metric_locked(dst, RTAX_SSTHRESH)) - dst_metric_set(dst, RTAX_SSTHRESH, - max(tp->snd_cwnd >> 1, tp->snd_ssthresh)); - if (!dst_metric_locked(dst, RTAX_CWND)) - dst_metric_set(dst, RTAX_CWND, - (dst_metric(dst, RTAX_CWND) + - tp->snd_cwnd) >> 1); - } else { - /* Else slow start did not finish, cwnd is non-sense, - ssthresh may be also invalid. - */ - if (!dst_metric_locked(dst, RTAX_CWND)) - dst_metric_set(dst, RTAX_CWND, - (dst_metric(dst, RTAX_CWND) + - tp->snd_ssthresh) >> 1); - if (dst_metric(dst, RTAX_SSTHRESH) && - !dst_metric_locked(dst, RTAX_SSTHRESH) && - tp->snd_ssthresh > dst_metric(dst, RTAX_SSTHRESH)) - dst_metric_set(dst, RTAX_SSTHRESH, tp->snd_ssthresh); - } + var = tcp_metric_get_jiffies(tm, TCP_METRIC_RTTVAR); + if (m >= var) + var = m; + else + var -= (var - m) >> 2; - if (!dst_metric_locked(dst, RTAX_REORDERING)) { - if (dst_metric(dst, RTAX_REORDERING) < tp->reordering && + tcp_metric_set_msecs(tm, TCP_METRIC_RTTVAR, var); + } + + if (tcp_in_initial_slowstart(tp)) { + /* Slow start still did not finish. */ + if (!tcp_metric_locked(tm, TCP_METRIC_SSTHRESH)) { + val = tcp_metric_get(tm, TCP_METRIC_SSTHRESH); + if (val && (tp->snd_cwnd >> 1) > val) + tcp_metric_set(tm, TCP_METRIC_SSTHRESH, + tp->snd_cwnd >> 1); + } + if (!tcp_metric_locked(tm, TCP_METRIC_CWND)) { + val = tcp_metric_get(tm, TCP_METRIC_CWND); + if (tp->snd_cwnd > val) + tcp_metric_set(tm, TCP_METRIC_CWND, + tp->snd_cwnd); + } + } else if (tp->snd_cwnd > tp->snd_ssthresh && + icsk->icsk_ca_state == TCP_CA_Open) { + /* Cong. avoidance phase, cwnd is reliable. */ + if (!tcp_metric_locked(tm, TCP_METRIC_SSTHRESH)) + tcp_metric_set(tm, TCP_METRIC_SSTHRESH, + max(tp->snd_cwnd >> 1, tp->snd_ssthresh)); + if (!tcp_metric_locked(tm, TCP_METRIC_CWND)) { + val = tcp_metric_get(tm, TCP_METRIC_CWND); + tcp_metric_set(tm, RTAX_CWND, (val + tp->snd_cwnd) >> 1); + } + } else { + /* Else slow start did not finish, cwnd is non-sense, + * ssthresh may be also invalid. + */ + if (!tcp_metric_locked(tm, TCP_METRIC_CWND)) { + val = tcp_metric_get(tm, TCP_METRIC_CWND); + tcp_metric_set(tm, TCP_METRIC_CWND, + (val + tp->snd_ssthresh) >> 1); + } + if (!tcp_metric_locked(tm, TCP_METRIC_SSTHRESH)) { + val = tcp_metric_get(tm, TCP_METRIC_SSTHRESH); + if (val && tp->snd_ssthresh > val) + tcp_metric_set(tm, TCP_METRIC_SSTHRESH, + tp->snd_ssthresh); + } + if (!tcp_metric_locked(tm, TCP_METRIC_REORDERING)) { + val = tcp_metric_get(tm, TCP_METRIC_REORDERING); + if (val < tp->reordering && tp->reordering != sysctl_tcp_reordering) - dst_metric_set(dst, RTAX_REORDERING, tp->reordering); + tcp_metric_set(tm, TCP_METRIC_REORDERING, + tp->reordering); } } + tm->tcpm_stamp = jiffies; +out_unlock: + rcu_read_unlock(); } /* Initialize metrics on socket. */ void tcp_init_metrics(struct sock *sk) { - struct tcp_sock *tp = tcp_sk(sk); struct dst_entry *dst = __sk_dst_get(sk); + struct tcp_sock *tp = tcp_sk(sk); + struct tcp_metrics_block *tm; + u32 val; if (dst == NULL) goto reset; dst_confirm(dst); - if (dst_metric_locked(dst, RTAX_CWND)) - tp->snd_cwnd_clamp = dst_metric(dst, RTAX_CWND); - if (dst_metric(dst, RTAX_SSTHRESH)) { - tp->snd_ssthresh = dst_metric(dst, RTAX_SSTHRESH); + rcu_read_lock(); + tm = tcp_get_metrics(sk, dst, true); + if (!tm) { + rcu_read_unlock(); + goto reset; + } + + if (tcp_metric_locked(tm, TCP_METRIC_CWND)) + tp->snd_cwnd_clamp = tcp_metric_get(tm, TCP_METRIC_CWND); + + val = tcp_metric_get(tm, TCP_METRIC_SSTHRESH); + if (val) { + tp->snd_ssthresh = val; if (tp->snd_ssthresh > tp->snd_cwnd_clamp) tp->snd_ssthresh = tp->snd_cwnd_clamp; } else { @@ -137,16 +434,18 @@ void tcp_init_metrics(struct sock *sk) */ tp->snd_ssthresh = TCP_INFINITE_SSTHRESH; } - if (dst_metric(dst, RTAX_REORDERING) && - tp->reordering != dst_metric(dst, RTAX_REORDERING)) { + val = tcp_metric_get(tm, TCP_METRIC_REORDERING); + if (val && tp->reordering != val) { tcp_disable_fack(tp); tcp_disable_early_retrans(tp); - tp->reordering = dst_metric(dst, RTAX_REORDERING); + tp->reordering = val; } - if (dst_metric(dst, RTAX_RTT) == 0 || tp->srtt == 0) + val = tcp_metric_get(tm, TCP_METRIC_RTT); + if (val == 0 || tp->srtt == 0) { + rcu_read_unlock(); goto reset; - + } /* Initial rtt is determined from SYN,SYN-ACK. * The segment is small and rtt may appear much * less than real one. Use per-dst memory @@ -161,14 +460,18 @@ void tcp_init_metrics(struct sock *sk) * to low value, and then abruptly stops to do it and starts to delay * ACKs, wait for troubles. */ - if (dst_metric_rtt(dst, RTAX_RTT) > tp->srtt) { - tp->srtt = dst_metric_rtt(dst, RTAX_RTT); + val = msecs_to_jiffies(val); + if (val > tp->srtt) { + tp->srtt = val; tp->rtt_seq = tp->snd_nxt; } - if (dst_metric_rtt(dst, RTAX_RTTVAR) > tp->mdev) { - tp->mdev = dst_metric_rtt(dst, RTAX_RTTVAR); + val = tcp_metric_get_jiffies(tm, TCP_METRIC_RTTVAR); + if (val > tp->mdev) { + tp->mdev = val; tp->mdev_max = tp->rttvar = max(tp->mdev, tcp_rto_min(sk)); } + rcu_read_unlock(); + tcp_set_rto(sk); reset: if (tp->srtt == 0) { @@ -195,8 +498,74 @@ reset: bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst) { + struct tcp_metrics_block *tm; + bool ret; + if (!dst) return false; - return dst_metric(dst, RTAX_RTT) ? true : false; + + rcu_read_lock(); + tm = __tcp_get_metrics_req(req, dst); + if (tm && tcp_metric_get(tm, TCP_METRIC_RTT)) + ret = true; + else + ret = false; + rcu_read_unlock(); + + return ret; } EXPORT_SYMBOL_GPL(tcp_peer_is_proven); + +static unsigned long tcpmhash_entries; +static int __init set_tcpmhash_entries(char *str) +{ + ssize_t ret; + + if (!str) + return 0; + + ret = kstrtoul(str, 0, &tcpmhash_entries); + if (ret) + return 0; + + return 1; +} +__setup("tcpmhash_entries=", set_tcpmhash_entries); + +static int __net_init tcp_net_metrics_init(struct net *net) +{ + int slots, size; + + slots = tcpmhash_entries; + if (!slots) { + if (totalram_pages >= 128 * 1024) + slots = 16 * 1024; + else + slots = 8 * 1024; + } + + size = slots * sizeof(struct tcpm_hash_bucket); + + net->ipv4.tcp_metrics_hash = kzalloc(size, GFP_KERNEL); + if (!net->ipv4.tcp_metrics_hash) + return -ENOMEM; + + net->ipv4.tcp_metrics_hash_mask = (slots - 1); + + return 0; +} + +static void __net_exit tcp_net_metrics_exit(struct net *net) +{ + kfree(net->ipv4.tcp_metrics_hash); +} + +static __net_initdata struct pernet_operations tcp_net_metrics_ops = { + .init = tcp_net_metrics_init, + .exit = tcp_net_metrics_exit, +}; + +void __init tcp_metrics_init(void) +{ + register_pernet_subsys(&tcp_net_metrics_ops); +} -- cgit v1.2.3 From 94334d5ed4b64ebcd2c4b421e133b921f8ccf75d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 10 Jul 2012 00:53:48 -0700 Subject: net: Kill set_dst_metric_rtt(). No longer used. Signed-off-by: David S. Miller --- include/net/dst.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index b2634e446613..51610468c63d 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -209,12 +209,6 @@ static inline unsigned long dst_metric_rtt(const struct dst_entry *dst, int metr return msecs_to_jiffies(dst_metric(dst, metric)); } -static inline void set_dst_metric_rtt(struct dst_entry *dst, int metric, - unsigned long rtt) -{ - dst_metric_set(dst, metric, jiffies_to_msecs(rtt)); -} - static inline u32 dst_allfrag(const struct dst_entry *dst) { -- cgit v1.2.3 From 81166dd6fa8eb780b2132d32fbc77eb6ac04e44e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 10 Jul 2012 03:14:24 -0700 Subject: tcp: Move timestamps from inetpeer to metrics cache. With help from Lin Ming. Signed-off-by: David S. Miller --- include/net/inetpeer.h | 4 +- include/net/tcp.h | 5 +- net/ipv4/inetpeer.c | 1 - net/ipv4/route.c | 8 +-- net/ipv4/tcp_ipv4.c | 30 ++--------- net/ipv4/tcp_metrics.c | 136 +++++++++++++++++++++++++++++++++++++++++++++-- net/ipv4/tcp_minisocks.c | 46 ---------------- net/ipv6/route.c | 13 +---- net/ipv6/tcp_ipv6.c | 33 ++---------- 9 files changed, 149 insertions(+), 127 deletions(-) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index c27c8f10ebdc..1119f6f6cdb4 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -46,15 +46,13 @@ struct inet_peer { }; /* * Once inet_peer is queued for deletion (refcnt == -1), following fields - * are not available: rid, ip_id_count, tcp_ts, tcp_ts_stamp + * are not available: rid, ip_id_count * We can share memory with rcu_head to help keep inet_peer small. */ union { struct { atomic_t rid; /* Frag reception counter */ atomic_t ip_id_count; /* IP ID for the next packet */ - __u32 tcp_ts; - __u32 tcp_ts_stamp; }; struct rcu_head rcu; struct inet_peer *gc_next; diff --git a/include/net/tcp.h b/include/net/tcp.h index 0900d63d1627..3618fefae049 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -390,7 +390,10 @@ extern void tcp_clear_retrans(struct tcp_sock *tp); extern void tcp_update_metrics(struct sock *sk); extern void tcp_init_metrics(struct sock *sk); extern void tcp_metrics_init(void); -extern bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst); +extern bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst, bool paws_check); +extern bool tcp_remember_stamp(struct sock *sk); +extern bool tcp_tw_remember_stamp(struct inet_timewait_sock *tw); +extern void tcp_fetch_timewait_stamp(struct sock *sk, struct dst_entry *dst); extern void tcp_disable_fack(struct tcp_sock *tp); extern void tcp_close(struct sock *sk, long timeout); extern void tcp_init_sock(struct sock *sk); diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index da90a8cab614..f457bcb41350 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -508,7 +508,6 @@ relookup: (daddr->family == AF_INET) ? secure_ip_id(daddr->addr.a4) : secure_ipv6_id(daddr->addr.a6)); - p->tcp_ts_stamp = 0; p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW; p->rate_tokens = 0; p->rate_last = 0; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index d02c91177d32..78d81543766d 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2846,7 +2846,7 @@ static int rt_fill_info(struct net *net, struct rtmsg *r; struct nlmsghdr *nlh; unsigned long expires = 0; - u32 id = 0, ts = 0, tsage = 0, error; + u32 id = 0, error; nlh = nlmsg_put(skb, pid, seq, event, sizeof(*r), flags); if (nlh == NULL) @@ -2903,10 +2903,6 @@ static int rt_fill_info(struct net *net, const struct inet_peer *peer = rt_peer_ptr(rt); inet_peer_refcheck(peer); id = atomic_read(&peer->ip_id_count) & 0xffff; - if (peer->tcp_ts_stamp) { - ts = peer->tcp_ts; - tsage = get_seconds() - peer->tcp_ts_stamp; - } expires = ACCESS_ONCE(peer->pmtu_expires); if (expires) { if (time_before(jiffies, expires)) @@ -2942,7 +2938,7 @@ static int rt_fill_info(struct net *net, goto nla_put_failure; } - if (rtnl_put_cacheinfo(skb, &rt->dst, id, ts, tsage, + if (rtnl_put_cacheinfo(skb, &rt->dst, id, 0, 0, expires, error) < 0) goto nla_put_failure; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index e9312a8f33a1..d406bf7f37d9 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -209,22 +209,8 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) } if (tcp_death_row.sysctl_tw_recycle && - !tp->rx_opt.ts_recent_stamp && fl4->daddr == daddr) { - struct inet_peer *peer = rt_get_peer(rt, fl4->daddr); - /* - * VJ's idea. We save last timestamp seen from - * the destination in peer table, when entering state - * TIME-WAIT * and initialize rx_opt.ts_recent from it, - * when trying new connection. - */ - if (peer) { - inet_peer_refcheck(peer); - if ((u32)get_seconds() - peer->tcp_ts_stamp <= TCP_PAWS_MSL) { - tp->rx_opt.ts_recent_stamp = peer->tcp_ts_stamp; - tp->rx_opt.ts_recent = peer->tcp_ts; - } - } - } + !tp->rx_opt.ts_recent_stamp && fl4->daddr == daddr) + tcp_fetch_timewait_stamp(sk, &rt->dst); inet->inet_dport = usin->sin_port; inet->inet_daddr = daddr; @@ -1375,7 +1361,6 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) isn = cookie_v4_init_sequence(sk, skb, &req->mss); req->cookie_ts = tmp_opt.tstamp_ok; } else if (!isn) { - struct inet_peer *peer = NULL; struct flowi4 fl4; /* VJ's idea. We save last timestamp seen @@ -1390,12 +1375,8 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle && (dst = inet_csk_route_req(sk, &fl4, req, want_cookie)) != NULL && - fl4.daddr == saddr && - (peer = rt_get_peer((struct rtable *)dst, fl4.daddr)) != NULL) { - inet_peer_refcheck(peer); - if ((u32)get_seconds() - peer->tcp_ts_stamp < TCP_PAWS_MSL && - (s32)(peer->tcp_ts - req->ts_recent) > - TCP_PAWS_WINDOW) { + fl4.daddr == saddr) { + if (!tcp_peer_is_proven(req, dst, true)) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED); goto drop_and_release; } @@ -1404,8 +1385,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) else if (!sysctl_tcp_syncookies && (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) < (sysctl_max_syn_backlog >> 2)) && - (!peer || !peer->tcp_ts_stamp) && - !tcp_peer_is_proven(req, dst)) { + !tcp_peer_is_proven(req, dst, false)) { /* Without syncookies last quarter of * backlog is filled with destinations, * proven to be alive. diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 56223bab251b..1fd83d3118fe 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -34,6 +34,8 @@ struct tcp_metrics_block { struct tcp_metrics_block __rcu *tcpm_next; struct inetpeer_addr tcpm_addr; unsigned long tcpm_stamp; + u32 tcpm_ts; + u32 tcpm_ts_stamp; u32 tcpm_lock; u32 tcpm_vals[TCP_METRIC_MAX]; }; @@ -114,6 +116,8 @@ static void tcpm_suck_dst(struct tcp_metrics_block *tm, struct dst_entry *dst) tm->tcpm_vals[TCP_METRIC_SSTHRESH] = dst_metric_raw(dst, RTAX_SSTHRESH); tm->tcpm_vals[TCP_METRIC_CWND] = dst_metric_raw(dst, RTAX_CWND); tm->tcpm_vals[TCP_METRIC_REORDERING] = dst_metric_raw(dst, RTAX_REORDERING); + tm->tcpm_ts = 0; + tm->tcpm_ts_stamp = 0; } static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, @@ -230,6 +234,45 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, return tm; } +static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock *tw) +{ + struct inet6_timewait_sock *tw6; + struct tcp_metrics_block *tm; + struct inetpeer_addr addr; + unsigned int hash; + struct net *net; + + addr.family = tw->tw_family; + switch (addr.family) { + case AF_INET: + addr.addr.a4 = tw->tw_daddr; + hash = (__force unsigned int) addr.addr.a4; + break; + case AF_INET6: + tw6 = inet6_twsk((struct sock *)tw); + *(struct in6_addr *)addr.addr.a6 = tw6->tw_v6_daddr; + hash = ((__force unsigned int) addr.addr.a6[0] ^ + (__force unsigned int) addr.addr.a6[1] ^ + (__force unsigned int) addr.addr.a6[2] ^ + (__force unsigned int) addr.addr.a6[3]); + break; + default: + return NULL; + } + + hash ^= (hash >> 24) ^ (hash >> 16) ^ (hash >> 8); + + net = twsk_net(tw); + hash &= net->ipv4.tcp_metrics_hash_mask; + + for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; + tm = rcu_dereference(tm->tcpm_next)) { + if (addr_same(&tm->tcpm_addr, &addr)) + break; + } + return tm; +} + static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, struct dst_entry *dst, bool create) @@ -496,7 +539,7 @@ reset: tp->snd_cwnd_stamp = tcp_time_stamp; } -bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst) +bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst, bool paws_check) { struct tcp_metrics_block *tm; bool ret; @@ -506,16 +549,99 @@ bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst) rcu_read_lock(); tm = __tcp_get_metrics_req(req, dst); - if (tm && tcp_metric_get(tm, TCP_METRIC_RTT)) - ret = true; - else - ret = false; + if (paws_check) { + if (tm && + (u32)get_seconds() - tm->tcpm_ts_stamp < TCP_PAWS_MSL && + (s32)(tm->tcpm_ts - req->ts_recent) > TCP_PAWS_WINDOW) + ret = false; + else + ret = true; + } else { + if (tm && tcp_metric_get(tm, TCP_METRIC_RTT) && tm->tcpm_ts_stamp) + ret = true; + else + ret = false; + } rcu_read_unlock(); return ret; } EXPORT_SYMBOL_GPL(tcp_peer_is_proven); +void tcp_fetch_timewait_stamp(struct sock *sk, struct dst_entry *dst) +{ + struct tcp_metrics_block *tm; + + rcu_read_lock(); + tm = tcp_get_metrics(sk, dst, true); + if (tm) { + struct tcp_sock *tp = tcp_sk(sk); + + if ((u32)get_seconds() - tm->tcpm_ts_stamp <= TCP_PAWS_MSL) { + tp->rx_opt.ts_recent_stamp = tm->tcpm_ts_stamp; + tp->rx_opt.ts_recent = tm->tcpm_ts; + } + } + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(tcp_fetch_timewait_stamp); + +/* VJ's idea. Save last timestamp seen from this destination and hold + * it at least for normal timewait interval to use for duplicate + * segment detection in subsequent connections, before they enter + * synchronized state. + */ +bool tcp_remember_stamp(struct sock *sk) +{ + struct dst_entry *dst = __sk_dst_get(sk); + bool ret = false; + + if (dst) { + struct tcp_metrics_block *tm; + + rcu_read_lock(); + tm = tcp_get_metrics(sk, dst, true); + if (tm) { + struct tcp_sock *tp = tcp_sk(sk); + + if ((s32)(tm->tcpm_ts - tp->rx_opt.ts_recent) <= 0 || + ((u32)get_seconds() - tm->tcpm_ts_stamp > TCP_PAWS_MSL && + tm->tcpm_ts_stamp <= (u32)tp->rx_opt.ts_recent_stamp)) { + tm->tcpm_ts_stamp = (u32)tp->rx_opt.ts_recent_stamp; + tm->tcpm_ts = tp->rx_opt.ts_recent; + } + ret = true; + } + rcu_read_unlock(); + } + return ret; +} + +bool tcp_tw_remember_stamp(struct inet_timewait_sock *tw) +{ + struct tcp_metrics_block *tm; + bool ret = false; + + rcu_read_lock(); + tm = __tcp_get_metrics_tw(tw); + if (tw) { + const struct tcp_timewait_sock *tcptw; + struct sock *sk = (struct sock *) tw; + + tcptw = tcp_twsk(sk); + if ((s32)(tm->tcpm_ts - tcptw->tw_ts_recent) <= 0 || + ((u32)get_seconds() - tm->tcpm_ts_stamp > TCP_PAWS_MSL && + tm->tcpm_ts_stamp <= (u32)tcptw->tw_ts_recent_stamp)) { + tm->tcpm_ts_stamp = (u32)tcptw->tw_ts_recent_stamp; + tm->tcpm_ts = tcptw->tw_ts_recent; + } + ret = true; + } + rcu_read_unlock(); + + return ret; +} + static unsigned long tcpmhash_entries; static int __init set_tcpmhash_entries(char *str) { diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 72b7c63b1a39..a51aa534dab1 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -49,52 +49,6 @@ struct inet_timewait_death_row tcp_death_row = { }; EXPORT_SYMBOL_GPL(tcp_death_row); -/* VJ's idea. Save last timestamp seen from this destination - * and hold it at least for normal timewait interval to use for duplicate - * segment detection in subsequent connections, before they enter synchronized - * state. - */ - -static bool tcp_remember_stamp(struct sock *sk) -{ - const struct inet_connection_sock *icsk = inet_csk(sk); - struct tcp_sock *tp = tcp_sk(sk); - struct inet_peer *peer; - - peer = icsk->icsk_af_ops->get_peer(sk); - if (peer) { - if ((s32)(peer->tcp_ts - tp->rx_opt.ts_recent) <= 0 || - ((u32)get_seconds() - peer->tcp_ts_stamp > TCP_PAWS_MSL && - peer->tcp_ts_stamp <= (u32)tp->rx_opt.ts_recent_stamp)) { - peer->tcp_ts_stamp = (u32)tp->rx_opt.ts_recent_stamp; - peer->tcp_ts = tp->rx_opt.ts_recent; - } - return true; - } - - return false; -} - -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; - - tcptw = tcp_twsk(sk); - peer = tcptw->tw_peer; - if (peer) { - 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; - } - return true; - } - return false; -} - static bool tcp_in_window(u32 seq, u32 end_seq, u32 s_win, u32 e_win) { if (seq == s_win) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 6cc6c881f54f..0c0684753781 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2348,13 +2348,11 @@ static int rt6_fill_node(struct net *net, int iif, int type, u32 pid, u32 seq, int prefix, int nowait, unsigned int flags) { - const struct inet_peer *peer; struct rtmsg *rtm; struct nlmsghdr *nlh; long expires; u32 table; struct neighbour *n; - u32 ts, tsage; if (prefix) { /* user wants prefix routes only */ if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { @@ -2473,16 +2471,7 @@ static int rt6_fill_node(struct net *net, else expires = INT_MAX; - peer = NULL; - if (rt6_has_peer(rt)) - peer = rt6_peer_ptr(rt); - ts = tsage = 0; - if (peer && peer->tcp_ts_stamp) { - ts = peer->tcp_ts; - tsage = get_seconds() - peer->tcp_ts_stamp; - } - - if (rtnl_put_cacheinfo(skb, &rt->dst, 0, ts, tsage, + if (rtnl_put_cacheinfo(skb, &rt->dst, 0, 0, 0, expires, rt->dst.error) < 0) goto nla_put_failure; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 75d179555c28..9e96b5f21d2a 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -277,22 +277,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, rt = (struct rt6_info *) dst; if (tcp_death_row.sysctl_tw_recycle && !tp->rx_opt.ts_recent_stamp && - ipv6_addr_equal(&rt->rt6i_dst.addr, &np->daddr)) { - struct inet_peer *peer = rt6_get_peer(rt); - /* - * VJ's idea. We save last timestamp seen from - * the destination in peer table, when entering state - * TIME-WAIT * and initialize rx_opt.ts_recent from it, - * when trying new connection. - */ - if (peer) { - inet_peer_refcheck(peer); - if ((u32)get_seconds() - peer->tcp_ts_stamp <= TCP_PAWS_MSL) { - tp->rx_opt.ts_recent_stamp = peer->tcp_ts_stamp; - tp->rx_opt.ts_recent = peer->tcp_ts; - } - } - } + ipv6_addr_equal(&rt->rt6i_dst.addr, &np->daddr)) + tcp_fetch_timewait_stamp(sk, dst); icsk->icsk_ext_hdr_len = 0; if (np->opt) @@ -1134,8 +1120,6 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) treq->iif = inet6_iif(skb); if (!isn) { - struct inet_peer *peer = NULL; - if (ipv6_opt_accepted(sk, skb) || np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo || np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) { @@ -1160,14 +1144,8 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) */ if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle && - (dst = inet6_csk_route_req(sk, &fl6, req)) != NULL && - (peer = rt6_get_peer((struct rt6_info *)dst)) != NULL && - ipv6_addr_equal((struct in6_addr *)peer->daddr.addr.a6, - &treq->rmt_addr)) { - inet_peer_refcheck(peer); - if ((u32)get_seconds() - peer->tcp_ts_stamp < TCP_PAWS_MSL && - (s32)(peer->tcp_ts - req->ts_recent) > - TCP_PAWS_WINDOW) { + (dst = inet6_csk_route_req(sk, &fl6, req)) != NULL) { + if (!tcp_peer_is_proven(req, dst, true)) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED); goto drop_and_release; } @@ -1176,8 +1154,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) else if (!sysctl_tcp_syncookies && (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) < (sysctl_max_syn_backlog >> 2)) && - (!peer || !peer->tcp_ts_stamp) && - !tcp_peer_is_proven(req, dst)) { + !tcp_peer_is_proven(req, dst, false)) { /* Without syncookies last quarter of * backlog is filled with destinations, * proven to be alive. -- cgit v1.2.3 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') 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 From 16d1839907e695387654901995f9286b65fbbc6a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 10 Jul 2012 03:32:59 -0700 Subject: inet: Remove ->get_peer() method. No longer used. Signed-off-by: David S. Miller --- include/net/inet_connection_sock.h | 1 - net/ipv4/tcp_ipv4.c | 16 ---------------- net/ipv6/tcp_ipv6.c | 16 ---------------- 3 files changed, 33 deletions(-) (limited to 'include') diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index af3c743a40e4..291e7cee14e7 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -43,7 +43,6 @@ struct inet_connection_sock_af_ops { struct sock *(*syn_recv_sock)(struct sock *sk, struct sk_buff *skb, struct request_sock *req, struct dst_entry *dst); - struct inet_peer *(*get_peer)(struct sock *sk); u16 net_header_len; u16 net_frag_header_len; u16 sockaddr_len; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index d406bf7f37d9..ddefd39ac0cf 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1847,21 +1847,6 @@ do_time_wait: goto discard_it; } -struct inet_peer *tcp_v4_get_peer(struct sock *sk) -{ - struct rtable *rt = (struct rtable *) __sk_dst_get(sk); - struct inet_sock *inet = inet_sk(sk); - - /* If we don't have a valid cached route, or we're doing IP - * options which make the IPv4 header destination address - * different from our peer's, do not bother with this. - */ - if (!rt || inet->cork.fl.u.ip4.daddr != inet->inet_daddr) - return NULL; - return rt_get_peer_create(rt, inet->inet_daddr); -} -EXPORT_SYMBOL(tcp_v4_get_peer); - static struct timewait_sock_ops tcp_timewait_sock_ops = { .twsk_obj_size = sizeof(struct tcp_timewait_sock), .twsk_unique = tcp_twsk_unique, @@ -1874,7 +1859,6 @@ const struct inet_connection_sock_af_ops ipv4_specific = { .rebuild_header = inet_sk_rebuild_header, .conn_request = tcp_v4_conn_request, .syn_recv_sock = tcp_v4_syn_recv_sock, - .get_peer = tcp_v4_get_peer, .net_header_len = sizeof(struct iphdr), .setsockopt = ip_setsockopt, .getsockopt = ip_getsockopt, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 9e96b5f21d2a..61175cb2478f 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1689,20 +1689,6 @@ do_time_wait: goto discard_it; } -static struct inet_peer *tcp_v6_get_peer(struct sock *sk) -{ - struct rt6_info *rt = (struct rt6_info *) __sk_dst_get(sk); - struct ipv6_pinfo *np = inet6_sk(sk); - - /* If we don't have a valid cached route, or we're doing IP - * options which make the IPv6 header destination address - * different from our peer's, do not bother with this. - */ - if (!rt || !ipv6_addr_equal(&np->daddr, &rt->rt6i_dst.addr)) - return NULL; - return rt6_get_peer_create(rt); -} - static struct timewait_sock_ops tcp6_timewait_sock_ops = { .twsk_obj_size = sizeof(struct tcp6_timewait_sock), .twsk_unique = tcp_twsk_unique, @@ -1715,7 +1701,6 @@ static const struct inet_connection_sock_af_ops ipv6_specific = { .rebuild_header = inet6_sk_rebuild_header, .conn_request = tcp_v6_conn_request, .syn_recv_sock = tcp_v6_syn_recv_sock, - .get_peer = tcp_v6_get_peer, .net_header_len = sizeof(struct ipv6hdr), .net_frag_header_len = sizeof(struct frag_hdr), .setsockopt = ipv6_setsockopt, @@ -1747,7 +1732,6 @@ static const struct inet_connection_sock_af_ops ipv6_mapped = { .rebuild_header = inet_sk_rebuild_header, .conn_request = tcp_v6_conn_request, .syn_recv_sock = tcp_v6_syn_recv_sock, - .get_peer = tcp_v4_get_peer, .net_header_len = sizeof(struct iphdr), .setsockopt = ipv6_setsockopt, .getsockopt = ipv6_getsockopt, -- cgit v1.2.3 From 3e12939a2a67fbb4cbd962c3b9bc398c73319766 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 10 Jul 2012 04:01:57 -0700 Subject: inet: Kill FLOWI_FLAG_PRECOW_METRICS. No longer needed. TCP writes metrics, but now in it's own special cache that does not dirty the route metrics. Therefore there is no longer any reason to pre-cow metrics in this way. Signed-off-by: David S. Miller --- include/net/flow.h | 5 ++--- include/net/inet_sock.h | 2 -- include/net/route.h | 2 -- net/ipv4/inet_connection_sock.c | 2 +- net/ipv4/route.c | 11 ++--------- net/ipv6/route.c | 2 +- 6 files changed, 6 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/flow.h b/include/net/flow.h index bd524f598561..ce9cb7656b47 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -20,9 +20,8 @@ struct flowi_common { __u8 flowic_proto; __u8 flowic_flags; #define FLOWI_FLAG_ANYSRC 0x01 -#define FLOWI_FLAG_PRECOW_METRICS 0x02 -#define FLOWI_FLAG_CAN_SLEEP 0x04 -#define FLOWI_FLAG_RT_NOCACHE 0x08 +#define FLOWI_FLAG_CAN_SLEEP 0x02 +#define FLOWI_FLAG_RT_NOCACHE 0x04 __u32 flowic_secid; }; diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index ae17e1352d7e..924d7b98ab60 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -245,8 +245,6 @@ static inline __u8 inet_sk_flowi_flags(const struct sock *sk) if (inet_sk(sk)->transparent || inet_sk(sk)->hdrincl) flags |= FLOWI_FLAG_ANYSRC; - if (sk->sk_protocol == IPPROTO_TCP) - flags |= FLOWI_FLAG_PRECOW_METRICS; return flags; } diff --git a/include/net/route.h b/include/net/route.h index 211e2665139b..635d7a99d199 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -278,8 +278,6 @@ static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst, __be32 if (inet_sk(sk)->transparent) flow_flags |= FLOWI_FLAG_ANYSRC; - if (protocol == IPPROTO_TCP) - flow_flags |= FLOWI_FLAG_PRECOW_METRICS; if (can_sleep) flow_flags |= FLOWI_FLAG_CAN_SLEEP; diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 034ddbe42adf..76825be3b643 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -375,7 +375,7 @@ struct dst_entry *inet_csk_route_req(struct sock *sk, const struct inet_request_sock *ireq = inet_rsk(req); struct ip_options_rcu *opt = inet_rsk(req)->opt; struct net *net = sock_net(sk); - int flags = inet_sk_flowi_flags(sk) & ~FLOWI_FLAG_PRECOW_METRICS; + int flags = inet_sk_flowi_flags(sk); if (nocache) flags |= FLOWI_FLAG_RT_NOCACHE; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index e376354dcb65..d4834e2914a0 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1658,7 +1658,7 @@ void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu, struct rtable *rt; flowi4_init_output(&fl4, oif, mark, RT_TOS(iph->tos), RT_SCOPE_UNIVERSE, - protocol, flow_flags | FLOWI_FLAG_PRECOW_METRICS, + protocol, flow_flags, iph->daddr, iph->saddr, 0, 0); rt = __ip_route_output_key(net, &fl4); if (!IS_ERR(rt)) { @@ -1836,18 +1836,11 @@ static void rt_init_metrics(struct rtable *rt, const struct flowi4 *fl4, { struct inet_peer_base *base; struct inet_peer *peer; - int create = 0; - - /* If a peer entry exists for this destination, we must hook - * it up in order to get at cached metrics. - */ - if (fl4 && (fl4->flowi4_flags & FLOWI_FLAG_PRECOW_METRICS)) - create = 1; base = inetpeer_base_ptr(rt->_peer); BUG_ON(!base); - peer = inet_getpeer_v4(base, rt->rt_dst, create); + peer = inet_getpeer_v4(base, rt->rt_dst, 0); if (peer) { __rt_set_peer(rt, peer); rt->rt_peer_genid = rt_peer_genid(); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 0c0684753781..b7eb51e1a0e1 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1093,7 +1093,7 @@ void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_oif = oif; fl6.flowi6_mark = mark; - fl6.flowi6_flags = FLOWI_FLAG_PRECOW_METRICS; + fl6.flowi6_flags = 0; fl6.daddr = iph->daddr; fl6.saddr = iph->saddr; fl6.flowlabel = (*(__be32 *) iph) & IPV6_FLOWINFO_MASK; -- cgit v1.2.3 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') 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 From 5943634fc5592037db0693b261f7f4bea6bb9457 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 10 Jul 2012 06:58:42 -0700 Subject: ipv4: Maintain redirect and PMTU info in struct rtable again. Maintaining this in the inetpeer entries was not the right way to do this at all. Signed-off-by: David S. Miller --- include/net/inetpeer.h | 4 -- include/net/route.h | 2 +- net/ipv4/inetpeer.c | 3 - net/ipv4/route.c | 185 ++++++++++-------------------------------------- net/ipv4/xfrm4_policy.c | 1 + 5 files changed, 41 insertions(+), 154 deletions(-) (limited to 'include') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index 1119f6f6cdb4..53f464d7cddc 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -36,10 +36,6 @@ struct inet_peer { u32 metrics[RTAX_MAX]; u32 rate_tokens; /* rate limiting for ICMP */ unsigned long rate_last; - unsigned long pmtu_expires; - u32 pmtu_orig; - u32 pmtu_learned; - struct inetpeer_addr_base redirect_learned; union { struct list_head gc_list; struct rcu_head gc_rcu; diff --git a/include/net/route.h b/include/net/route.h index 635d7a99d199..c27449466d18 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -65,7 +65,7 @@ struct rtable { __be32 rt_gateway; /* Miscellaneous cached information */ - u32 rt_peer_genid; + u32 rt_pmtu; unsigned long _peer; /* long-living peer info */ struct fib_info *fi; /* for client ref to shared metrics */ }; diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index f457bcb41350..e1e0a4e8fd34 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -511,9 +511,6 @@ relookup: p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW; p->rate_tokens = 0; p->rate_last = 0; - p->pmtu_expires = 0; - p->pmtu_orig = 0; - memset(&p->redirect_learned, 0, sizeof(p->redirect_learned)); INIT_LIST_HEAD(&p->gc_list); /* Link the node. */ diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 67b08745daf9..677d65253e4c 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -669,7 +669,7 @@ static inline int rt_fast_clean(struct rtable *rth) static inline int rt_valuable(struct rtable *rth) { return (rth->rt_flags & (RTCF_REDIRECTED | RTCF_NOTIFY)) || - (rt_has_peer(rth) && rt_peer_ptr(rth)->pmtu_expires); + rth->dst.expires; } static int rt_may_expire(struct rtable *rth, unsigned long tmo1, unsigned long tmo2) @@ -1242,13 +1242,6 @@ skip_hashing: return rt; } -static atomic_t __rt_peer_genid = ATOMIC_INIT(0); - -static u32 rt_peer_genid(void) -{ - return atomic_read(&__rt_peer_genid); -} - void rt_bind_peer(struct rtable *rt, __be32 daddr, int create) { struct inet_peer_base *base; @@ -1262,8 +1255,6 @@ void rt_bind_peer(struct rtable *rt, __be32 daddr, int create) if (peer) { if (!rt_set_peer(rt, peer)) inet_putpeer(peer); - else - rt->rt_peer_genid = rt_peer_genid(); } } @@ -1323,30 +1314,6 @@ static void rt_del(unsigned int hash, struct rtable *rt) spin_unlock_bh(rt_hash_lock_addr(hash)); } -static void check_peer_redir(struct dst_entry *dst, struct inet_peer *peer) -{ - struct rtable *rt = (struct rtable *) dst; - __be32 orig_gw = rt->rt_gateway; - struct neighbour *n; - - dst_confirm(&rt->dst); - - rt->rt_gateway = peer->redirect_learned.a4; - - n = ipv4_neigh_lookup(&rt->dst, NULL, &rt->rt_gateway); - if (!n) { - rt->rt_gateway = orig_gw; - return; - } - if (!(n->nud_state & NUD_VALID)) { - neigh_event_send(n, NULL); - } else { - rt->rt_flags |= RTCF_REDIRECTED; - call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, n); - } - neigh_release(n); -} - /* called in rcu_read_lock() section */ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, __be32 saddr, struct net_device *dev) @@ -1355,7 +1322,6 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, struct in_device *in_dev = __in_dev_get_rcu(dev); __be32 skeys[2] = { saddr, 0 }; int ikeys[2] = { dev->ifindex, 0 }; - struct inet_peer *peer; struct net *net; if (!in_dev) @@ -1388,6 +1354,8 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, rthp = &rt_hash_table[hash].chain; while ((rt = rcu_dereference(*rthp)) != NULL) { + struct neighbour *n; + rthp = &rt->dst.rt_next; if (rt->rt_key_dst != daddr || @@ -1401,13 +1369,16 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, rt->rt_gateway != old_gw) continue; - peer = rt_get_peer_create(rt, rt->rt_dst); - if (peer) { - if (peer->redirect_learned.a4 != new_gw) { - peer->redirect_learned.a4 = new_gw; - atomic_inc(&__rt_peer_genid); + n = ipv4_neigh_lookup(&rt->dst, NULL, &new_gw); + if (n) { + if (!(n->nud_state & NUD_VALID)) { + neigh_event_send(n, NULL); + } else { + rt->rt_gateway = new_gw; + rt->rt_flags |= RTCF_REDIRECTED; + call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, n); } - check_peer_redir(&rt->dst, peer); + neigh_release(n); } } } @@ -1425,23 +1396,6 @@ reject_redirect: ; } -static bool peer_pmtu_expired(struct inet_peer *peer) -{ - unsigned long orig = ACCESS_ONCE(peer->pmtu_expires); - - return orig && - time_after_eq(jiffies, orig) && - cmpxchg(&peer->pmtu_expires, orig, 0) == orig; -} - -static bool peer_pmtu_cleaned(struct inet_peer *peer) -{ - unsigned long orig = ACCESS_ONCE(peer->pmtu_expires); - - return orig && - cmpxchg(&peer->pmtu_expires, orig, 0) == orig; -} - static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst) { struct rtable *rt = (struct rtable *)dst; @@ -1451,16 +1405,13 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst) if (dst->obsolete > 0) { ip_rt_put(rt); ret = NULL; - } else if (rt->rt_flags & RTCF_REDIRECTED) { + } else if ((rt->rt_flags & RTCF_REDIRECTED) || + rt->dst.expires) { unsigned int hash = rt_hash(rt->rt_key_dst, rt->rt_key_src, rt->rt_oif, rt_genid(dev_net(dst->dev))); rt_del(hash, rt); ret = NULL; - } else if (rt_has_peer(rt)) { - struct inet_peer *peer = rt_peer_ptr(rt); - if (peer_pmtu_expired(peer)) - dst_metric_set(dst, RTAX_MTU, peer->pmtu_orig); } } return ret; @@ -1604,50 +1555,17 @@ out: kfree_skb(skb); return 0; } -static void check_peer_pmtu(struct dst_entry *dst, struct inet_peer *peer) -{ - unsigned long expires = ACCESS_ONCE(peer->pmtu_expires); - - if (!expires) - return; - if (time_before(jiffies, expires)) { - u32 orig_dst_mtu = dst_mtu(dst); - if (peer->pmtu_learned < orig_dst_mtu) { - if (!peer->pmtu_orig) - peer->pmtu_orig = dst_metric_raw(dst, RTAX_MTU); - dst_metric_set(dst, RTAX_MTU, peer->pmtu_learned); - } - } else if (cmpxchg(&peer->pmtu_expires, expires, 0) == expires) - dst_metric_set(dst, RTAX_MTU, peer->pmtu_orig); -} - static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu) { struct rtable *rt = (struct rtable *) dst; - struct inet_peer *peer; dst_confirm(dst); - peer = rt_get_peer_create(rt, rt->rt_dst); - if (peer) { - unsigned long pmtu_expires = ACCESS_ONCE(peer->pmtu_expires); - - if (mtu < ip_rt_min_pmtu) - mtu = ip_rt_min_pmtu; - if (!pmtu_expires || mtu < peer->pmtu_learned) { - - pmtu_expires = jiffies + ip_rt_mtu_expires; - if (!pmtu_expires) - pmtu_expires = 1UL; - - peer->pmtu_learned = mtu; - peer->pmtu_expires = pmtu_expires; + if (mtu < ip_rt_min_pmtu) + mtu = ip_rt_min_pmtu; - atomic_inc(&__rt_peer_genid); - rt->rt_peer_genid = rt_peer_genid(); - } - check_peer_pmtu(dst, peer); - } + rt->rt_pmtu = mtu; + dst_set_expires(&rt->dst, ip_rt_mtu_expires); } void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu, @@ -1679,30 +1597,12 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) } EXPORT_SYMBOL_GPL(ipv4_sk_update_pmtu); -static void ipv4_validate_peer(struct rtable *rt) -{ - if (rt->rt_peer_genid != rt_peer_genid()) { - struct inet_peer *peer = rt_get_peer(rt, rt->rt_dst); - - if (peer) { - check_peer_pmtu(&rt->dst, peer); - - if (peer->redirect_learned.a4 && - peer->redirect_learned.a4 != rt->rt_gateway) - check_peer_redir(&rt->dst, peer); - } - - rt->rt_peer_genid = rt_peer_genid(); - } -} - static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) { struct rtable *rt = (struct rtable *) dst; if (rt_is_expired(rt)) return NULL; - ipv4_validate_peer(rt); return dst; } @@ -1728,11 +1628,8 @@ static void ipv4_link_failure(struct sk_buff *skb) icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0); rt = skb_rtable(skb); - if (rt && rt_has_peer(rt)) { - struct inet_peer *peer = rt_peer_ptr(rt); - if (peer_pmtu_cleaned(peer)) - dst_metric_set(&rt->dst, RTAX_MTU, peer->pmtu_orig); - } + if (rt) + dst_set_expires(&rt->dst, 0); } static int ip_rt_bug(struct sk_buff *skb) @@ -1812,7 +1709,13 @@ static unsigned int ipv4_default_advmss(const struct dst_entry *dst) static unsigned int ipv4_mtu(const struct dst_entry *dst) { const struct rtable *rt = (const struct rtable *) dst; - unsigned int mtu = dst_metric_raw(dst, RTAX_MTU); + unsigned int mtu = rt->rt_pmtu; + + if (mtu && time_after_eq(jiffies, rt->dst.expires)) + mtu = 0; + + if (!mtu) + mtu = dst_metric_raw(dst, RTAX_MTU); if (mtu && rt_is_output_route(rt)) return mtu; @@ -1843,19 +1746,10 @@ static void rt_init_metrics(struct rtable *rt, const struct flowi4 *fl4, peer = inet_getpeer_v4(base, rt->rt_dst, 0); if (peer) { __rt_set_peer(rt, peer); - rt->rt_peer_genid = rt_peer_genid(); if (inet_metrics_new(peer)) memcpy(peer->metrics, fi->fib_metrics, sizeof(u32) * RTAX_MAX); dst_init_metrics(&rt->dst, peer->metrics, false); - - check_peer_pmtu(&rt->dst, peer); - - if (peer->redirect_learned.a4 && - peer->redirect_learned.a4 != rt->rt_gateway) { - rt->rt_gateway = peer->redirect_learned.a4; - rt->rt_flags |= RTCF_REDIRECTED; - } } else { if (fi->fib_metrics != (u32 *) dst_default_metrics) { rt->fi = fi; @@ -1955,8 +1849,8 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, rth->rt_iif = dev->ifindex; rth->rt_oif = 0; rth->rt_mark = skb->mark; + rth->rt_pmtu = 0; rth->rt_gateway = daddr; - rth->rt_peer_genid = 0; rt_init_peer(rth, dev_net(dev)->ipv4.peers); rth->fi = NULL; if (our) { @@ -2081,8 +1975,8 @@ static int __mkroute_input(struct sk_buff *skb, rth->rt_iif = in_dev->dev->ifindex; rth->rt_oif = 0; rth->rt_mark = skb->mark; + rth->rt_pmtu = 0; rth->rt_gateway = daddr; - rth->rt_peer_genid = 0; rt_init_peer(rth, &res->table->tb_peers); rth->fi = NULL; @@ -2260,8 +2154,8 @@ local_input: rth->rt_iif = dev->ifindex; rth->rt_oif = 0; rth->rt_mark = skb->mark; + rth->rt_pmtu = 0; rth->rt_gateway = daddr; - rth->rt_peer_genid = 0; rt_init_peer(rth, net->ipv4.peers); rth->fi = NULL; if (res.type == RTN_UNREACHABLE) { @@ -2337,7 +2231,6 @@ int ip_route_input_common(struct sk_buff *skb, __be32 daddr, __be32 saddr, rth->rt_mark == skb->mark && net_eq(dev_net(rth->dst.dev), net) && !rt_is_expired(rth)) { - ipv4_validate_peer(rth); if (noref) { dst_use_noref(&rth->dst, jiffies); skb_dst_set_noref(skb, &rth->dst); @@ -2459,8 +2352,8 @@ static struct rtable *__mkroute_output(const struct fib_result *res, rth->rt_iif = orig_oif ? : dev_out->ifindex; rth->rt_oif = orig_oif; rth->rt_mark = fl4->flowi4_mark; + rth->rt_pmtu = 0; rth->rt_gateway = fl4->daddr; - rth->rt_peer_genid = 0; rt_init_peer(rth, (res->table ? &res->table->tb_peers : dev_net(dev_out)->ipv4.peers)); @@ -2717,7 +2610,6 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *flp4) (IPTOS_RT_MASK | RTO_ONLINK)) && net_eq(dev_net(rth->dst.dev), net) && !rt_is_expired(rth)) { - ipv4_validate_peer(rth); dst_use(&rth->dst, jiffies); RT_CACHE_STAT_INC(out_hit); rcu_read_unlock_bh(); @@ -2794,6 +2686,7 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or rt->rt_iif = ort->rt_iif; rt->rt_oif = ort->rt_oif; rt->rt_mark = ort->rt_mark; + rt->rt_pmtu = ort->rt_pmtu; rt->rt_genid = rt_genid(net); rt->rt_flags = ort->rt_flags; @@ -2896,13 +2789,13 @@ static int rt_fill_info(struct net *net, const struct inet_peer *peer = rt_peer_ptr(rt); inet_peer_refcheck(peer); id = atomic_read(&peer->ip_id_count) & 0xffff; - expires = ACCESS_ONCE(peer->pmtu_expires); - if (expires) { - if (time_before(jiffies, expires)) - expires -= jiffies; - else - expires = 0; - } + } + expires = rt->dst.expires; + if (expires) { + if (time_before(jiffies, expires)) + expires -= jiffies; + else + expires = 0; } if (rt_is_input_route(rt)) { diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 9815ea0bca7f..951bcf35b21c 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -100,6 +100,7 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, xdst->u.rt.rt_src = rt->rt_src; xdst->u.rt.rt_dst = rt->rt_dst; xdst->u.rt.rt_gateway = rt->rt_gateway; + xdst->u.rt.rt_pmtu = rt->rt_pmtu; return 0; } -- cgit v1.2.3 From f185071ddf799e194ba015d040d3d49cdbfa7e48 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 10 Jul 2012 07:26:01 -0700 Subject: ipv4: Remove inetpeer from routes. No longer used. Signed-off-by: David S. Miller --- include/net/route.h | 57 ---------------------------------------------- net/ipv4/route.c | 60 +++++-------------------------------------------- net/ipv4/xfrm4_policy.c | 7 ------ 3 files changed, 6 insertions(+), 118 deletions(-) (limited to 'include') diff --git a/include/net/route.h b/include/net/route.h index c27449466d18..52362368af09 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -40,7 +40,6 @@ #define RT_CONN_FLAGS(sk) (RT_TOS(inet_sk(sk)->tos) | sock_flag(sk, SOCK_LOCALROUTE)) struct fib_nh; -struct inet_peer; struct fib_info; struct rtable { struct dst_entry dst; @@ -66,44 +65,9 @@ struct rtable { /* Miscellaneous cached information */ u32 rt_pmtu; - unsigned long _peer; /* long-living peer info */ struct fib_info *fi; /* for client ref to shared metrics */ }; -static inline struct inet_peer *rt_peer_ptr(struct rtable *rt) -{ - return inetpeer_ptr(rt->_peer); -} - -static inline bool rt_has_peer(struct rtable *rt) -{ - return inetpeer_ptr_is_peer(rt->_peer); -} - -static inline void __rt_set_peer(struct rtable *rt, struct inet_peer *peer) -{ - __inetpeer_ptr_set_peer(&rt->_peer, peer); -} - -static inline bool rt_set_peer(struct rtable *rt, struct inet_peer *peer) -{ - return inetpeer_ptr_set_peer(&rt->_peer, peer); -} - -static inline void rt_init_peer(struct rtable *rt, struct inet_peer_base *base) -{ - inetpeer_init_ptr(&rt->_peer, base); -} - -static inline void rt_transfer_peer(struct rtable *rt, struct rtable *ort) -{ - rt->_peer = ort->_peer; - if (rt_has_peer(ort)) { - struct inet_peer *peer = rt_peer_ptr(ort); - atomic_inc(&peer->refcnt); - } -} - static inline bool rt_is_input_route(const struct rtable *rt) { return rt->rt_route_iif != 0; @@ -326,27 +290,6 @@ static inline struct rtable *ip_route_newports(struct flowi4 *fl4, struct rtable return rt; } -extern void rt_bind_peer(struct rtable *rt, __be32 daddr, int create); - -static inline struct inet_peer *__rt_get_peer(struct rtable *rt, __be32 daddr, int create) -{ - if (rt_has_peer(rt)) - return rt_peer_ptr(rt); - - rt_bind_peer(rt, daddr, create); - return (rt_has_peer(rt) ? rt_peer_ptr(rt) : NULL); -} - -static inline struct inet_peer *rt_get_peer(struct rtable *rt, __be32 daddr) -{ - return __rt_get_peer(rt, daddr, 0); -} - -static inline struct inet_peer *rt_get_peer_create(struct rtable *rt, __be32 daddr) -{ - return __rt_get_peer(rt, daddr, 1); -} - static inline int inet_iif(const struct sk_buff *skb) { return skb_rtable(skb)->rt_iif; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 9cc00f8a6ee5..95bfa1ba5b28 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -889,7 +889,6 @@ static void rt_cache_invalidate(struct net *net) get_random_bytes(&shuffle, sizeof(shuffle)); atomic_add(shuffle + 1U, &net->ipv4.rt_genid); - inetpeer_invalidate_family(AF_INET); } /* @@ -1216,22 +1215,6 @@ skip_hashing: return rt; } -void rt_bind_peer(struct rtable *rt, __be32 daddr, int create) -{ - struct inet_peer_base *base; - struct inet_peer *peer; - - base = inetpeer_base_ptr(rt->_peer); - if (!base) - return; - - peer = inet_getpeer_v4(base, daddr, create); - if (peer) { - if (!rt_set_peer(rt, peer)) - inet_putpeer(peer); - } -} - /* * Peer allocation may fail only in serious out-of-memory conditions. However * we still can generate some output. @@ -1588,10 +1571,6 @@ static void ipv4_dst_destroy(struct dst_entry *dst) fib_info_put(rt->fi); rt->fi = NULL; } - if (rt_has_peer(rt)) { - struct inet_peer *peer = rt_peer_ptr(rt); - inet_putpeer(peer); - } } @@ -1711,26 +1690,11 @@ static unsigned int ipv4_mtu(const struct dst_entry *dst) static void rt_init_metrics(struct rtable *rt, const struct flowi4 *fl4, struct fib_info *fi) { - struct inet_peer_base *base; - struct inet_peer *peer; - - base = inetpeer_base_ptr(rt->_peer); - BUG_ON(!base); - - peer = inet_getpeer_v4(base, rt->rt_dst, 0); - if (peer) { - __rt_set_peer(rt, peer); - if (inet_metrics_new(peer)) - memcpy(peer->metrics, fi->fib_metrics, - sizeof(u32) * RTAX_MAX); - dst_init_metrics(&rt->dst, peer->metrics, false); - } else { - if (fi->fib_metrics != (u32 *) dst_default_metrics) { - rt->fi = fi; - atomic_inc(&fi->fib_clntref); - } - dst_init_metrics(&rt->dst, fi->fib_metrics, true); + if (fi->fib_metrics != (u32 *) dst_default_metrics) { + rt->fi = fi; + atomic_inc(&fi->fib_clntref); } + dst_init_metrics(&rt->dst, fi->fib_metrics, true); } static void rt_set_nexthop(struct rtable *rt, const struct flowi4 *fl4, @@ -1820,7 +1784,6 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, rth->rt_mark = skb->mark; rth->rt_pmtu = 0; rth->rt_gateway = daddr; - rt_init_peer(rth, dev_net(dev)->ipv4.peers); rth->fi = NULL; if (our) { rth->dst.input= ip_local_deliver; @@ -1946,7 +1909,6 @@ static int __mkroute_input(struct sk_buff *skb, rth->rt_mark = skb->mark; rth->rt_pmtu = 0; rth->rt_gateway = daddr; - rt_init_peer(rth, &res->table->tb_peers); rth->fi = NULL; rth->dst.input = ip_forward; @@ -2125,7 +2087,6 @@ local_input: rth->rt_mark = skb->mark; rth->rt_pmtu = 0; rth->rt_gateway = daddr; - rt_init_peer(rth, net->ipv4.peers); rth->fi = NULL; if (res.type == RTN_UNREACHABLE) { rth->dst.input= ip_error; @@ -2323,9 +2284,6 @@ static struct rtable *__mkroute_output(const struct fib_result *res, rth->rt_mark = fl4->flowi4_mark; rth->rt_pmtu = 0; rth->rt_gateway = fl4->daddr; - rt_init_peer(rth, (res->table ? - &res->table->tb_peers : - dev_net(dev_out)->ipv4.peers)); rth->fi = NULL; RT_CACHE_STAT_INC(out_slow_tot); @@ -2662,7 +2620,6 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or rt->rt_dst = ort->rt_dst; rt->rt_src = ort->rt_src; rt->rt_gateway = ort->rt_gateway; - rt_transfer_peer(rt, ort); rt->fi = ort->fi; if (rt->fi) atomic_inc(&rt->fi->fib_clntref); @@ -2700,7 +2657,7 @@ static int rt_fill_info(struct net *net, struct rtmsg *r; struct nlmsghdr *nlh; unsigned long expires = 0; - u32 id = 0, error; + u32 error; nlh = nlmsg_put(skb, pid, seq, event, sizeof(*r), flags); if (nlh == NULL) @@ -2753,11 +2710,6 @@ static int rt_fill_info(struct net *net, goto nla_put_failure; error = rt->dst.error; - if (rt_has_peer(rt)) { - const struct inet_peer *peer = rt_peer_ptr(rt); - inet_peer_refcheck(peer); - id = atomic_read(&peer->ip_id_count) & 0xffff; - } expires = rt->dst.expires; if (expires) { if (time_before(jiffies, expires)) @@ -2792,7 +2744,7 @@ static int rt_fill_info(struct net *net, goto nla_put_failure; } - if (rtnl_put_cacheinfo(skb, &rt->dst, id, expires, error) < 0) + if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, error) < 0) goto nla_put_failure; return nlmsg_end(skb, nlh); diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 951bcf35b21c..87d3fcc302d4 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -90,8 +90,6 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, xdst->u.dst.dev = dev; dev_hold(dev); - rt_transfer_peer(&xdst->u.rt, rt); - /* Sheit... I remember I did this right. Apparently, * it was magically lost, so this code needs audit */ xdst->u.rt.rt_flags = rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST | @@ -210,11 +208,6 @@ static void xfrm4_dst_destroy(struct dst_entry *dst) dst_destroy_metrics_generic(dst); - if (rt_has_peer(&xdst->u.rt)) { - struct inet_peer *peer = rt_peer_ptr(&xdst->u.rt); - inet_putpeer(peer); - } - xfrm_dst_destroy(xdst); } -- cgit v1.2.3 From 1a203cb33a7dc791b6c0aedf701e70ac00c50cdb Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 10 Jul 2012 19:05:57 +0000 Subject: ipv6: optimize ipv6 addresses compares On 64 bit arches having efficient unaligned accesses (eg x86_64) we can use long words to reduce number of instructions for free. Joe Perches suggested to change ipv6_masked_addr_cmp() to return a bool instead of 'int', to make sure ipv6_masked_addr_cmp() cannot be used in a sorting function. Signed-off-by: Eric Dumazet Cc: Joe Perches Signed-off-by: David S. Miller --- include/net/ipv6.h | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index aecf88436abf..d4261d4d6c47 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -298,14 +298,23 @@ static inline int ipv6_addr_cmp(const struct in6_addr *a1, const struct in6_addr return memcmp(a1, a2, sizeof(struct in6_addr)); } -static inline int +static inline bool ipv6_masked_addr_cmp(const struct in6_addr *a1, const struct in6_addr *m, const struct in6_addr *a2) { +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 + const unsigned long *ul1 = (const unsigned long *)a1; + const unsigned long *ulm = (const unsigned long *)m; + const unsigned long *ul2 = (const unsigned long *)a2; + + return !!(((ul1[0] ^ ul2[0]) & ulm[0]) | + ((ul1[1] ^ ul2[1]) & ulm[1])); +#else return !!(((a1->s6_addr32[0] ^ a2->s6_addr32[0]) & m->s6_addr32[0]) | ((a1->s6_addr32[1] ^ a2->s6_addr32[1]) & m->s6_addr32[1]) | ((a1->s6_addr32[2] ^ a2->s6_addr32[2]) & m->s6_addr32[2]) | ((a1->s6_addr32[3] ^ a2->s6_addr32[3]) & m->s6_addr32[3])); +#endif } static inline void ipv6_addr_prefix(struct in6_addr *pfx, @@ -335,10 +344,17 @@ static inline void ipv6_addr_set(struct in6_addr *addr, static inline bool ipv6_addr_equal(const struct in6_addr *a1, const struct in6_addr *a2) { +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 + const unsigned long *ul1 = (const unsigned long *)a1; + const unsigned long *ul2 = (const unsigned long *)a2; + + return ((ul1[0] ^ ul2[0]) | (ul1[1] ^ ul2[1])) == 0UL; +#else return ((a1->s6_addr32[0] ^ a2->s6_addr32[0]) | (a1->s6_addr32[1] ^ a2->s6_addr32[1]) | (a1->s6_addr32[2] ^ a2->s6_addr32[2]) | (a1->s6_addr32[3] ^ a2->s6_addr32[3])) == 0; +#endif } static inline bool __ipv6_prefix_equal(const __be32 *a1, const __be32 *a2, @@ -391,8 +407,14 @@ bool ip6_frag_match(struct inet_frag_queue *q, void *a); static inline bool ipv6_addr_any(const struct in6_addr *a) { +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 + const unsigned long *ul = (const unsigned long *)a; + + return (ul[0] | ul[1]) == 0UL; +#else return (a->s6_addr32[0] | a->s6_addr32[1] | a->s6_addr32[2] | a->s6_addr32[3]) == 0; +#endif } static inline bool ipv6_addr_loopback(const struct in6_addr *a) -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 From c5c4bdf02e518a281b229ae0891b346919e2d291 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 5 Jul 2012 09:41:22 -0700 Subject: USB: Remove unused LPM variable. hub_initiated_lpm_disable_count is not used by any code, so remove it. This commit should be backported to kernels as old as 3.5, that contain the commit 8306095fd2c1100e8244c09bf560f97aca5a311d "USB: Disable USB 3.0 LPM in critical sections." Signed-off-by: Sarah Sharp Cc: stable@vger.kernel.org --- include/linux/usb.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/usb.h b/include/linux/usb.h index d4f9de1acd45..dea2f0de063e 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -561,7 +561,6 @@ struct usb_device { struct usb3_lpm_parameters u1_params; struct usb3_lpm_parameters u2_params; unsigned lpm_disable_count; - unsigned hub_initiated_lpm_disable_count; }; #define to_usb_device(d) container_of(d, struct usb_device, dev) -- cgit v1.2.3 From f74631e3426474183389e55f703797bd965cd356 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 25 Jun 2012 12:08:08 -0700 Subject: USB: Enable Latency Tolerance Messaging (LTM). USB 3.0 devices may optionally support a new feature called Latency Tolerance Messaging. If both the xHCI host controller and the device support LTM, it should be turned on in order to give the system hardware a better clue about the latency tolerance values of its PCI devices. Once a Set Feature request to enable LTM is received, the USB 3.0 device will begin to send LTM updates as its buffers fill or empty, and it can tolerate more or less latency. The USB 3.0 spec, section C.4.2 says that LTM should be disabled just before the device is placed into suspend. Then the device will send an updated LTM notification, so that the system doesn't think it should remain in an active state in order to satisfy the latency requirements of the suspended device. The Set and Clear Feature LTM enable command can only be sent to a configured device. The device will respond with an error if that command is sent while it is in the Default or Addressed state. Make sure to check udev->actconfig in usb_enable_ltm() and usb_disable_ltm(), and don't send those commands when the device is unconfigured. LTM should be enabled once a new configuration is installed in usb_set_configuration(). If we end up sending duplicate Set Feature LTM Enable commands on a switch from one installed configuration to another configuration, that should be harmless. Make sure that LTM is disabled before the device is unconfigured in usb_disable_device(). If no drivers are bound to the device, it doesn't make sense to allow the device to control the latency tolerance of the xHCI host controller. Signed-off-by: Sarah Sharp --- drivers/usb/core/hub.c | 87 ++++++++++++++++++++++++++++++++++++++++++---- drivers/usb/core/message.c | 3 ++ include/linux/usb.h | 3 ++ 3 files changed, 87 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 5c31d2c2f95a..b5bd6bd8fd12 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2610,6 +2610,57 @@ static int check_port_resume_type(struct usb_device *udev, return status; } +static bool usb_device_supports_ltm(struct usb_device *udev) +{ + if (udev->speed != USB_SPEED_SUPER || !udev->bos || !udev->bos->ss_cap) + return false; + return udev->bos->ss_cap->bmAttributes & USB_LTM_SUPPORT; +} + +int usb_disable_ltm(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + /* Check if the roothub and device supports LTM. */ + if (!usb_device_supports_ltm(hcd->self.root_hub) || + !usb_device_supports_ltm(udev)) + return 0; + + /* Clear Feature LTM Enable can only be sent if the device is + * configured. + */ + if (!udev->actconfig) + return 0; + + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_LTM_ENABLE, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} +EXPORT_SYMBOL_GPL(usb_disable_ltm); + +void usb_enable_ltm(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + /* Check if the roothub and device supports LTM. */ + if (!usb_device_supports_ltm(hcd->self.root_hub) || + !usb_device_supports_ltm(udev)) + return; + + /* Set Feature LTM Enable can only be sent if the device is + * configured. + */ + if (!udev->actconfig) + return; + + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_LTM_ENABLE, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} +EXPORT_SYMBOL_GPL(usb_enable_ltm); + #ifdef CONFIG_USB_SUSPEND /* @@ -2705,6 +2756,11 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_enabled == 1) usb_set_usb2_hardware_lpm(udev, 0); + if (usb_disable_ltm(udev)) { + dev_err(&udev->dev, "%s Failed to disable LTM before suspend\n.", + __func__); + return -ENOMEM; + } if (usb_unlocked_disable_lpm(udev)) { dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.", __func__); @@ -2734,7 +2790,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_capable == 1) usb_set_usb2_hardware_lpm(udev, 1); - /* Try to enable USB3 LPM again */ + /* Try to enable USB3 LTM and LPM again */ + usb_enable_ltm(udev); usb_unlocked_enable_lpm(udev); /* System sleep transitions should never fail */ @@ -2935,7 +2992,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_capable == 1) usb_set_usb2_hardware_lpm(udev, 1); - /* Try to enable USB3 LPM */ + /* Try to enable USB3 LTM and LPM */ + usb_enable_ltm(udev); usb_unlocked_enable_lpm(udev); } @@ -3488,6 +3546,15 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm); void usb_unlocked_enable_lpm(struct usb_device *udev) { } EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); + +int usb_disable_ltm(struct usb_device *udev) +{ + return 0; +} +EXPORT_SYMBOL_GPL(usb_disable_ltm); + +void usb_enable_ltm(struct usb_device *udev) { } +EXPORT_SYMBOL_GPL(usb_enable_ltm); #endif @@ -4673,15 +4740,22 @@ static int usb_reset_and_verify_device(struct usb_device *udev) } parent_hub = hdev_to_hub(parent_hdev); - /* Disable LPM while we reset the device and reinstall the alt settings. - * Device-initiated LPM settings, and system exit latency settings are - * cleared when the device is reset, so we have to set them up again. + /* Disable LPM and LTM while we reset the device and reinstall the alt + * settings. Device-initiated LPM settings, and system exit latency + * settings are cleared when the device is reset, so we have to set + * them up again. */ ret = usb_unlocked_disable_lpm(udev); if (ret) { dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); goto re_enumerate; } + ret = usb_disable_ltm(udev); + if (ret) { + dev_err(&udev->dev, "%s Failed to disable LTM\n.", + __func__); + goto re_enumerate; + } set_bit(port1, parent_hub->busy_bits); for (i = 0; i < SET_CONFIG_TRIES; ++i) { @@ -4769,8 +4843,9 @@ static int usb_reset_and_verify_device(struct usb_device *udev) } done: - /* Now that the alt settings are re-installed, enable LPM. */ + /* Now that the alt settings are re-installed, enable LTM and LPM. */ usb_unlocked_enable_lpm(udev); + usb_enable_ltm(udev); return 0; re_enumerate: diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index c0877b7f505a..0ab7da2283e3 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1175,6 +1175,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) dev->actconfig->interface[i] = NULL; } usb_unlocked_disable_lpm(dev); + usb_disable_ltm(dev); dev->actconfig = NULL; if (dev->state == USB_STATE_CONFIGURED) usb_set_device_state(dev, USB_STATE_ADDRESS); @@ -1879,6 +1880,8 @@ free_interfaces: /* Now that the interfaces are installed, re-enable LPM. */ usb_unlocked_enable_lpm(dev); + /* Enable LTM if it was turned off by usb_disable_device. */ + usb_enable_ltm(dev); /* Now that all the interfaces are set up, register them * to trigger binding of drivers to interfaces. probe() diff --git a/include/linux/usb.h b/include/linux/usb.h index dea2f0de063e..f29831bad235 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -633,6 +633,9 @@ extern void usb_enable_lpm(struct usb_device *udev); extern int usb_unlocked_disable_lpm(struct usb_device *udev); extern void usb_unlocked_enable_lpm(struct usb_device *udev); +extern int usb_disable_ltm(struct usb_device *udev); +extern void usb_enable_ltm(struct usb_device *udev); + /*-------------------------------------------------------------------------*/ /* for drivers using iso endpoints */ -- cgit v1.2.3 From 024f117c2f3c4bb5df6e6696b709e0f3ed7e5dbb Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 5 Jul 2012 17:17:24 -0700 Subject: USB: Add a sysfs file to show LTM capabilities. USB 3.0 devices can optionally support Latency Tolerance Messaging (LTM). Add a new sysfs file in the device directory to show whether a device is LTM capable. This file will be present for both USB 2.0 and USB 3.0 devices. Signed-off-by: Sarah Sharp --- Documentation/ABI/testing/sysfs-bus-usb | 12 ++++++++++++ drivers/usb/core/hub.c | 7 ------- drivers/usb/core/sysfs.c | 10 ++++++++++ include/linux/usb.h | 8 ++++++++ 4 files changed, 30 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index 6df4e6f57560..5f75f8f7df34 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -208,3 +208,15 @@ Description: such as ACPI. This file will read either "removable" or "fixed" if the information is available, and "unknown" otherwise. + +What: /sys/bus/usb/devices/.../ltm_capable +Date: July 2012 +Contact: Sarah Sharp +Description: + USB 3.0 devices may optionally support Latency Tolerance + Messaging (LTM). They indicate their support by setting a bit + in the bmAttributes field of their SuperSpeed BOS descriptors. + If that bit is set for the device, ltm_capable will read "yes". + If the device doesn't support LTM, the file will read "no". + The file will be present for all speeds of USB devices, and will + always read "no" for USB 1.1 and USB 2.0 devices. diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b5bd6bd8fd12..d739f966b5a8 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2610,13 +2610,6 @@ static int check_port_resume_type(struct usb_device *udev, return status; } -static bool usb_device_supports_ltm(struct usb_device *udev) -{ - if (udev->speed != USB_SPEED_SUPER || !udev->bos || !udev->bos->ss_cap) - return false; - return udev->bos->ss_cap->bmAttributes & USB_LTM_SUPPORT; -} - int usb_disable_ltm(struct usb_device *udev) { struct usb_hcd *hcd = bus_to_hcd(udev->bus); diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 777f03c37725..682e8256b95d 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -253,6 +253,15 @@ show_removable(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(removable, S_IRUGO, show_removable, NULL); +static ssize_t +show_ltm_capable(struct device *dev, struct device_attribute *attr, char *buf) +{ + if (usb_device_supports_ltm(to_usb_device(dev))) + return sprintf(buf, "%s\n", "yes"); + return sprintf(buf, "%s\n", "no"); +} +static DEVICE_ATTR(ltm_capable, S_IRUGO, show_ltm_capable, NULL); + #ifdef CONFIG_PM static ssize_t @@ -649,6 +658,7 @@ static struct attribute *dev_attrs[] = { &dev_attr_authorized.attr, &dev_attr_remove.attr, &dev_attr_removable.attr, + &dev_attr_ltm_capable.attr, NULL, }; static struct attribute_group dev_attr_grp = { diff --git a/include/linux/usb.h b/include/linux/usb.h index f29831bad235..f8506ed0f97b 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -636,6 +636,14 @@ extern void usb_unlocked_enable_lpm(struct usb_device *udev); extern int usb_disable_ltm(struct usb_device *udev); extern void usb_enable_ltm(struct usb_device *udev); +static inline bool usb_device_supports_ltm(struct usb_device *udev) +{ + if (udev->speed != USB_SPEED_SUPER || !udev->bos || !udev->bos->ss_cap) + return false; + return udev->bos->ss_cap->bmAttributes & USB_LTM_SUPPORT; +} + + /*-------------------------------------------------------------------------*/ /* for drivers using iso endpoints */ -- cgit v1.2.3 From 4b10b274e22ca6df1cda2fccf3870b8586feec15 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 11 Jul 2012 14:43:34 +0300 Subject: Bluetooth: debug: Print l2cap_chan refcount Improve debug output. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index d80e3f0691b4..e5164ff55f27 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -672,11 +672,15 @@ enum { static inline void l2cap_chan_hold(struct l2cap_chan *c) { + BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->refcnt)); + atomic_inc(&c->refcnt); } static inline void l2cap_chan_put(struct l2cap_chan *c) { + BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->refcnt)); + if (atomic_dec_and_test(&c->refcnt)) kfree(c); } -- cgit v1.2.3 From 781d0f46d81e2c26c70649903b503bcfe817efc8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 5 Jul 2012 12:19:19 +0100 Subject: irq_domain: Standardise legacy/linear domain selection A large proportion of interrupt controllers that support legacy mappings do so because non-DT systems need to use fixed IRQ numbers when registering devices via buses but can otherwise use a linear mapping. The interrupt controller itself typically is not affected by the mapping used and best practice is to use a linear mapping where possible so drivers frequently select at runtime depending on if a legacy range has been allocated to them. Standardise this behaviour by providing irq_domain_register_simple() which will allocate a linear mapping unless a positive first_irq is provided in which case it will fall back to a legacy mapping. This helps make best practice for irq_domain adoption clearer. Signed-off-by: Mark Brown Signed-off-by: Grant Likely --- Documentation/IRQ-domain.txt | 5 +++++ include/linux/irqdomain.h | 5 +++++ kernel/irq/irqdomain.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) (limited to 'include') diff --git a/Documentation/IRQ-domain.txt b/Documentation/IRQ-domain.txt index 27dcaabfb4db..1401cece745a 100644 --- a/Documentation/IRQ-domain.txt +++ b/Documentation/IRQ-domain.txt @@ -93,6 +93,7 @@ Linux IRQ number into the hardware. Most drivers cannot use this mapping. ==== Legacy ==== +irq_domain_add_simple() irq_domain_add_legacy() irq_domain_add_legacy_isa() @@ -115,3 +116,7 @@ The legacy map should only be used if fixed IRQ mappings must be supported. For example, ISA controllers would use the legacy map for mapping Linux IRQs 0-15 so that existing ISA drivers get the correct IRQ numbers. + +Most users of legacy mappings should use irq_domain_add_simple() which +will use a legacy domain only if an IRQ range is supplied by the +system and will otherwise use a linear domain mapping. diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 5abb533eb8eb..17b60be30fff 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -112,6 +112,11 @@ struct irq_domain { }; #ifdef CONFIG_IRQ_DOMAIN +struct irq_domain *irq_domain_add_simple(struct device_node *of_node, + unsigned int size, + unsigned int first_irq, + const struct irq_domain_ops *ops, + void *host_data); struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, unsigned int size, unsigned int first_irq, diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index d3968e91bfd2..0c51958c6335 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -139,6 +139,36 @@ static unsigned int irq_domain_legacy_revmap(struct irq_domain *domain, return hwirq - first_hwirq + domain->revmap_data.legacy.first_irq; } +/** + * irq_domain_add_simple() - Allocate and register a simple irq_domain. + * @of_node: pointer to interrupt controller's device tree node. + * @size: total number of irqs in mapping + * @first_irq: first number of irq block assigned to the domain + * @ops: map/unmap domain callbacks + * @host_data: Controller private data pointer + * + * Allocates a legacy irq_domain if irq_base is positive or a linear + * domain otherwise. + * + * This is intended to implement the expected behaviour for most + * interrupt controllers which is that a linear mapping should + * normally be used unless the system requires a legacy mapping in + * order to support supplying interrupt numbers during non-DT + * registration of devices. + */ +struct irq_domain *irq_domain_add_simple(struct device_node *of_node, + unsigned int size, + unsigned int first_irq, + const struct irq_domain_ops *ops, + void *host_data) +{ + if (first_irq > 0) + return irq_domain_add_legacy(of_node, size, first_irq, 0, + ops, host_data); + else + return irq_domain_add_linear(of_node, size, ops, host_data); +} + /** * irq_domain_add_legacy() - Allocate and register a legacy revmap irq_domain. * @of_node: pointer to interrupt controller's device tree node. -- cgit v1.2.3 From 98aa468e045a0091a7c34d9f5205a629634fabf4 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sun, 17 Jun 2012 16:17:04 -0600 Subject: irqdomain: Support for static IRQ mapping and association. This adds a new strict mapping API for supporting creation of linux IRQs at existing positions within the domain. The new routines are as follows: For dynamic allocation and insertion to specified ranges: - irq_create_identity_mapping() - irq_create_strict_mappings() These will allocate and associate a range of linux IRQs at the specified location. This can be used by controllers that have their own static linux IRQ definitions to map a hwirq range to, as well as for platforms that wish to establish 1:1 identity mapping between linux and hwirq space. For insertion to specified ranges by platforms that do their own irq_desc management: - irq_domain_associate() - irq_domain_associate_many() These in turn call back in to the domain's ->map() routine, for further processing by the platform. Disassociation of IRQs get handled through irq_dispose_mapping() as normal. With these in place it should be possible to begin migration of legacy IRQ domains to linear ones, without requiring special handling for static vs dynamic IRQ definitions in DT vs non-DT paths. This also makes it possible for domains with static mappings to adopt whichever tree model best fits their needs, rather than simply restricting them to linear revmaps. Signed-off-by: Paul Mundt [grant.likely: Reorganized irq_domain_associate{,_many} to have all logic in one place] [grant.likely: Add error checking for unallocated irq_descs at associate time] Signed-off-by: Grant Likely Cc: Benjamin Herrenschmidt Cc: Thomas Gleixner Cc: Rob Herring --- include/linux/irqdomain.h | 19 ++++++++ kernel/irq/irqdomain.c | 113 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 107 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 17b60be30fff..eab8a0e60b8e 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -149,12 +149,31 @@ static inline struct irq_domain *irq_domain_add_legacy_isa( extern void irq_domain_remove(struct irq_domain *host); +extern int irq_domain_associate_many(struct irq_domain *domain, + unsigned int irq_base, + irq_hw_number_t hwirq_base, int count); +static inline int irq_domain_associate(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + return irq_domain_associate_many(domain, irq, hwirq, 1); +} + extern unsigned int irq_create_mapping(struct irq_domain *host, irq_hw_number_t hwirq); extern void irq_dispose_mapping(unsigned int virq); extern unsigned int irq_find_mapping(struct irq_domain *host, irq_hw_number_t hwirq); extern unsigned int irq_create_direct_mapping(struct irq_domain *host); +extern int irq_create_strict_mappings(struct irq_domain *domain, + unsigned int irq_base, + irq_hw_number_t hwirq_base, int count); + +static inline int irq_create_identity_mapping(struct irq_domain *host, + irq_hw_number_t hwirq) +{ + return irq_create_strict_mappings(host, hwirq, hwirq, 1); +} + extern void irq_radix_revmap_insert(struct irq_domain *host, unsigned int virq, irq_hw_number_t hwirq); extern unsigned int irq_radix_revmap_lookup(struct irq_domain *host, diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 091732c9dbdc..a07d92446b66 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -410,36 +410,61 @@ static void irq_domain_disassociate_many(struct irq_domain *domain, } } -static int irq_setup_virq(struct irq_domain *domain, unsigned int virq, - irq_hw_number_t hwirq) +int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base, + irq_hw_number_t hwirq_base, int count) { - struct irq_data *irq_data = irq_get_irq_data(virq); + unsigned int virq = irq_base; + irq_hw_number_t hwirq = hwirq_base; + int i; - irq_data->hwirq = hwirq; - irq_data->domain = domain; - if (domain->ops->map && domain->ops->map(domain, virq, hwirq)) { - pr_err("irq-%i==>hwirq-0x%lx mapping failed\n", virq, hwirq); - irq_data->domain = NULL; - irq_data->hwirq = 0; - return -1; - } + pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__, + of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count); - switch (domain->revmap_type) { - case IRQ_DOMAIN_MAP_LINEAR: - if (hwirq < domain->revmap_data.linear.size) - domain->revmap_data.linear.revmap[hwirq] = virq; - break; - case IRQ_DOMAIN_MAP_TREE: - mutex_lock(&revmap_trees_mutex); - irq_radix_revmap_insert(domain, virq, hwirq); - mutex_unlock(&revmap_trees_mutex); - break; - } + for (i = 0; i < count; i++) { + struct irq_data *irq_data = irq_get_irq_data(virq + i); + + if (WARN(!irq_data, "error: irq_desc not allocated; " + "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i)) + return -EINVAL; + if (WARN(irq_data->domain, "error: irq_desc already associated; " + "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i)) + return -EINVAL; + }; + + for (i = 0; i < count; i++, virq++, hwirq++) { + struct irq_data *irq_data = irq_get_irq_data(virq); + + irq_data->hwirq = hwirq; + irq_data->domain = domain; + if (domain->ops->map && domain->ops->map(domain, virq, hwirq)) { + pr_err("irq-%i==>hwirq-0x%lx mapping failed\n", virq, hwirq); + irq_data->domain = NULL; + irq_data->hwirq = 0; + goto err_unmap; + } + + switch (domain->revmap_type) { + case IRQ_DOMAIN_MAP_LINEAR: + if (hwirq < domain->revmap_data.linear.size) + domain->revmap_data.linear.revmap[hwirq] = virq; + break; + case IRQ_DOMAIN_MAP_TREE: + mutex_lock(&revmap_trees_mutex); + irq_radix_revmap_insert(domain, virq, hwirq); + mutex_unlock(&revmap_trees_mutex); + break; + } - irq_clear_status_flags(virq, IRQ_NOREQUEST); + irq_clear_status_flags(virq, IRQ_NOREQUEST); + } return 0; + + err_unmap: + irq_domain_disassociate_many(domain, irq_base, i); + return -EINVAL; } +EXPORT_SYMBOL_GPL(irq_domain_associate_many); /** * irq_create_direct_mapping() - Allocate an irq for direct mapping @@ -472,7 +497,7 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain) } pr_debug("create_direct obtained virq %d\n", virq); - if (irq_setup_virq(domain, virq, virq)) { + if (irq_domain_associate(domain, virq, virq)) { irq_free_desc(virq); return 0; } @@ -533,7 +558,7 @@ unsigned int irq_create_mapping(struct irq_domain *domain, return 0; } - if (irq_setup_virq(domain, virq, hwirq)) { + if (irq_domain_associate(domain, virq, hwirq)) { irq_free_desc(virq); return 0; } @@ -545,6 +570,44 @@ unsigned int irq_create_mapping(struct irq_domain *domain, } EXPORT_SYMBOL_GPL(irq_create_mapping); +/** + * irq_create_strict_mappings() - Map a range of hw irqs to fixed linux irqs + * @domain: domain owning the interrupt range + * @irq_base: beginning of linux IRQ range + * @hwirq_base: beginning of hardware IRQ range + * @count: Number of interrupts to map + * + * This routine is used for allocating and mapping a range of hardware + * irqs to linux irqs where the linux irq numbers are at pre-defined + * locations. For use by controllers that already have static mappings + * to insert in to the domain. + * + * Non-linear users can use irq_create_identity_mapping() for IRQ-at-a-time + * domain insertion. + * + * 0 is returned upon success, while any failure to establish a static + * mapping is treated as an error. + */ +int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base, + irq_hw_number_t hwirq_base, int count) +{ + int ret; + + ret = irq_alloc_descs(irq_base, irq_base, count, + of_node_to_nid(domain->of_node)); + if (unlikely(ret < 0)) + return ret; + + ret = irq_domain_associate_many(domain, irq_base, hwirq_base, count); + if (unlikely(ret < 0)) { + irq_free_descs(irq_base, count); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(irq_create_strict_mappings); + unsigned int irq_create_of_mapping(struct device_node *controller, const u32 *intspec, unsigned int intsize) { -- cgit v1.2.3 From d6b0d1f7058f7cf818138cd7fd116dca3f3576d9 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sun, 3 Jun 2012 22:04:37 -0700 Subject: irqdomain: Eliminate dedicated radix lookup functions In preparation to remove the slow revmap path, eliminate the public radix revmap lookup functions. This simplifies the code and makes the slowpath removal patch a lot simpler. Signed-off-by: Grant Likely Cc: Paul Mundt Cc: Benjamin Herrenschmidt Cc: Thomas Gleixner Cc: Rob Herring --- arch/powerpc/sysdev/xics/icp-hv.c | 2 +- arch/powerpc/sysdev/xics/icp-native.c | 2 +- include/linux/irqdomain.h | 4 --- kernel/irq/irqdomain.c | 60 +---------------------------------- 4 files changed, 3 insertions(+), 65 deletions(-) (limited to 'include') diff --git a/arch/powerpc/sysdev/xics/icp-hv.c b/arch/powerpc/sysdev/xics/icp-hv.c index 253dce98c16e..14469cf9df68 100644 --- a/arch/powerpc/sysdev/xics/icp-hv.c +++ b/arch/powerpc/sysdev/xics/icp-hv.c @@ -111,7 +111,7 @@ static unsigned int icp_hv_get_irq(void) if (vec == XICS_IRQ_SPURIOUS) return NO_IRQ; - irq = irq_radix_revmap_lookup(xics_host, vec); + irq = irq_find_mapping(xics_host, vec); if (likely(irq != NO_IRQ)) { xics_push_cppr(vec); return irq; diff --git a/arch/powerpc/sysdev/xics/icp-native.c b/arch/powerpc/sysdev/xics/icp-native.c index 4c79b6fbee1c..48861d3fcd07 100644 --- a/arch/powerpc/sysdev/xics/icp-native.c +++ b/arch/powerpc/sysdev/xics/icp-native.c @@ -119,7 +119,7 @@ static unsigned int icp_native_get_irq(void) if (vec == XICS_IRQ_SPURIOUS) return NO_IRQ; - irq = irq_radix_revmap_lookup(xics_host, vec); + irq = irq_find_mapping(xics_host, vec); if (likely(irq != NO_IRQ)) { xics_push_cppr(vec); return irq; diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index eab8a0e60b8e..0d5b17bf5e51 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -174,10 +174,6 @@ static inline int irq_create_identity_mapping(struct irq_domain *host, return irq_create_strict_mappings(host, hwirq, hwirq, 1); } -extern void irq_radix_revmap_insert(struct irq_domain *host, unsigned int virq, - irq_hw_number_t hwirq); -extern unsigned int irq_radix_revmap_lookup(struct irq_domain *host, - irq_hw_number_t hwirq); extern unsigned int irq_linear_revmap(struct irq_domain *host, irq_hw_number_t hwirq); diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index a07d92446b66..f540bb1eff84 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -450,7 +450,7 @@ int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base, break; case IRQ_DOMAIN_MAP_TREE: mutex_lock(&revmap_trees_mutex); - irq_radix_revmap_insert(domain, virq, hwirq); + radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data); mutex_unlock(&revmap_trees_mutex); break; } @@ -723,64 +723,6 @@ unsigned int irq_find_mapping(struct irq_domain *domain, } EXPORT_SYMBOL_GPL(irq_find_mapping); -/** - * irq_radix_revmap_lookup() - Find a linux irq from a hw irq number. - * @domain: domain owning this hardware interrupt - * @hwirq: hardware irq number in that domain space - * - * This is a fast path, for use by irq controller code that uses radix tree - * revmaps - */ -unsigned int irq_radix_revmap_lookup(struct irq_domain *domain, - irq_hw_number_t hwirq) -{ - struct irq_data *irq_data; - - if (WARN_ON_ONCE(domain->revmap_type != IRQ_DOMAIN_MAP_TREE)) - return irq_find_mapping(domain, hwirq); - - /* - * Freeing an irq can delete nodes along the path to - * do the lookup via call_rcu. - */ - rcu_read_lock(); - irq_data = radix_tree_lookup(&domain->revmap_data.tree, hwirq); - rcu_read_unlock(); - - /* - * If found in radix tree, then fine. - * Else fallback to linear lookup - this should not happen in practice - * as it means that we failed to insert the node in the radix tree. - */ - return irq_data ? irq_data->irq : irq_find_mapping(domain, hwirq); -} -EXPORT_SYMBOL_GPL(irq_radix_revmap_lookup); - -/** - * irq_radix_revmap_insert() - Insert a hw irq to linux irq number mapping. - * @domain: domain owning this hardware interrupt - * @virq: linux irq number - * @hwirq: hardware irq number in that domain space - * - * This is for use by irq controllers that use a radix tree reverse - * mapping for fast lookup. - */ -void irq_radix_revmap_insert(struct irq_domain *domain, unsigned int virq, - irq_hw_number_t hwirq) -{ - struct irq_data *irq_data = irq_get_irq_data(virq); - - if (WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_TREE)) - return; - - if (virq) { - mutex_lock(&revmap_trees_mutex); - radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data); - mutex_unlock(&revmap_trees_mutex); - } -} -EXPORT_SYMBOL_GPL(irq_radix_revmap_insert); - /** * irq_linear_revmap() - Find a linux irq from a hw irq number. * @domain: domain owning this hardware interrupt -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 From d9c2ede63c74048dfddbb129c59ac01176b0ab71 Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Fri, 6 Jul 2012 21:31:56 +0200 Subject: sunrpc/cache.h: fix coding style Neaten code style in get_int(). Also use sizeof() instead of hard coded number as suggested by Joe Perches . Cc: Joe Perches Signed-off-by: Eldad Zack Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/cache.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index f5fd6160dbca..6def1f6cc269 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -219,11 +219,17 @@ static inline int get_int(char **bpp, int *anint) char buf[50]; char *ep; int rv; - int len = qword_get(bpp, buf, 50); - if (len < 0) return -EINVAL; - if (len ==0) return -ENOENT; + int len = qword_get(bpp, buf, sizeof(buf)); + + if (len < 0) + return -EINVAL; + if (len == 0) + return -ENOENT; + rv = simple_strtol(buf, &ep, 0); - if (*ep) return -EINVAL; + if (*ep) + return -EINVAL; + *anint = rv; return 0; } -- cgit v1.2.3 From bbf43dc888833ac0539e437dbaeb28bfd4fbab9f Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Fri, 6 Jul 2012 21:31:57 +0200 Subject: sunrpc/cache.h: replace simple_strtoul This patch replaces the usage of simple_strtoul with kstrtoint in get_int(), since the simple_str* family doesn't account for overflow and is deprecated. Also, in this specific case, the long from strtol is silently converted to an int by the caller. As Joe Perches suggested, this patch also removes the redundant temporary variable rv, since kstrtoint() will not write to anint unless it's successful. Cc: Joe Perches Signed-off-by: Eldad Zack Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/cache.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index 6def1f6cc269..af42596a82f9 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -217,8 +217,6 @@ extern int qword_get(char **bpp, char *dest, int bufsize); static inline int get_int(char **bpp, int *anint) { char buf[50]; - char *ep; - int rv; int len = qword_get(bpp, buf, sizeof(buf)); if (len < 0) @@ -226,11 +224,9 @@ static inline int get_int(char **bpp, int *anint) if (len == 0) return -ENOENT; - rv = simple_strtol(buf, &ep, 0); - if (*ep) + if (kstrtoint(buf, 0, anint)) return -EINVAL; - *anint = rv; return 0; } -- cgit v1.2.3 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') 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 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') 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 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') 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 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') 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 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') 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
+
+ V4L2 in Linux 3.6 + + + Added V4L2_CAP_VIDEO_M2M and V4L2_CAP_VIDEO_M2M_MPLANE capabilities. + + +
+
Relation of V4L2 to other Linux multimedia APIs diff --git a/Documentation/DocBook/media/v4l/vidioc-querycap.xml b/Documentation/DocBook/media/v4l/vidioc-querycap.xml index 4643505cd4ca..f33dd746b66b 100644 --- a/Documentation/DocBook/media/v4l/vidioc-querycap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-querycap.xml @@ -191,6 +191,19 @@ linkend="output">Video Output interface. multi-planar API through the Video Output interface. + + V4L2_CAP_VIDEO_M2M + 0x00004000 + The device supports the single-planar API through the + Video Memory-To-Memory interface. + + + V4L2_CAP_VIDEO_M2M_MPLANE + 0x00008000 + The device supports the + multi-planar API through the + Video Memory-To-Memory interface. + V4L2_CAP_VIDEO_OVERLAY 0x00000004 diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c index 7fdee8fcf3f7..7efe9ad7acc7 100644 --- a/drivers/media/video/mem2mem_testdev.c +++ b/drivers/media/video/mem2mem_testdev.c @@ -431,8 +431,7 @@ static int vidioc_querycap(struct file *file, void *priv, strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1); strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1); strlcpy(cap->bus_info, MEM2MEM_NAME, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT - | V4L2_CAP_STREAMING; + cap->capabilities = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } diff --git a/drivers/media/video/mx2_emmaprp.c b/drivers/media/video/mx2_emmaprp.c index 0bd5815de369..5f8a6f5b98f9 100644 --- a/drivers/media/video/mx2_emmaprp.c +++ b/drivers/media/video/mx2_emmaprp.c @@ -396,9 +396,13 @@ static int vidioc_querycap(struct file *file, void *priv, { strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1); strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT - | V4L2_CAP_STREAMING; - + /* + * This is only a mem-to-mem video device. The capture and output + * device capability flags are left only for backward compatibility + * and are scheduled for removal. + */ + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | + V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; return 0; } diff --git a/drivers/media/video/s5p-fimc/fimc-m2m.c b/drivers/media/video/s5p-fimc/fimc-m2m.c index 41eda2e27488..c587011d80ef 100644 --- a/drivers/media/video/s5p-fimc/fimc-m2m.c +++ b/drivers/media/video/s5p-fimc/fimc-m2m.c @@ -259,7 +259,12 @@ static int fimc_m2m_querycap(struct file *file, void *fh, strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); cap->bus_info[0] = 0; - cap->capabilities = V4L2_CAP_STREAMING | + /* + * This is only a mem-to-mem video device. The capture and output + * device capability flags are left only for backward compatibility + * and are scheduled for removal. + */ + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; return 0; diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c index 7c98ee7377ee..7c2200435206 100644 --- a/drivers/media/video/s5p-g2d/g2d.c +++ b/drivers/media/video/s5p-g2d/g2d.c @@ -290,8 +290,13 @@ static int vidioc_querycap(struct file *file, void *priv, strncpy(cap->card, G2D_NAME, sizeof(cap->card) - 1); cap->bus_info[0] = 0; cap->version = KERNEL_VERSION(1, 0, 0); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT - | V4L2_CAP_STREAMING; + /* + * This is only a mem-to-mem video device. The capture and output + * device capability flags are left only for backward compatibility + * and are scheduled for removal. + */ + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | + V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; return 0; } diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c index 95f23024b17d..813b801238d1 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.c +++ b/drivers/media/video/s5p-jpeg/jpeg-core.c @@ -489,9 +489,13 @@ static int s5p_jpeg_querycap(struct file *file, void *priv, sizeof(cap->card)); } cap->bus_info[0] = 0; - cap->capabilities = V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_VIDEO_OUTPUT; + /* + * This is only a mem-to-mem video device. The capture and output + * device capability flags are left only for backward compatibility + * and are scheduled for removal. + */ + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT; return 0; } diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c index feea867f318c..c5d567f87d77 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c @@ -220,8 +220,14 @@ static int vidioc_querycap(struct file *file, void *priv, strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1); cap->bus_info[0] = 0; cap->version = KERNEL_VERSION(1, 0, 0); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE | - V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING; + /* + * This is only a mem-to-mem video device. The capture and output + * device capability flags are left only for backward compatibility + * and are scheduled for removal. + */ + cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_VIDEO_OUTPUT_MPLANE; return 0; } diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c index 158b78989b89..aa1c244cf66e 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c @@ -779,9 +779,14 @@ static int vidioc_querycap(struct file *file, void *priv, strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1); cap->bus_info[0] = 0; cap->version = KERNEL_VERSION(1, 0, 0); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE - | V4L2_CAP_VIDEO_OUTPUT_MPLANE - | V4L2_CAP_STREAMING; + /* + * This is only a mem-to-mem video device. The capture and output + * device capability flags are left only for backward compatibility + * and are scheduled for removal. + */ + cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_VIDEO_OUTPUT_MPLANE; return 0; } diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 5d78910f926c..4cf766e6f120 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -274,6 +274,10 @@ struct v4l2_capability { #define V4L2_CAP_VIDEO_CAPTURE_MPLANE 0x00001000 /* Is a video output device that supports multiplanar formats */ #define V4L2_CAP_VIDEO_OUTPUT_MPLANE 0x00002000 +/* Is a video mem-to-mem device that supports multiplanar formats */ +#define V4L2_CAP_VIDEO_M2M_MPLANE 0x00004000 +/* Is a video mem-to-mem device */ +#define V4L2_CAP_VIDEO_M2M 0x00008000 #define V4L2_CAP_TUNER 0x00010000 /* has a tuner */ #define V4L2_CAP_AUDIO 0x00020000 /* has audio support */ -- cgit v1.2.3 From ab7017a3a0a64b953e091619c30413b3721d925d Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Mon, 30 Jul 2012 16:05:16 -0400 Subject: NFS: Add version registering framework This patch adds in the code to track multiple versions of the NFS protocol. I created default structures for v2, v3 and v4 so that each version can continue to work while I convert them into kernel modules. I also removed the const parameter from the rpc_version array so that I can change it at runtime. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/Makefile | 4 +- fs/nfs/client.c | 147 +++++++++++++++++++++++++++++++++++----------- fs/nfs/inode.c | 9 +-- fs/nfs/internal.h | 10 ++-- fs/nfs/nfs.h | 72 +++++++++++++++++++++++ fs/nfs/nfs2super.c | 25 ++++++++ fs/nfs/nfs3super.c | 25 ++++++++ fs/nfs/nfs4_fs.h | 1 + fs/nfs/nfs4client.c | 4 +- fs/nfs/nfs4super.c | 14 ++++- fs/nfs/super.c | 32 ++++++---- include/linux/nfs_fs_sb.h | 1 + 12 files changed, 283 insertions(+), 61 deletions(-) create mode 100644 fs/nfs/nfs.h create mode 100644 fs/nfs/nfs2super.c create mode 100644 fs/nfs/nfs3super.c (limited to 'include') diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 0b96c2038346..66dd3075e5db 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -9,8 +9,8 @@ nfs-y := client.o dir.o file.o getroot.o inode.o super.o \ write.o namespace.o mount_clnt.o \ dns_resolve.o cache_lib.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o -nfs-$(CONFIG_NFS_V2) += proc.o nfs2xdr.o -nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o +nfs-$(CONFIG_NFS_V2) += nfs2super.o proc.o nfs2xdr.o +nfs-$(CONFIG_NFS_V3) += nfs3super.o nfs3proc.o nfs3xdr.o nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ nfs4super.o nfs4file.o delegation.o idmap.o \ diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 65afa382c5e3..462de24482b4 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -51,25 +51,23 @@ #include "internal.h" #include "fscache.h" #include "pnfs.h" +#include "nfs.h" #include "netns.h" #define NFSDBG_FACILITY NFSDBG_CLIENT static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); +static DEFINE_SPINLOCK(nfs_version_lock); +static DEFINE_MUTEX(nfs_version_mutex); +static LIST_HEAD(nfs_versions); /* * RPC cruft for NFS */ static const struct rpc_version *nfs_version[5] = { -#ifdef CONFIG_NFS_V2 - [2] = &nfs_version2, -#endif -#ifdef CONFIG_NFS_V3 - [3] = &nfs_version3, -#endif -#ifdef CONFIG_NFS_V4 - [4] = &nfs_version4, -#endif + [2] = NULL, + [3] = NULL, + [4] = NULL, }; const struct rpc_program nfs_program = { @@ -101,6 +99,93 @@ const struct rpc_program nfsacl_program = { }; #endif /* CONFIG_NFS_V3_ACL */ +static struct nfs_subversion *find_nfs_version(unsigned int version) +{ + struct nfs_subversion *nfs; + spin_lock(&nfs_version_lock); + + list_for_each_entry(nfs, &nfs_versions, list) { + if (nfs->rpc_ops->version == version) { + spin_unlock(&nfs_version_lock); + return nfs; + } + }; + + spin_unlock(&nfs_version_lock); + return ERR_PTR(-EPROTONOSUPPORT);; +} + +struct nfs_subversion *get_nfs_version(unsigned int version) +{ + struct nfs_subversion *nfs = find_nfs_version(version); + + if (IS_ERR(nfs)) { + mutex_lock(&nfs_version_mutex); + request_module("nfs%d", version); + nfs = find_nfs_version(version); + mutex_unlock(&nfs_version_mutex); + } + + if (!IS_ERR(nfs)) + try_module_get(nfs->owner); + return nfs; +} + +void put_nfs_version(struct nfs_subversion *nfs) +{ + module_put(nfs->owner); +} + +void register_nfs_version(struct nfs_subversion *nfs) +{ + spin_lock(&nfs_version_lock); + + list_add(&nfs->list, &nfs_versions); + nfs_version[nfs->rpc_ops->version] = nfs->rpc_vers; + + spin_unlock(&nfs_version_lock); +} +EXPORT_SYMBOL_GPL(register_nfs_version); + +void unregister_nfs_version(struct nfs_subversion *nfs) +{ + spin_lock(&nfs_version_lock); + + nfs_version[nfs->rpc_ops->version] = NULL; + list_del(&nfs->list); + + spin_unlock(&nfs_version_lock); +} +EXPORT_SYMBOL_GPL(unregister_nfs_version); + +/* + * Preload all configured NFS versions during module init. + * This function should be edited after each protocol is converted, + * and eventually removed. + */ +int __init nfs_register_versions(void) +{ + int err = init_nfs_v2(); + if (err) + return err; + + err = init_nfs_v3(); + if (err) + return err; + + return init_nfs_v4(); +} + +/* + * Remove each pre-loaded NFS version + */ +void nfs_unregister_versions(void) +{ + exit_nfs_v2(); + exit_nfs_v3(); + exit_nfs_v4(); +} + /* * Allocate a shared client record * @@ -116,7 +201,10 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) goto error_0; - clp->rpc_ops = cl_init->rpc_ops; + clp->cl_nfs_mod = cl_init->nfs_mod; + try_module_get(clp->cl_nfs_mod->owner); + + clp->rpc_ops = clp->cl_nfs_mod->rpc_ops; atomic_set(&clp->cl_count, 1); clp->cl_cons_state = NFS_CS_INITING; @@ -145,6 +233,7 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) return clp; error_cleanup: + put_nfs_version(clp->cl_nfs_mod); kfree(clp); error_0: return ERR_PTR(err); @@ -205,6 +294,7 @@ void nfs_free_client(struct nfs_client *clp) put_rpccred(clp->cl_machine_cred); put_net(clp->cl_net); + put_nfs_version(clp->cl_nfs_mod); kfree(clp->cl_hostname); kfree(clp); @@ -362,7 +452,7 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat continue; /* Different NFS versions cannot share the same nfs_client */ - if (clp->rpc_ops != data->rpc_ops) + if (clp->rpc_ops != data->nfs_mod->rpc_ops) continue; if (clp->cl_proto != data->proto) @@ -431,9 +521,10 @@ nfs_get_client(const struct nfs_client_initdata *cl_init, { struct nfs_client *clp, *new = NULL; struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id); + const struct nfs_rpc_ops *rpc_ops = cl_init->nfs_mod->rpc_ops; dprintk("--> nfs_get_client(%s,v%u)\n", - cl_init->hostname ?: "", cl_init->rpc_ops->version); + cl_init->hostname ?: "", rpc_ops->version); /* see if the client already exists */ do { @@ -450,14 +541,13 @@ nfs_get_client(const struct nfs_client_initdata *cl_init, list_add(&new->cl_share_link, &nn->nfs_client_list); spin_unlock(&nn->nfs_client_lock); new->cl_flags = cl_init->init_flags; - return cl_init->rpc_ops->init_client(new, - timeparms, ip_addr, - authflavour); + return rpc_ops->init_client(new, timeparms, ip_addr, + authflavour); } spin_unlock(&nn->nfs_client_lock); - new = cl_init->rpc_ops->alloc_client(cl_init); + new = rpc_ops->alloc_client(cl_init); } while (!IS_ERR(new)); dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n", @@ -714,13 +804,14 @@ error: * Create a version 2 or 3 client */ static int nfs_init_server(struct nfs_server *server, - const struct nfs_parsed_mount_data *data) + const struct nfs_parsed_mount_data *data, + struct nfs_subversion *nfs_mod) { struct nfs_client_initdata cl_init = { .hostname = data->nfs_server.hostname, .addr = (const struct sockaddr *)&data->nfs_server.address, .addrlen = data->nfs_server.addrlen, - .rpc_ops = NULL, + .nfs_mod = nfs_mod, .proto = data->nfs_server.protocol, .net = data->net, }; @@ -730,21 +821,6 @@ static int nfs_init_server(struct nfs_server *server, dprintk("--> nfs_init_server()\n"); - switch (data->version) { -#ifdef CONFIG_NFS_V2 - case 2: - cl_init.rpc_ops = &nfs_v2_clientops; - break; -#endif -#ifdef CONFIG_NFS_V3 - case 3: - cl_init.rpc_ops = &nfs_v3_clientops; - break; -#endif - default: - return -EPROTONOSUPPORT; - } - nfs_init_timeout_values(&timeparms, data->nfs_server.protocol, data->timeo, data->retrans); if (data->flags & NFS_MOUNT_NORESVPORT) @@ -1033,7 +1109,8 @@ void nfs_free_server(struct nfs_server *server) * - keyed on server and FSID */ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, - struct nfs_fh *mntfh) + struct nfs_fh *mntfh, + struct nfs_subversion *nfs_mod) { struct nfs_server *server; struct nfs_fattr *fattr; @@ -1049,7 +1126,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, goto error; /* Get a client representation */ - error = nfs_init_server(server, data); + error = nfs_init_server(server, data, nfs_mod); if (error < 0) goto error; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 35f7e4bc680e..e8877c82582d 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -50,6 +50,7 @@ #include "fscache.h" #include "dns_resolve.h" #include "pnfs.h" +#include "nfs.h" #include "netns.h" #define NFSDBG_FACILITY NFSDBG_VFS @@ -1671,21 +1672,17 @@ static int __init init_nfs_fs(void) rpc_proc_register(&init_net, &nfs_rpcstat); #endif -#ifdef CONFIG_NFS_V4 - err = init_nfs_v4(); + err = nfs_register_versions(); if (err) goto out1; -#endif if ((err = register_nfs_fs()) != 0) goto out0; return 0; out0: -#ifdef CONFIG_NFS_V4 - exit_nfs_v4(); + nfs_unregister_versions(); out1: -#endif #ifdef CONFIG_PROC_FS rpc_proc_unregister(&init_net, "nfs"); #endif diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index cfafd13b6fe9..ac936476b3bc 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -90,7 +90,7 @@ struct nfs_client_initdata { const char *hostname; const struct sockaddr *addr; size_t addrlen; - const struct nfs_rpc_ops *rpc_ops; + struct nfs_subversion *nfs_mod; int proto; u32 minorversion; struct net *net; @@ -189,7 +189,8 @@ nfs4_find_client_sessionid(struct net *, const struct sockaddr *, struct nfs4_sessionid *); extern struct nfs_server *nfs_create_server( const struct nfs_parsed_mount_data *, - struct nfs_fh *); + struct nfs_fh *, + struct nfs_subversion *); extern struct nfs_server *nfs4_create_server( const struct nfs_parsed_mount_data *, struct nfs_fh *); @@ -321,6 +322,7 @@ void nfs_zap_acl_cache(struct inode *inode); extern int nfs_wait_bit_killable(void *word); /* super.c */ +extern struct file_system_type nfs_fs_type; extern struct file_system_type nfs_xdev_fs_type; #ifdef CONFIG_NFS_V4 extern struct file_system_type nfs4_xdev_fs_type; @@ -329,8 +331,8 @@ extern struct file_system_type nfs4_referral_fs_type; void nfs_initialise_sb(struct super_block *); int nfs_set_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *); int nfs_clone_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *); -struct dentry *nfs_fs_mount_common(struct file_system_type *, struct nfs_server *, - int, const char *, struct nfs_mount_info *); +struct dentry *nfs_fs_mount_common(struct nfs_server *, int, const char *, + struct nfs_mount_info *, struct nfs_subversion *); struct dentry *nfs_fs_mount(struct file_system_type *, int, const char *, void *); struct dentry * nfs_xdev_mount_common(struct file_system_type *, int, const char *, struct nfs_mount_info *); diff --git a/fs/nfs/nfs.h b/fs/nfs/nfs.h new file mode 100644 index 000000000000..ac10b9e6c920 --- /dev/null +++ b/fs/nfs/nfs.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2012 Netapp, Inc. All rights reserved. + * + * Function and structures exported by the NFS module + * for use by NFS version-specific modules. + */ +#ifndef __LINUX_INTERNAL_NFS_H +#define __LINUX_INTERNAL_NFS_H + +#include +#include +#include + +struct nfs_subversion { + struct module *owner; /* THIS_MODULE pointer */ + struct file_system_type *nfs_fs; /* NFS filesystem type */ + const struct rpc_version *rpc_vers; /* NFS version information */ + const struct nfs_rpc_ops *rpc_ops; /* NFS operations */ + struct list_head list; /* List of NFS versions */ +}; + +int nfs_register_versions(void); +void nfs_unregister_versions(void); + +#ifdef CONFIG_NFS_V2 +int init_nfs_v2(void); +void exit_nfs_v2(void); +#else /* CONFIG_NFS_V2 */ +static inline int __init init_nfs_v2(void) +{ + return 0; +} + +static inline void exit_nfs_v2(void) +{ +} +#endif /* CONFIG_NFS_V2 */ + +#ifdef CONFIG_NFS_V3 +int init_nfs_v3(void); +void exit_nfs_v3(void); +#else /* CONFIG_NFS_V3 */ +static inline int __init init_nfs_v3(void) +{ + return 0; +} + +static inline void exit_nfs_v3(void) +{ +} +#endif /* CONFIG_NFS_V3 */ + +#ifdef CONFIG_NFS_V4 +int init_nfs_v4(void); +void exit_nfs_v4(void); +#else /* CONFIG_NFS_V4 */ +static inline int __init init_nfs_v4(void) +{ + return 0; +} + +static inline void exit_nfs_v4(void) +{ +} +#endif /* CONFIG_NFS_V4 */ + +struct nfs_subversion *get_nfs_version(unsigned int); +void put_nfs_version(struct nfs_subversion *); +void register_nfs_version(struct nfs_subversion *); +void unregister_nfs_version(struct nfs_subversion *); + +#endif /* __LINUX_INTERNAL_NFS_H */ diff --git a/fs/nfs/nfs2super.c b/fs/nfs/nfs2super.c new file mode 100644 index 000000000000..cef06d42334a --- /dev/null +++ b/fs/nfs/nfs2super.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2012 Netapp, Inc. All rights reserved. + */ +#include +#include +#include "internal.h" +#include "nfs.h" + +static struct nfs_subversion nfs_v2 = { + .owner = THIS_MODULE, + .nfs_fs = &nfs_fs_type, + .rpc_vers = &nfs_version2, + .rpc_ops = &nfs_v2_clientops, +}; + +int __init init_nfs_v2(void) +{ + register_nfs_version(&nfs_v2); + return 0; +} + +void exit_nfs_v2(void) +{ + unregister_nfs_version(&nfs_v2); +} diff --git a/fs/nfs/nfs3super.c b/fs/nfs/nfs3super.c new file mode 100644 index 000000000000..f815cf359d97 --- /dev/null +++ b/fs/nfs/nfs3super.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2012 Netapp, Inc. All rights reserved. + */ +#include +#include +#include "internal.h" +#include "nfs.h" + +static struct nfs_subversion nfs_v3 = { + .owner = THIS_MODULE, + .nfs_fs = &nfs_fs_type, + .rpc_vers = &nfs_version3, + .rpc_ops = &nfs_v3_clientops, +}; + +int __init init_nfs_v3(void) +{ + register_nfs_version(&nfs_v3); + return 0; +} + +void exit_nfs_v3(void) +{ + unregister_nfs_version(&nfs_v3); +} diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 5511690de8a5..99c2e7e4d3ea 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -366,6 +366,7 @@ extern const nfs4_stateid zero_stateid; /* nfs4super.c */ struct nfs_mount_info; +extern struct nfs_subversion nfs_v4; struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *); int init_nfs_v4(void); void exit_nfs_v4(void); diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 1c3f13c8e472..769e798b3959 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -357,7 +357,7 @@ static int nfs4_set_client(struct nfs_server *server, .hostname = hostname, .addr = addr, .addrlen = addrlen, - .rpc_ops = &nfs_v4_clientops, + .nfs_mod = &nfs_v4, .proto = proto, .minorversion = minorversion, .net = net, @@ -411,7 +411,7 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp, struct nfs_client_initdata cl_init = { .addr = ds_addr, .addrlen = ds_addrlen, - .rpc_ops = &nfs_v4_clientops, + .nfs_mod = &nfs_v4, .proto = ds_proto, .minorversion = mds_clp->cl_minorversion, .net = mds_clp->cl_net, diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c index 59264fb335c8..1f3401902c2f 100644 --- a/fs/nfs/nfs4super.c +++ b/fs/nfs/nfs4super.c @@ -8,6 +8,7 @@ #include #include "internal.h" #include "nfs4_fs.h" +#include "nfs.h" #define NFSDBG_FACILITY NFSDBG_VFS @@ -75,6 +76,13 @@ static const struct super_operations nfs4_sops = { .remount_fs = nfs_remount, }; +struct nfs_subversion nfs_v4 = { + .owner = THIS_MODULE, + .nfs_fs = &nfs4_fs_type, + .rpc_vers = &nfs_version4, + .rpc_ops = &nfs_v4_clientops, +}; + /* * Set up an NFS4 superblock */ @@ -113,7 +121,7 @@ nfs4_remote_mount(struct file_system_type *fs_type, int flags, goto out; } - mntroot = nfs_fs_mount_common(fs_type, server, flags, dev_name, mount_info); + mntroot = nfs_fs_mount_common(server, flags, dev_name, mount_info, &nfs_v4); out: return mntroot; @@ -293,7 +301,7 @@ nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags, goto out; } - mntroot = nfs_fs_mount_common(&nfs4_fs_type, server, flags, dev_name, &mount_info); + mntroot = nfs_fs_mount_common(server, flags, dev_name, &mount_info, &nfs_v4); out: nfs_free_fhandle(mount_info.mntfh); return mntroot; @@ -343,6 +351,7 @@ int __init init_nfs_v4(void) if (err < 0) goto out2; + register_nfs_version(&nfs_v4); return 0; out2: nfs4_unregister_sysctl(); @@ -354,6 +363,7 @@ out: void exit_nfs_v4(void) { + unregister_nfs_version(&nfs_v4); unregister_filesystem(&nfs4_fs_type); nfs4_unregister_sysctl(); nfs_idmap_quit(); diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 95866a8c21bb..61405a7a6b3c 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -64,6 +64,7 @@ #include "internal.h" #include "fscache.h" #include "pnfs.h" +#include "nfs.h" #define NFSDBG_FACILITY NFSDBG_VFS #define NFS_TEXT_DATA 1 @@ -281,7 +282,7 @@ static match_table_t nfs_vers_tokens = { static struct dentry *nfs_xdev_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data); -static struct file_system_type nfs_fs_type = { +struct file_system_type nfs_fs_type = { .owner = THIS_MODULE, .name = "nfs", .mount = nfs_fs_mount, @@ -1650,7 +1651,8 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args, } static struct dentry *nfs_try_mount(int flags, const char *dev_name, - struct nfs_mount_info *mount_info) + struct nfs_mount_info *mount_info, + struct nfs_subversion *nfs_mod) { int status; struct nfs_server *server; @@ -1662,11 +1664,11 @@ static struct dentry *nfs_try_mount(int flags, const char *dev_name, } /* Get a volume representation */ - server = nfs_create_server(mount_info->parsed, mount_info->mntfh); + server = nfs_create_server(mount_info->parsed, mount_info->mntfh, nfs_mod); if (IS_ERR(server)) return ERR_CAST(server); - return nfs_fs_mount_common(&nfs_fs_type, server, flags, dev_name, mount_info); + return nfs_fs_mount_common(server, flags, dev_name, mount_info, nfs_mod); } /* @@ -2297,10 +2299,10 @@ int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot, return 0; } -struct dentry *nfs_fs_mount_common(struct file_system_type *fs_type, - struct nfs_server *server, +struct dentry *nfs_fs_mount_common(struct nfs_server *server, int flags, const char *dev_name, - struct nfs_mount_info *mount_info) + struct nfs_mount_info *mount_info, + struct nfs_subversion *nfs_mod) { struct super_block *s; struct dentry *mntroot = ERR_PTR(-ENOMEM); @@ -2319,7 +2321,7 @@ struct dentry *nfs_fs_mount_common(struct file_system_type *fs_type, sb_mntdata.mntflags |= MS_SYNCHRONOUS; /* Get a superblock - note that we may end up sharing one that already exists */ - s = sget(fs_type, compare_super, nfs_set_super, flags, &sb_mntdata); + s = sget(nfs_mod->nfs_fs, compare_super, nfs_set_super, flags, &sb_mntdata); if (IS_ERR(s)) { mntroot = ERR_CAST(s); goto out_err_nosb; @@ -2378,6 +2380,7 @@ struct dentry *nfs_fs_mount(struct file_system_type *fs_type, .set_security = nfs_set_sb_security, }; struct dentry *mntroot = ERR_PTR(-ENOMEM); + struct nfs_subversion *nfs_mod; int error; mount_info.parsed = nfs_alloc_parsed_mount_data(); @@ -2394,12 +2397,20 @@ struct dentry *nfs_fs_mount(struct file_system_type *fs_type, goto out; } + nfs_mod = get_nfs_version(mount_info.parsed->version); + if (IS_ERR(nfs_mod)) { + mntroot = ERR_CAST(nfs_mod); + goto out; + } + #ifdef CONFIG_NFS_V4 if (mount_info.parsed->version == 4) mntroot = nfs4_try_mount(flags, dev_name, &mount_info); else #endif /* CONFIG_NFS_V4 */ - mntroot = nfs_try_mount(flags, dev_name, &mount_info); + mntroot = nfs_try_mount(flags, dev_name, &mount_info, nfs_mod); + + put_nfs_version(nfs_mod); out: nfs_free_parsed_mount_data(mount_info.parsed); @@ -2440,6 +2451,7 @@ nfs_xdev_mount_common(struct file_system_type *fs_type, int flags, struct nfs_clone_mount *data = mount_info->cloned; struct nfs_server *server; struct dentry *mntroot = ERR_PTR(-ENOMEM); + struct nfs_subversion *nfs_mod = NFS_SB(data->sb)->nfs_client->cl_nfs_mod; int error; dprintk("--> nfs_xdev_mount_common()\n"); @@ -2453,7 +2465,7 @@ nfs_xdev_mount_common(struct file_system_type *fs_type, int flags, goto out_err; } - mntroot = nfs_fs_mount_common(fs_type, server, flags, dev_name, mount_info); + mntroot = nfs_fs_mount_common(server, flags, dev_name, mount_info, nfs_mod); dprintk("<-- nfs_xdev_mount_common() = 0\n"); out: return mntroot; diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 65327652c61a..6039297801f4 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -48,6 +48,7 @@ struct nfs_client { struct rpc_clnt * cl_rpcclient; const struct nfs_rpc_ops *rpc_ops; /* NFS protocol vector */ int cl_proto; /* Network transport protocol */ + struct nfs_subversion * cl_nfs_mod; /* pointer to nfs version module */ u32 cl_minorversion;/* NFSv4 minorversion */ struct rpc_cred *cl_machine_cred; -- cgit v1.2.3 From ff9099f26645818563c8d396a154c2ce6ee422eb Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Mon, 30 Jul 2012 16:05:18 -0400 Subject: NFS: Create a try_mount rpc op I'm already looking up the nfs subversion in nfs_fs_mount(), so I have easy access to rpc_ops that used to be difficult to reach. This allows me to set up a different mount path for NFS v2/3 and NFS v4. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/internal.h | 2 ++ fs/nfs/nfs3proc.c | 1 + fs/nfs/nfs4_fs.h | 2 +- fs/nfs/nfs4proc.c | 1 + fs/nfs/nfs4super.c | 3 ++- fs/nfs/proc.c | 1 + fs/nfs/super.c | 14 ++++---------- include/linux/nfs_xdr.h | 4 ++++ 8 files changed, 16 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index ac936476b3bc..3364eccd17ef 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -328,6 +328,8 @@ extern struct file_system_type nfs_xdev_fs_type; extern struct file_system_type nfs4_xdev_fs_type; extern struct file_system_type nfs4_referral_fs_type; #endif +struct dentry *nfs_try_mount(int, const char *, struct nfs_mount_info *, + struct nfs_subversion *); void nfs_initialise_sb(struct super_block *); int nfs_set_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *); int nfs_clone_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 65d23eb92fe0..4f4cb8e49716 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -925,6 +925,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .file_ops = &nfs_file_operations, .getroot = nfs3_proc_get_root, .submount = nfs_submount, + .try_mount = nfs_try_mount, .getattr = nfs3_proc_getattr, .setattr = nfs3_proc_setattr, .lookup = nfs3_proc_lookup, diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 99c2e7e4d3ea..c321fb59d801 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -367,7 +367,7 @@ extern const nfs4_stateid zero_stateid; /* nfs4super.c */ struct nfs_mount_info; extern struct nfs_subversion nfs_v4; -struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *); +struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *, struct nfs_subversion *); int init_nfs_v4(void); void exit_nfs_v4(void); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 6843e0a37de8..eb4ba1d99df9 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6870,6 +6870,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .file_ops = &nfs4_file_operations, .getroot = nfs4_proc_get_root, .submount = nfs4_submount, + .try_mount = nfs4_try_mount, .getattr = nfs4_proc_getattr, .setattr = nfs4_proc_setattr, .lookup = nfs4_proc_lookup, diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c index 8a505573c289..9384f666b6ab 100644 --- a/fs/nfs/nfs4super.c +++ b/fs/nfs/nfs4super.c @@ -226,7 +226,8 @@ static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt, } struct dentry *nfs4_try_mount(int flags, const char *dev_name, - struct nfs_mount_info *mount_info) + struct nfs_mount_info *mount_info, + struct nfs_subversion *nfs_mod) { char *export_path; struct vfsmount *root_mnt; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 4d3356af3309..ebb3d9c5227b 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -774,6 +774,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .file_ops = &nfs_file_operations, .getroot = nfs_proc_get_root, .submount = nfs_submount, + .try_mount = nfs_try_mount, .getattr = nfs_proc_getattr, .setattr = nfs_proc_setattr, .lookup = nfs_proc_lookup, diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 4faefa19a8c3..5fca59d73e40 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1650,9 +1650,9 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args, return nfs_walk_authlist(args, &request); } -static struct dentry *nfs_try_mount(int flags, const char *dev_name, - struct nfs_mount_info *mount_info, - struct nfs_subversion *nfs_mod) +struct dentry *nfs_try_mount(int flags, const char *dev_name, + struct nfs_mount_info *mount_info, + struct nfs_subversion *nfs_mod) { int status; struct nfs_server *server; @@ -2403,15 +2403,9 @@ struct dentry *nfs_fs_mount(struct file_system_type *fs_type, goto out; } -#ifdef CONFIG_NFS_V4 - if (mount_info.parsed->version == 4) - mntroot = nfs4_try_mount(flags, dev_name, &mount_info); - else -#endif /* CONFIG_NFS_V4 */ - mntroot = nfs_try_mount(flags, dev_name, &mount_info, nfs_mod); + mntroot = nfs_mod->rpc_ops->try_mount(flags, dev_name, &mount_info, nfs_mod); put_nfs_version(nfs_mod); - out: nfs_free_parsed_mount_data(mount_info.parsed); nfs_free_fhandle(mount_info.mntfh); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 0e181c2320b7..bc7415baf44d 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1353,6 +1353,8 @@ struct nfs_renamedata { struct nfs_access_entry; struct nfs_client; struct rpc_timeout; +struct nfs_subversion; +struct nfs_mount_info; struct nfs_client_initdata; struct nfs_pageio_descriptor; @@ -1370,6 +1372,8 @@ struct nfs_rpc_ops { struct nfs_fsinfo *); struct vfsmount *(*submount) (struct nfs_server *, struct dentry *, struct nfs_fh *, struct nfs_fattr *); + struct dentry *(*try_mount) (int, const char *, struct nfs_mount_info *, + struct nfs_subversion *); int (*getattr) (struct nfs_server *, struct nfs_fh *, struct nfs_fattr *); int (*setattr) (struct dentry *, struct nfs_fattr *, -- cgit v1.2.3 From 1179acc6a3e260bc4edc74fa94f6c7908290eaec Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Mon, 30 Jul 2012 16:05:19 -0400 Subject: NFS: Only initialize the ACL client in the v3 case v2 and v4 don't use it, so I create two new nfs_rpc_ops functions to initialize the ACL client only when we are using v3. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/Makefile | 2 +- fs/nfs/client.c | 61 ++++------------------------------------------ fs/nfs/internal.h | 15 ++++++++---- fs/nfs/nfs3client.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs3proc.c | 2 ++ fs/nfs/nfs4client.c | 10 +++++--- fs/nfs/nfs4proc.c | 2 ++ fs/nfs/nfs4super.c | 2 +- fs/nfs/proc.c | 2 ++ fs/nfs/super.c | 4 +-- include/linux/nfs_xdr.h | 3 +++ 11 files changed, 99 insertions(+), 69 deletions(-) create mode 100644 fs/nfs/nfs3client.c (limited to 'include') diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 66dd3075e5db..7ca0125da65e 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -10,7 +10,7 @@ nfs-y := client.o dir.o file.o getroot.o inode.o super.o \ dns_resolve.o cache_lib.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o nfs-$(CONFIG_NFS_V2) += nfs2super.o proc.o nfs2xdr.o -nfs-$(CONFIG_NFS_V3) += nfs3super.o nfs3proc.o nfs3xdr.o +nfs-$(CONFIG_NFS_V3) += nfs3super.o nfs3client.o nfs3proc.o nfs3xdr.o nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ nfs4super.o nfs4file.o delegation.o idmap.o \ diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 462de24482b4..1f2908287cba 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -83,22 +83,6 @@ struct rpc_stat nfs_rpcstat = { .program = &nfs_program }; - -#ifdef CONFIG_NFS_V3_ACL -static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; -static const struct rpc_version *nfsacl_version[] = { - [3] = &nfsacl_version3, -}; - -const struct rpc_program nfsacl_program = { - .name = "nfsacl", - .number = NFS_ACL_PROGRAM, - .nrvers = ARRAY_SIZE(nfsacl_version), - .version = nfsacl_version, - .stats = &nfsacl_rpcstat, -}; -#endif /* CONFIG_NFS_V3_ACL */ - static struct nfs_subversion *find_nfs_version(unsigned int version) { struct nfs_subversion *nfs; @@ -695,36 +679,6 @@ static int nfs_start_lockd(struct nfs_server *server) return 0; } -/* - * Initialise an NFSv3 ACL client connection - */ -#ifdef CONFIG_NFS_V3_ACL -static void nfs_init_server_aclclient(struct nfs_server *server) -{ - if (server->nfs_client->rpc_ops->version != 3) - goto out_noacl; - if (server->flags & NFS_MOUNT_NOACL) - goto out_noacl; - - server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); - if (IS_ERR(server->client_acl)) - goto out_noacl; - - /* No errors! Assume that Sun nfsacls are supported */ - server->caps |= NFS_CAP_ACLS; - return; - -out_noacl: - server->caps &= ~NFS_CAP_ACLS; -} -#else -static inline void nfs_init_server_aclclient(struct nfs_server *server) -{ - server->flags &= ~NFS_MOUNT_NOACL; - server->caps &= ~NFS_CAP_ACLS; -} -#endif - /* * Create a general RPC client */ @@ -874,8 +828,6 @@ static int nfs_init_server(struct nfs_server *server, server->mountd_protocol = data->mount_server.protocol; server->namelen = data->namlen; - /* Create a client RPC handle for the NFSv3 ACL management interface */ - nfs_init_server_aclclient(server); dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp); return 0; @@ -1108,8 +1060,7 @@ void nfs_free_server(struct nfs_server *server) * Create a version 2 or 3 volume record * - keyed on server and FSID */ -struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, - struct nfs_fh *mntfh, +struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info, struct nfs_subversion *nfs_mod) { struct nfs_server *server; @@ -1126,7 +1077,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, goto error; /* Get a client representation */ - error = nfs_init_server(server, data, nfs_mod); + error = nfs_init_server(server, mount_info->parsed, nfs_mod); if (error < 0) goto error; @@ -1135,13 +1086,13 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); /* Probe the root fh to retrieve its FSID */ - error = nfs_probe_fsinfo(server, mntfh, fattr); + error = nfs_probe_fsinfo(server, mount_info->mntfh, fattr); if (error < 0) goto error; if (server->nfs_client->rpc_ops->version == 3) { if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) server->namelen = NFS3_MAXNAMLEN; - if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) + if (!(mount_info->parsed->flags & NFS_MOUNT_NORDIRPLUS)) server->caps |= NFS_CAP_READDIRPLUS; } else { if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) @@ -1149,7 +1100,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data, } if (!(fattr->valid & NFS_ATTR_FATTR)) { - error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr); + error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, fattr); if (error < 0) { dprintk("nfs_create_server: getattr error = %d\n", -error); goto error; @@ -1210,8 +1161,6 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source, flavor); if (error < 0) goto out_free_server; - if (!IS_ERR(source->client_acl)) - nfs_init_server_aclclient(server); /* probe the filesystem info for this server filesystem */ error = nfs_probe_fsinfo(server, fh, fattr_fsinfo); diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 3364eccd17ef..2151bafd55b4 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -187,13 +187,11 @@ extern struct nfs_client *nfs4_find_client_ident(struct net *, int); extern struct nfs_client * nfs4_find_client_sessionid(struct net *, const struct sockaddr *, struct nfs4_sessionid *); -extern struct nfs_server *nfs_create_server( - const struct nfs_parsed_mount_data *, - struct nfs_fh *, +extern struct nfs_server *nfs_create_server(struct nfs_mount_info *, struct nfs_subversion *); extern struct nfs_server *nfs4_create_server( - const struct nfs_parsed_mount_data *, - struct nfs_fh *); + struct nfs_mount_info *, + struct nfs_subversion *); extern struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *, struct nfs_fh *); extern void nfs_free_server(struct nfs_server *server); @@ -225,6 +223,13 @@ static inline void nfs_fs_proc_exit(void) int nfs_sockaddr_match_ipaddr(const struct sockaddr *, const struct sockaddr *); #endif +/* nfs3client.c */ +#ifdef CONFIG_NFS_V3 +struct nfs_server *nfs3_create_server(struct nfs_mount_info *, struct nfs_subversion *); +struct nfs_server *nfs3_clone_server(struct nfs_server *, struct nfs_fh *, + struct nfs_fattr *, rpc_authflavor_t); +#endif + /* callback_xdr.c */ extern struct svc_version nfs4_callback_version1; extern struct svc_version nfs4_callback_version4; diff --git a/fs/nfs/nfs3client.c b/fs/nfs/nfs3client.c new file mode 100644 index 000000000000..b3fc65ef39ca --- /dev/null +++ b/fs/nfs/nfs3client.c @@ -0,0 +1,65 @@ +#include +#include +#include "internal.h" + +#ifdef CONFIG_NFS_V3_ACL +static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; +static const struct rpc_version *nfsacl_version[] = { + [3] = &nfsacl_version3, +}; + +const struct rpc_program nfsacl_program = { + .name = "nfsacl", + .number = NFS_ACL_PROGRAM, + .nrvers = ARRAY_SIZE(nfsacl_version), + .version = nfsacl_version, + .stats = &nfsacl_rpcstat, +}; + +/* + * Initialise an NFSv3 ACL client connection + */ +static void nfs_init_server_aclclient(struct nfs_server *server) +{ + if (server->flags & NFS_MOUNT_NOACL) + goto out_noacl; + + server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); + if (IS_ERR(server->client_acl)) + goto out_noacl; + + /* No errors! Assume that Sun nfsacls are supported */ + server->caps |= NFS_CAP_ACLS; + return; + +out_noacl: + server->caps &= ~NFS_CAP_ACLS; +} +#else +static inline void nfs_init_server_aclclient(struct nfs_server *server) +{ + server->flags &= ~NFS_MOUNT_NOACL; + server->caps &= ~NFS_CAP_ACLS; +} +#endif + +struct nfs_server *nfs3_create_server(struct nfs_mount_info *mount_info, + struct nfs_subversion *nfs_mod) +{ + struct nfs_server *server = nfs_create_server(mount_info, nfs_mod); + /* Create a client RPC handle for the NFS v3 ACL management interface */ + if (!IS_ERR(server)) + nfs_init_server_aclclient(server); + return server; +} + +struct nfs_server *nfs3_clone_server(struct nfs_server *source, + struct nfs_fh *fh, + struct nfs_fattr *fattr, + rpc_authflavor_t flavor) +{ + struct nfs_server *server = nfs_clone_server(source, fh, fattr, flavor); + if (!IS_ERR(server) && !IS_ERR(source->client_acl)) + nfs_init_server_aclclient(server); + return server; +} diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 4f4cb8e49716..0952c791df36 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -969,4 +969,6 @@ const struct nfs_rpc_ops nfs_v3_clientops = { .alloc_client = nfs_alloc_client, .init_client = nfs_init_client, .free_client = nfs_free_client, + .create_server = nfs3_create_server, + .clone_server = nfs3_clone_server, }; diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 769e798b3959..b2d409d2805a 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -574,8 +574,10 @@ error: * Create a version 4 volume record * - keyed on server and FSID */ -struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, - struct nfs_fh *mntfh) +/*struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, + struct nfs_fh *mntfh)*/ +struct nfs_server *nfs4_create_server(struct nfs_mount_info *mount_info, + struct nfs_subversion *nfs_mod) { struct nfs_server *server; int error; @@ -587,11 +589,11 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, return ERR_PTR(-ENOMEM); /* set up the general RPC client */ - error = nfs4_init_server(server, data); + error = nfs4_init_server(server, mount_info->parsed); if (error < 0) goto error; - error = nfs4_server_common_setup(server, mntfh); + error = nfs4_server_common_setup(server, mount_info->mntfh); if (error < 0) goto error; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index eb4ba1d99df9..36c6432aac7b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6916,6 +6916,8 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .alloc_client = nfs4_alloc_client, .init_client = nfs4_init_client, .free_client = nfs4_free_client, + .create_server = nfs4_create_server, + .clone_server = nfs_clone_server, }; static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = { diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c index 9384f666b6ab..a62836256665 100644 --- a/fs/nfs/nfs4super.c +++ b/fs/nfs/nfs4super.c @@ -105,7 +105,7 @@ nfs4_remote_mount(struct file_system_type *fs_type, int flags, mount_info->set_security = nfs_set_sb_security; /* Get a volume representation */ - server = nfs4_create_server(mount_info->parsed, mount_info->mntfh); + server = nfs4_create_server(mount_info, &nfs_v4); if (IS_ERR(server)) { mntroot = ERR_CAST(server); goto out; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index ebb3d9c5227b..50a88c3546ed 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -817,4 +817,6 @@ const struct nfs_rpc_ops nfs_v2_clientops = { .alloc_client = nfs_alloc_client, .init_client = nfs_init_client, .free_client = nfs_free_client, + .create_server = nfs_create_server, + .clone_server = nfs_clone_server, }; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 5fca59d73e40..a5f9fb3bfdcc 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1664,7 +1664,7 @@ struct dentry *nfs_try_mount(int flags, const char *dev_name, } /* Get a volume representation */ - server = nfs_create_server(mount_info->parsed, mount_info->mntfh, nfs_mod); + server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod); if (IS_ERR(server)) return ERR_CAST(server); @@ -2458,7 +2458,7 @@ nfs_xdev_mount(struct file_system_type *fs_type, int flags, mount_info.mntfh = mount_info.cloned->fh; /* create a new volume representation */ - server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr, data->authflavor); + server = nfs_mod->rpc_ops->clone_server(NFS_SB(data->sb), data->fh, data->fattr, data->authflavor); if (IS_ERR(server)) { error = PTR_ERR(server); goto out_err; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index bc7415baf44d..631182062994 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1439,6 +1439,9 @@ struct nfs_rpc_ops { (*init_client) (struct nfs_client *, const struct rpc_timeout *, const char *, rpc_authflavor_t); void (*free_client) (struct nfs_client *); + struct nfs_server *(*create_server)(struct nfs_mount_info *, struct nfs_subversion *); + struct nfs_server *(*clone_server)(struct nfs_server *, struct nfs_fh *, + struct nfs_fattr *, rpc_authflavor_t); }; /* -- cgit v1.2.3 From 89d77c8fa8e6d1cb7e2cce95b428be30ddcc6f23 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Mon, 30 Jul 2012 16:05:25 -0400 Subject: NFS: Convert v4 into a module This patch exports symbols needed by the v4 module. In addition, I also switch over to using IS_ENABLED() to check if CONFIG_NFS_V4 or CONFIG_NFS_V4_MODULE are set. The module (nfs4.ko) will be created in the same directory as nfs.ko and will be automatically loaded the first time you try to mount over NFS v4. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/Kconfig | 2 +- fs/nfs/Makefile | 19 ++++++-------- fs/nfs/callback.h | 2 +- fs/nfs/client.c | 34 +++++++++++-------------- fs/nfs/delegation.h | 2 +- fs/nfs/dir.c | 6 ++++- fs/nfs/direct.c | 2 +- fs/nfs/dns_resolve.c | 4 +++ fs/nfs/file.c | 13 ++++++++++ fs/nfs/inode.c | 64 +++++++++++++++++++++++------------------------ fs/nfs/internal.h | 8 +++--- fs/nfs/namespace.c | 2 ++ fs/nfs/netns.h | 2 +- fs/nfs/nfs.h | 17 ------------- fs/nfs/nfs4_fs.h | 5 ++-- fs/nfs/nfs4super.c | 9 +++++-- fs/nfs/pagelist.c | 4 +++ fs/nfs/pnfs.c | 2 ++ fs/nfs/read.c | 4 +++ fs/nfs/super.c | 41 +++++++++++++++++++++++------- fs/nfs/write.c | 13 +++++++--- include/linux/nfs_fs.h | 6 ++--- include/linux/nfs_fs_sb.h | 6 ++--- include/linux/nfs_idmap.h | 2 +- include/linux/nfs_xdr.h | 2 +- 25 files changed, 155 insertions(+), 116 deletions(-) (limited to 'include') diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index f81a729c00e9..195c1ea6151a 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -72,7 +72,7 @@ config NFS_V3_ACL If unsure, say N. config NFS_V4 - bool "NFS client support for NFS version 4" + tristate "NFS client support for NFS version 4" depends on NFS_FS select SUNRPC_GSS select KEYS diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 01846edc5c94..8bf3a3f6925a 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -9,17 +9,7 @@ nfs-y := client.o dir.o file.o getroot.o inode.o super.o \ write.o namespace.o mount_clnt.o \ dns_resolve.o cache_lib.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o -nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ - nfs4super.o nfs4file.o delegation.o idmap.o \ - callback.o callback_xdr.o callback_proc.o \ - nfs4namespace.o nfs4getroot.o nfs4client.o -nfs-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o - -ifeq ($(CONFIG_SYSCTL), y) -nfs-y += sysctl.o -nfs-$(CONFIG_NFS_V4) += nfs4sysctl.o -endif - +nfs-$(CONFIG_SYSCTL) += sysctl.o nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o obj-$(CONFIG_NFS_V2) += nfs2.o @@ -29,6 +19,13 @@ obj-$(CONFIG_NFS_V3) += nfs3.o nfs3-y := nfs3super.o nfs3client.o nfs3proc.o nfs3xdr.o nfs3-$(CONFIG_NFS_V3_ACL) += nfs3acl.o +obj-$(CONFIG_NFS_V4) += nfs4.o +nfs4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \ + delegation.o idmap.o callback.o callback_xdr.o callback_proc.o \ + nfs4namespace.o nfs4getroot.o nfs4client.o +nfs4-$(CONFIG_SYSCTL) += nfs4sysctl.o +nfs4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o + obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o nfs_layout_nfsv41_files-y := nfs4filelayout.o nfs4filelayoutdev.o diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index a5527c90a5aa..b44d7b128b71 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -192,7 +192,7 @@ extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_process_state *cps); extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy, struct cb_process_state *cps); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt); extern void nfs_callback_down(int minorversion); extern int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation, diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 8687b6b6edc1..9fc0d9dfc91b 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -142,24 +142,6 @@ void unregister_nfs_version(struct nfs_subversion *nfs) } EXPORT_SYMBOL_GPL(unregister_nfs_version); -/* - * Preload all configured NFS versions during module init. - * This function should be edited after each protocol is converted, - * and eventually removed. - */ -int __init nfs_register_versions(void) -{ - return init_nfs_v4(); -} - -/* - * Remove each pre-loaded NFS version - */ -void nfs_unregister_versions(void) -{ - exit_nfs_v4(); -} - /* * Allocate a shared client record * @@ -214,7 +196,7 @@ error_0: } EXPORT_SYMBOL_GPL(nfs_alloc_client); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) /* idr_remove_all is not needed as all id's are removed by nfs_put_client */ void nfs_cleanup_cb_ident_idr(struct net *net) { @@ -390,6 +372,7 @@ int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, } return 0; } +EXPORT_SYMBOL_GPL(nfs_sockaddr_match_ipaddr); #endif /* CONFIG_NFS_V4_1 */ /* @@ -456,6 +439,7 @@ int nfs_wait_client_init_complete(const struct nfs_client *clp) return wait_event_killable(nfs_client_active_wq, nfs_client_init_is_complete(clp)); } +EXPORT_SYMBOL_GPL(nfs_wait_client_init_complete); /* * Found an existing client. Make sure it's ready before returning. @@ -530,6 +514,7 @@ nfs_get_client(const struct nfs_client_initdata *cl_init, cl_init->hostname ?: "", PTR_ERR(new)); return new; } +EXPORT_SYMBOL_GPL(nfs_get_client); /* * Mark a server as ready or failed @@ -540,6 +525,7 @@ void nfs_mark_client_ready(struct nfs_client *clp, int state) clp->cl_cons_state = state; wake_up_all(&nfs_client_active_wq); } +EXPORT_SYMBOL_GPL(nfs_mark_client_ready); /* * Initialise the timeout values for a connection @@ -581,6 +567,7 @@ void nfs_init_timeout_values(struct rpc_timeout *to, int proto, BUG(); } } +EXPORT_SYMBOL_GPL(nfs_init_timeout_values); /* * Create an RPC client handle @@ -620,6 +607,7 @@ int nfs_create_rpc_client(struct nfs_client *clp, clp->cl_rpcclient = clnt; return 0; } +EXPORT_SYMBOL_GPL(nfs_create_rpc_client); /* * Version 2 or 3 client destruction @@ -706,6 +694,7 @@ int nfs_init_server_rpcclient(struct nfs_server *server, return 0; } +EXPORT_SYMBOL_GPL(nfs_init_server_rpcclient); /** * nfs_init_client - Initialise an NFS2 or NFS3 client @@ -932,6 +921,7 @@ out_error: dprintk("nfs_probe_fsinfo: error = %d\n", -error); return error; } +EXPORT_SYMBOL_GPL(nfs_probe_fsinfo); /* * Copy useful information when duplicating a server record @@ -948,6 +938,7 @@ void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *sour target->caps = source->caps; target->options = source->options; } +EXPORT_SYMBOL_GPL(nfs_server_copy_userdata); void nfs_server_insert_lists(struct nfs_server *server) { @@ -961,6 +952,7 @@ void nfs_server_insert_lists(struct nfs_server *server) spin_unlock(&nn->nfs_client_lock); } +EXPORT_SYMBOL_GPL(nfs_server_insert_lists); static void nfs_server_remove_lists(struct nfs_server *server) { @@ -1020,6 +1012,7 @@ struct nfs_server *nfs_alloc_server(void) return server; } +EXPORT_SYMBOL_GPL(nfs_alloc_server); /* * Free up a server record @@ -1048,6 +1041,7 @@ void nfs_free_server(struct nfs_server *server) nfs_release_automount_timer(); dprintk("<-- nfs_free_server()\n"); } +EXPORT_SYMBOL_GPL(nfs_free_server); /* * Create a version 2 or 3 volume record @@ -1193,7 +1187,7 @@ void nfs_clients_init(struct net *net) INIT_LIST_HEAD(&nn->nfs_client_list); INIT_LIST_HEAD(&nn->nfs_volume_list); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) idr_init(&nn->cb_ident_idr); #endif spin_lock_init(&nn->nfs_client_lock); diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index 1f3ccd934635..bbc6a4dba0d8 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -8,7 +8,7 @@ #ifndef FS_NFS_DELEGATION_H #define FS_NFS_DELEGATION_H -#if defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V4) /* * NFSv4 delegation */ diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 55438c970cbf..627f108ede23 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -936,6 +936,7 @@ void nfs_force_lookup_revalidate(struct inode *dir) { NFS_I(dir)->cache_change_attribute++; } +EXPORT_SYMBOL_GPL(nfs_force_lookup_revalidate); /* * A check for whether or not the parent directory has changed. @@ -1267,7 +1268,7 @@ out: } EXPORT_SYMBOL_GPL(nfs_lookup); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) static int nfs4_lookup_revalidate(struct dentry *, unsigned int); const struct dentry_operations nfs4_dentry_operations = { @@ -1277,6 +1278,7 @@ const struct dentry_operations nfs4_dentry_operations = { .d_automount = nfs_d_automount, .d_release = nfs_d_release, }; +EXPORT_SYMBOL_GPL(nfs4_dentry_operations); static fmode_t flags_to_mode(int flags) { @@ -1419,6 +1421,7 @@ no_open: return finish_no_open(file, res); } +EXPORT_SYMBOL_GPL(nfs_atomic_open); static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags) { @@ -2142,6 +2145,7 @@ int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags) { return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags)); } +EXPORT_SYMBOL_GPL(nfs_may_open); int nfs_permission(struct inode *inode, int mask) { diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 899238156b11..b7b4f80968b5 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -460,7 +460,7 @@ static void nfs_inode_dio_write_done(struct inode *inode) inode_dio_done(inode); } -#if IS_ENABLED(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4) static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) { struct nfs_pageio_descriptor desc; diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c index b3924b8a6000..31c26c4dcc23 100644 --- a/fs/nfs/dns_resolve.c +++ b/fs/nfs/dns_resolve.c @@ -8,6 +8,7 @@ #ifdef CONFIG_NFS_USE_KERNEL_DNS +#include #include #include #include "dns_resolve.h" @@ -27,9 +28,11 @@ ssize_t nfs_dns_resolve_name(struct net *net, char *name, size_t namelen, kfree(ip_addr); return ret; } +EXPORT_SYMBOL_GPL(nfs_dns_resolve_name); #else +#include #include #include #include @@ -345,6 +348,7 @@ ssize_t nfs_dns_resolve_name(struct net *net, char *name, ret = -ESRCH; return ret; } +EXPORT_SYMBOL_GPL(nfs_dns_resolve_name); int nfs_dns_resolver_cache_init(struct net *net) { diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 5b3e70389553..1557978ca7b3 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -53,6 +53,7 @@ int nfs_check_flags(int flags) return 0; } +EXPORT_SYMBOL_GPL(nfs_check_flags); /* * Open file @@ -85,6 +86,7 @@ nfs_file_release(struct inode *inode, struct file *filp) nfs_inc_stats(inode, NFSIOS_VFSRELEASE); return nfs_release(inode, filp); } +EXPORT_SYMBOL_GPL(nfs_file_release); /** * nfs_revalidate_size - Revalidate the file size @@ -138,6 +140,7 @@ loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin) return generic_file_llseek(filp, offset, origin); } +EXPORT_SYMBOL_GPL(nfs_file_llseek); /* * Flush all dirty pages, and check for write errors. @@ -166,6 +169,7 @@ nfs_file_flush(struct file *file, fl_owner_t id) /* Flush writes to the server and return any errors */ return vfs_fsync(file, 0); } +EXPORT_SYMBOL_GPL(nfs_file_flush); ssize_t nfs_file_read(struct kiocb *iocb, const struct iovec *iov, @@ -190,6 +194,7 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov, } return result; } +EXPORT_SYMBOL_GPL(nfs_file_read); ssize_t nfs_file_splice_read(struct file *filp, loff_t *ppos, @@ -212,6 +217,7 @@ nfs_file_splice_read(struct file *filp, loff_t *ppos, } return res; } +EXPORT_SYMBOL_GPL(nfs_file_splice_read); int nfs_file_mmap(struct file * file, struct vm_area_struct * vma) @@ -233,6 +239,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma) } return status; } +EXPORT_SYMBOL_GPL(nfs_file_mmap); /* * Flush any dirty pages for this process, and check for write errors. @@ -271,6 +278,7 @@ nfs_file_fsync_commit(struct file *file, loff_t start, loff_t end, int datasync) ret = status; return ret; } +EXPORT_SYMBOL_GPL(nfs_file_fsync_commit); static int nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync) @@ -615,6 +623,7 @@ out_swapfile: printk(KERN_INFO "NFS: attempt to write to active swap file!\n"); goto out; } +EXPORT_SYMBOL_GPL(nfs_file_write); ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe, struct file *filp, loff_t *ppos, @@ -646,6 +655,7 @@ ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe, nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written); return ret; } +EXPORT_SYMBOL_GPL(nfs_file_splice_write); static int do_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local) @@ -806,6 +816,7 @@ int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) out_err: return ret; } +EXPORT_SYMBOL_GPL(nfs_lock); /* * Lock a (portion of) a file @@ -835,6 +846,7 @@ int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) return do_unlk(filp, cmd, fl, is_local); return do_setlk(filp, cmd, fl, is_local); } +EXPORT_SYMBOL_GPL(nfs_flock); /* * There is no protocol support for leases, so we have no way to implement @@ -847,6 +859,7 @@ int nfs_setlease(struct file *file, long arg, struct file_lock **fl) file->f_path.dentry->d_name.name, arg); return -EINVAL; } +EXPORT_SYMBOL_GPL(nfs_setlease); const struct file_operations nfs_file_operations = { .llseek = nfs_file_llseek, diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 78dfc3e895ec..2ed6138f32ad 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -82,6 +82,7 @@ int nfs_wait_bit_killable(void *word) freezable_schedule(); return 0; } +EXPORT_SYMBOL_GPL(nfs_wait_bit_killable); /** * nfs_compat_user_ino64 - returns the user-visible inode number @@ -117,6 +118,7 @@ void nfs_clear_inode(struct inode *inode) nfs_access_zap_cache(inode); nfs_fscache_release_inode_cookie(inode); } +EXPORT_SYMBOL_GPL(nfs_clear_inode); void nfs_evict_inode(struct inode *inode) { @@ -393,6 +395,7 @@ out_no_inode: dprintk("nfs_fhget: iget failed with error %ld\n", PTR_ERR(inode)); goto out; } +EXPORT_SYMBOL_GPL(nfs_fhget); #define NFS_VALID_ATTRS (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE|ATTR_ATIME|ATTR_ATIME_SET|ATTR_MTIME|ATTR_MTIME_SET|ATTR_FILE|ATTR_OPEN) @@ -655,6 +658,7 @@ struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, fmode_t f ctx->mdsthreshold = NULL; return ctx; } +EXPORT_SYMBOL_GPL(alloc_nfs_open_context); struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) { @@ -662,6 +666,7 @@ struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) atomic_inc(&ctx->lock_context.count); return ctx; } +EXPORT_SYMBOL_GPL(get_nfs_open_context); static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync) { @@ -689,6 +694,7 @@ void put_nfs_open_context(struct nfs_open_context *ctx) { __put_nfs_open_context(ctx, 0); } +EXPORT_SYMBOL_GPL(put_nfs_open_context); /* * Ensure that mmap has a recent RPC credential for use when writing out @@ -704,6 +710,7 @@ void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx) list_add(&ctx->list, &nfsi->open_files); spin_unlock(&inode->i_lock); } +EXPORT_SYMBOL_GPL(nfs_file_set_open_context); /* * Given an inode, search for an open context with the desired characteristics @@ -1497,11 +1504,12 @@ struct inode *nfs_alloc_inode(struct super_block *sb) nfsi->acl_access = ERR_PTR(-EAGAIN); nfsi->acl_default = ERR_PTR(-EAGAIN); #endif -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) nfsi->nfs4_acl = NULL; #endif /* CONFIG_NFS_V4 */ return &nfsi->vfs_inode; } +EXPORT_SYMBOL_GPL(nfs_alloc_inode); static void nfs_i_callback(struct rcu_head *head) { @@ -1513,10 +1521,11 @@ void nfs_destroy_inode(struct inode *inode) { call_rcu(&inode->i_rcu, nfs_i_callback); } +EXPORT_SYMBOL_GPL(nfs_destroy_inode); static inline void nfs4_init_once(struct nfs_inode *nfsi) { -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) INIT_LIST_HEAD(&nfsi->open_states); nfsi->delegation = NULL; nfsi->delegation_state = 0; @@ -1562,6 +1571,7 @@ static void nfs_destroy_inodecache(void) } struct workqueue_struct *nfsiod_workqueue; +EXPORT_SYMBOL_GPL(nfsiod_workqueue); /* * start up the nfsiod workqueue @@ -1622,90 +1632,80 @@ static int __init init_nfs_fs(void) err = nfs_dns_resolver_init(); if (err < 0) - goto out11; + goto out10;; err = register_pernet_subsys(&nfs_net_ops); if (err < 0) - goto out10; + goto out9; err = nfs_fscache_register(); if (err < 0) - goto out9; + goto out8; err = nfsiod_start(); if (err) - goto out8; + goto out7; err = nfs_fs_proc_init(); if (err) - goto out7; + goto out6; err = nfs_init_nfspagecache(); if (err) - goto out6; + goto out5; err = nfs_init_inodecache(); if (err) - goto out5; + goto out4; err = nfs_init_readpagecache(); if (err) - goto out4; + goto out3; err = nfs_init_writepagecache(); if (err) - goto out3; + goto out2; err = nfs_init_directcache(); if (err) - goto out2; + goto out1; #ifdef CONFIG_PROC_FS rpc_proc_register(&init_net, &nfs_rpcstat); #endif - - err = nfs_register_versions(); - if (err) - goto out1; - if ((err = register_nfs_fs()) != 0) goto out0; return 0; out0: - nfs_unregister_versions(); -out1: #ifdef CONFIG_PROC_FS rpc_proc_unregister(&init_net, "nfs"); #endif nfs_destroy_directcache(); -out2: +out1: nfs_destroy_writepagecache(); -out3: +out2: nfs_destroy_readpagecache(); -out4: +out3: nfs_destroy_inodecache(); -out5: +out4: nfs_destroy_nfspagecache(); -out6: +out5: nfs_fs_proc_exit(); -out7: +out6: nfsiod_stop(); -out8: +out7: nfs_fscache_unregister(); -out9: +out8: unregister_pernet_subsys(&nfs_net_ops); -out10: +out9: nfs_dns_resolver_destroy(); -out11: +out10: return err; } static void __exit exit_nfs_fs(void) { -#ifdef CONFIG_NFS_V4 - exit_nfs_v4(); -#endif nfs_destroy_directcache(); nfs_destroy_writepagecache(); nfs_destroy_readpagecache(); diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 64f0dc41a9b7..8865538b26b6 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -262,7 +262,7 @@ extern int nfs3_decode_dirent(struct xdr_stream *, struct nfs_entry *, int); /* nfs4xdr.c */ -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) extern int nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *, int); #endif @@ -272,7 +272,7 @@ extern const u32 nfs41_maxwrite_overhead; #endif /* nfs4proc.c */ -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) extern struct rpc_procinfo nfs4_procedures[]; #endif @@ -328,7 +328,7 @@ extern int nfs_wait_bit_killable(void *word); extern const struct super_operations nfs_sops; extern struct file_system_type nfs_fs_type; extern struct file_system_type nfs_xdev_fs_type; -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) extern struct file_system_type nfs4_xdev_fs_type; extern struct file_system_type nfs4_referral_fs_type; #endif @@ -364,7 +364,7 @@ struct vfsmount *nfs_do_submount(struct dentry *, struct nfs_fh *, /* getroot.c */ extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *, const char *); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) extern struct dentry *nfs4_get_root(struct super_block *, struct nfs_fh *, const char *); diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index 2a3b170e88e0..655925373b91 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -113,6 +113,7 @@ Elong_unlock: Elong: return ERR_PTR(-ENAMETOOLONG); } +EXPORT_SYMBOL_GPL(nfs_path); /* * nfs_d_automount - Handle crossing a mountpoint on the server @@ -241,6 +242,7 @@ out: dprintk("<-- nfs_do_submount() = %p\n", mnt); return mnt; } +EXPORT_SYMBOL_GPL(nfs_do_submount); struct vfsmount *nfs_submount(struct nfs_server *server, struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr) diff --git a/fs/nfs/netns.h b/fs/nfs/netns.h index 8a6394edb8b0..0539de1b8d1f 100644 --- a/fs/nfs/netns.h +++ b/fs/nfs/netns.h @@ -20,7 +20,7 @@ struct nfs_net { wait_queue_head_t bl_wq; struct list_head nfs_client_list; struct list_head nfs_volume_list; -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) struct idr cb_ident_idr; /* Protected by nfs_client_lock */ #endif spinlock_t nfs_client_lock; diff --git a/fs/nfs/nfs.h b/fs/nfs/nfs.h index 3e1b84baa57f..43679df56cd0 100644 --- a/fs/nfs/nfs.h +++ b/fs/nfs/nfs.h @@ -21,23 +21,6 @@ struct nfs_subversion { struct list_head list; /* List of NFS versions */ }; -int nfs_register_versions(void); -void nfs_unregister_versions(void); - -#ifdef CONFIG_NFS_V4 -int init_nfs_v4(void); -void exit_nfs_v4(void); -#else /* CONFIG_NFS_V4 */ -static inline int __init init_nfs_v4(void) -{ - return 0; -} - -static inline void exit_nfs_v4(void) -{ -} -#endif /* CONFIG_NFS_V4 */ - struct nfs_subversion *get_nfs_version(unsigned int); void put_nfs_version(struct nfs_subversion *); void register_nfs_version(struct nfs_subversion *); diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index bafe5186c9cd..3b950dd81e81 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -9,7 +9,7 @@ #ifndef __LINUX_FS_NFS_NFS4_FS_H #define __LINUX_FS_NFS_NFS4_FS_H -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) struct idmap; @@ -365,11 +365,10 @@ extern const nfs4_stateid zero_stateid; struct nfs_mount_info; extern struct nfs_subversion nfs_v4; struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *, struct nfs_subversion *); -int init_nfs_v4(void); -void exit_nfs_v4(void); extern bool nfs4_disable_idmapping; extern unsigned short max_session_slots; extern unsigned short send_implementation_id; + /* nfs4sysctl.c */ #ifdef CONFIG_SYSCTL int nfs4_register_sysctl(void); diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c index 1c825f3bef51..12a31a9dbcdd 100644 --- a/fs/nfs/nfs4super.c +++ b/fs/nfs/nfs4super.c @@ -332,7 +332,7 @@ static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type, } -int __init init_nfs_v4(void) +static int __init init_nfs_v4(void) { int err; @@ -358,10 +358,15 @@ out: return err; } -void exit_nfs_v4(void) +static void __exit exit_nfs_v4(void) { unregister_nfs_version(&nfs_v4); unregister_filesystem(&nfs4_fs_type); nfs4_unregister_sysctl(); nfs_idmap_quit(); } + +MODULE_LICENSE("GPL"); + +module_init(init_nfs_v4); +module_exit(exit_nfs_v4); diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index aed913c833f4..1e7d8879dae6 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -54,6 +54,7 @@ void nfs_pgheader_init(struct nfs_pageio_descriptor *desc, if (hdr->completion_ops->init_hdr) hdr->completion_ops->init_hdr(hdr); } +EXPORT_SYMBOL_GPL(nfs_pgheader_init); void nfs_set_pgio_error(struct nfs_pgio_header *hdr, int error, loff_t pos) { @@ -268,6 +269,7 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc, desc->pg_lseg = NULL; desc->pg_dreq = NULL; } +EXPORT_SYMBOL_GPL(nfs_pageio_init); /** * nfs_can_coalesce_requests - test two requests for compatibility @@ -409,6 +411,7 @@ int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc, } while (ret); return ret; } +EXPORT_SYMBOL_GPL(nfs_pageio_add_request); /** * nfs_pageio_complete - Complete I/O on an nfs_pageio_descriptor @@ -424,6 +427,7 @@ void nfs_pageio_complete(struct nfs_pageio_descriptor *desc) break; } } +EXPORT_SYMBOL_GPL(nfs_pageio_complete); /** * nfs_pageio_cond_complete - Conditional I/O completion diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 7fbd25afe418..76875bfcf19c 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1407,6 +1407,7 @@ static void pnfs_writehdr_free(struct nfs_pgio_header *hdr) put_lseg(hdr->lseg); nfs_writehdr_free(hdr); } +EXPORT_SYMBOL_GPL(pnfs_writehdr_free); int pnfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc) @@ -1561,6 +1562,7 @@ static void pnfs_readhdr_free(struct nfs_pgio_header *hdr) put_lseg(hdr->lseg); nfs_readhdr_free(hdr); } +EXPORT_SYMBOL_GPL(pnfs_readhdr_free); int pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc) diff --git a/fs/nfs/read.c b/fs/nfs/read.c index b000e4c0cf83..6935e401ad76 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -48,6 +48,7 @@ struct nfs_read_header *nfs_readhdr_alloc(void) } return rhdr; } +EXPORT_SYMBOL_GPL(nfs_readhdr_alloc); static struct nfs_read_data *nfs_readdata_alloc(struct nfs_pgio_header *hdr, unsigned int pagecount) @@ -80,6 +81,7 @@ void nfs_readhdr_free(struct nfs_pgio_header *hdr) kmem_cache_free(nfs_rdata_cachep, rhdr); } +EXPORT_SYMBOL_GPL(nfs_readhdr_free); void nfs_readdata_release(struct nfs_read_data *rdata) { @@ -96,6 +98,7 @@ void nfs_readdata_release(struct nfs_read_data *rdata) if (atomic_dec_and_test(&hdr->refcnt)) hdr->completion_ops->completion(hdr); } +EXPORT_SYMBOL_GPL(nfs_readdata_release); static int nfs_return_empty_page(struct page *page) @@ -398,6 +401,7 @@ int nfs_generic_pagein(struct nfs_pageio_descriptor *desc, return nfs_pagein_multi(desc, hdr); return nfs_pagein_one(desc, hdr); } +EXPORT_SYMBOL_GPL(nfs_generic_pagein); static int nfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc) { diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 558a85c9594a..ac6a3c55dce4 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -315,7 +315,7 @@ const struct super_operations nfs_sops = { }; EXPORT_SYMBOL_GPL(nfs_sops); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *); static int nfs4_validate_mount_data(void *options, struct nfs_parsed_mount_data *args, const char *dev_name); @@ -366,6 +366,7 @@ void nfs_sb_active(struct super_block *sb) if (atomic_inc_return(&server->active) == 1) atomic_inc(&sb->s_active); } +EXPORT_SYMBOL_GPL(nfs_sb_active); void nfs_sb_deactive(struct super_block *sb) { @@ -374,6 +375,7 @@ void nfs_sb_deactive(struct super_block *sb) if (atomic_dec_and_test(&server->active)) deactivate_super(sb); } +EXPORT_SYMBOL_GPL(nfs_sb_deactive); /* * Deliver file system statistics to userspace @@ -439,6 +441,7 @@ int nfs_statfs(struct dentry *dentry, struct kstatfs *buf) dprintk("%s: statfs error = %d\n", __func__, -error); return error; } +EXPORT_SYMBOL_GPL(nfs_statfs); /* * Map the security flavour number to a name @@ -544,7 +547,7 @@ static void nfs_show_mountd_options(struct seq_file *m, struct nfs_server *nfss, nfs_show_mountd_netid(m, nfss, showdefaults); } -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) static void nfs_show_nfsv4_options(struct seq_file *m, struct nfs_server *nfss, int showdefaults) { @@ -675,8 +678,9 @@ int nfs_show_options(struct seq_file *m, struct dentry *root) return 0; } +EXPORT_SYMBOL_GPL(nfs_show_options); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) #ifdef CONFIG_NFS_V4_1 static void show_sessions(struct seq_file *m, struct nfs_server *server) { @@ -709,7 +713,7 @@ static void show_implementation_id(struct seq_file *m, struct nfs_server *nfss) } } #else -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) static void show_pnfs(struct seq_file *m, struct nfs_server *server) { } @@ -734,12 +738,14 @@ int nfs_show_devname(struct seq_file *m, struct dentry *root) free_page((unsigned long)page); return err; } +EXPORT_SYMBOL_GPL(nfs_show_devname); int nfs_show_path(struct seq_file *m, struct dentry *dentry) { seq_puts(m, "/"); return 0; } +EXPORT_SYMBOL_GPL(nfs_show_path); /* * Present statistical information for this VFS mountpoint @@ -774,7 +780,7 @@ int nfs_show_stats(struct seq_file *m, struct dentry *root) seq_printf(m, ",bsize=%u", nfss->bsize); seq_printf(m, ",namlen=%u", nfss->namelen); -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) if (nfss->nfs_client->rpc_ops->version == 4) { seq_printf(m, "\n\tnfsv4:\t"); seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]); @@ -832,6 +838,7 @@ int nfs_show_stats(struct seq_file *m, struct dentry *root) return 0; } +EXPORT_SYMBOL_GPL(nfs_show_stats); /* * Begin unmount by attempting to remove all automounted mountpoints we added @@ -851,6 +858,7 @@ void nfs_umount_begin(struct super_block *sb) if (!IS_ERR(rpc)) rpc_killall_tasks(rpc); } +EXPORT_SYMBOL_GPL(nfs_umount_begin); static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void) { @@ -1915,7 +1923,7 @@ out_invalid_fh: return -EINVAL; } -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) static int nfs_validate_mount_data(struct file_system_type *fs_type, void *options, struct nfs_parsed_mount_data *args, @@ -1953,7 +1961,7 @@ static int nfs_validate_text_mount_data(void *options, goto out_no_address; if (args->version == 4) { -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) port = NFS_PORT; max_namelen = NFS4_MAXNAMLEN; max_pathlen = NFS4_MAXPATHLEN; @@ -1976,7 +1984,7 @@ static int nfs_validate_text_mount_data(void *options, &args->nfs_server.export_path, max_pathlen); -#ifndef CONFIG_NFS_V4 +#if !IS_ENABLED(CONFIG_NFS_V4) out_v4_not_compiled: dfprintk(MOUNT, "NFS: NFSv4 is not compiled into kernel\n"); return -EPROTONOSUPPORT; @@ -2075,6 +2083,7 @@ out: kfree(data); return error; } +EXPORT_SYMBOL_GPL(nfs_remount); /* * Initialise the common bits of the superblock @@ -2123,6 +2132,7 @@ void nfs_fill_super(struct super_block *sb, struct nfs_mount_info *mount_info) nfs_initialise_sb(sb); } +EXPORT_SYMBOL_GPL(nfs_fill_super); /* * Finish setting up a cloned NFS2/3/4 superblock @@ -2292,6 +2302,7 @@ int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot, { return security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts); } +EXPORT_SYMBOL_GPL(nfs_set_sb_security); int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot, struct nfs_mount_info *mount_info) @@ -2302,6 +2313,7 @@ int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot, return -ESTALE; return 0; } +EXPORT_SYMBOL_GPL(nfs_clone_sb_security); struct dentry *nfs_fs_mount_common(struct nfs_server *server, int flags, const char *dev_name, @@ -2375,6 +2387,7 @@ error_splat_bdi: deactivate_locked_super(s); goto out; } +EXPORT_SYMBOL_GPL(nfs_fs_mount_common); struct dentry *nfs_fs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data) @@ -2415,6 +2428,7 @@ out: nfs_free_fhandle(mount_info.mntfh); return mntroot; } +EXPORT_SYMBOL_GPL(nfs_fs_mount); /* * Ensure that we unregister the bdi before kill_anon_super @@ -2426,6 +2440,7 @@ void nfs_put_super(struct super_block *s) bdi_unregister(&server->backing_dev_info); } +EXPORT_SYMBOL_GPL(nfs_put_super); /* * Destroy an NFS2/3 superblock @@ -2438,6 +2453,7 @@ void nfs_kill_super(struct super_block *s) nfs_fscache_release_super_cookie(s); nfs_free_server(server); } +EXPORT_SYMBOL_GPL(nfs_kill_super); /* * Clone an NFS2/3/4 server record on xdev traversal (FSID-change) @@ -2478,7 +2494,7 @@ out_err: goto out; } -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args) { @@ -2590,6 +2606,13 @@ bool nfs4_disable_idmapping = true; unsigned short max_session_slots = NFS4_DEF_SLOT_TABLE_SIZE; unsigned short send_implementation_id = 1; +EXPORT_SYMBOL_GPL(nfs_callback_set_tcpport); +EXPORT_SYMBOL_GPL(nfs_callback_tcpport); +EXPORT_SYMBOL_GPL(nfs_idmap_cache_timeout); +EXPORT_SYMBOL_GPL(nfs4_disable_idmapping); +EXPORT_SYMBOL_GPL(max_session_slots); +EXPORT_SYMBOL_GPL(send_implementation_id); + #define NFS_CALLBACK_MAXPORTNR (65535U) static int param_set_portnr(const char *val, const struct kernel_param *kp) diff --git a/fs/nfs/write.c b/fs/nfs/write.c index f268fe4f2785..e4a2ad2059bd 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -84,6 +84,7 @@ struct nfs_write_header *nfs_writehdr_alloc(void) } return p; } +EXPORT_SYMBOL_GPL(nfs_writehdr_alloc); static struct nfs_write_data *nfs_writedata_alloc(struct nfs_pgio_header *hdr, unsigned int pagecount) @@ -115,6 +116,7 @@ void nfs_writehdr_free(struct nfs_pgio_header *hdr) struct nfs_write_header *whdr = container_of(hdr, struct nfs_write_header, header); mempool_free(whdr, nfs_wdata_mempool); } +EXPORT_SYMBOL_GPL(nfs_writehdr_free); void nfs_writedata_release(struct nfs_write_data *wdata) { @@ -131,6 +133,7 @@ void nfs_writedata_release(struct nfs_write_data *wdata) if (atomic_dec_and_test(&hdr->refcnt)) hdr->completion_ops->completion(hdr); } +EXPORT_SYMBOL_GPL(nfs_writedata_release); static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error) { @@ -446,7 +449,7 @@ nfs_mark_request_dirty(struct nfs_page *req) __set_page_dirty_nobuffers(req->wb_page); } -#if IS_ENABLED(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4) /** * nfs_request_add_commit_list - add request to a commit list * @req: pointer to a struct nfs_page @@ -636,7 +639,7 @@ out: hdr->release(hdr); } -#if IS_ENABLED(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4) static unsigned long nfs_reqs_to_commit(struct nfs_commit_info *cinfo) { @@ -1173,6 +1176,7 @@ int nfs_generic_flush(struct nfs_pageio_descriptor *desc, return nfs_flush_multi(desc, hdr); return nfs_flush_one(desc, hdr); } +EXPORT_SYMBOL_GPL(nfs_generic_flush); static int nfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc) { @@ -1298,7 +1302,7 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) return; nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, resp->count); -#if IS_ENABLED(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4) if (resp->verf->committed < argp->stable && task->tk_status >= 0) { /* We tried a write call, but the server did not * commit data to stable storage even though we @@ -1358,7 +1362,7 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) } -#if IS_ENABLED(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4) static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait) { int ret; @@ -1674,6 +1678,7 @@ int nfs_write_inode(struct inode *inode, struct writeback_control *wbc) { return nfs_commit_unstable_pages(inode, wbc); } +EXPORT_SYMBOL_GPL(nfs_write_inode); /* * flush the inode to disk. diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 4b6043c20f77..2889877318bc 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -191,7 +191,7 @@ struct nfs_inode { struct hlist_head silly_list; wait_queue_head_t waitqueue; -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) struct nfs4_cached_acl *nfs4_acl; /* NFSv4 state */ struct list_head open_states; @@ -428,7 +428,7 @@ extern __be32 root_nfs_parse_addr(char *name); /*__init*/ * linux/fs/nfs/file.c */ extern const struct file_operations nfs_file_operations; -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) extern const struct file_operations nfs4_file_operations; #endif /* CONFIG_NFS_V4 */ extern const struct address_space_operations nfs_file_aops; @@ -538,7 +538,7 @@ extern void nfs_writeback_done(struct rpc_task *, struct nfs_write_data *); extern int nfs_wb_all(struct inode *inode); extern int nfs_wb_page(struct inode *inode, struct page* page); extern int nfs_wb_page_cancel(struct inode *inode, struct page* page); -#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4) extern int nfs_commit_inode(struct inode *, int); extern struct nfs_commit_data *nfs_commitdata_alloc(void); extern void nfs_commit_free(struct nfs_commit_data *data); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 6039297801f4..310c63c8ab2c 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -53,7 +53,7 @@ struct nfs_client { u32 cl_minorversion;/* NFSv4 minorversion */ struct rpc_cred *cl_machine_cred; -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) u64 cl_clientid; /* constant */ nfs4_verifier cl_confirm; /* Clientid verifier */ unsigned long cl_state; @@ -138,7 +138,7 @@ struct nfs_server { #endif u32 pnfs_blksize; /* layout_blksize attr */ -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) u32 attr_bitmask[3];/* V4 bitmask representing the set of attributes supported on this filesystem */ @@ -201,7 +201,7 @@ struct nfs_server { #define NFS4_MAX_SLOT_TABLE (256U) #define NFS4_NO_SLOT ((u32)-1) -#if defined(CONFIG_NFS_V4) +#if IS_ENABLED(CONFIG_NFS_V4) /* Sessions */ #define SLOT_TABLE_SZ DIV_ROUND_UP(NFS4_MAX_SLOT_TABLE, 8*sizeof(long)) diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index 7eed2012d288..ece91c57ad79 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -69,7 +69,7 @@ struct nfs_server; struct nfs_fattr; struct nfs4_string; -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) int nfs_idmap_init(void); void nfs_idmap_quit(void); #else diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 631182062994..00485e084394 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -824,7 +824,7 @@ struct nfs3_getaclres { struct posix_acl * acl_default; }; -#ifdef CONFIG_NFS_V4 +#if IS_ENABLED(CONFIG_NFS_V4) typedef u64 clientid4; -- cgit v1.2.3 From 3d687b49ff085b325488e7aeb2ee4df99dd7ca6e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 5 Jul 2012 06:04:04 -0300 Subject: [media] videodev2.h: add VIDIOC_ENUM_FREQ_BANDS Add a new ioctl to enumerate the supported frequency bands of a tuner. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 4cf766e6f120..63c950f6fcc2 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -2032,6 +2032,7 @@ struct v4l2_modulator { #define V4L2_TUNER_CAP_RDS 0x0080 #define V4L2_TUNER_CAP_RDS_BLOCK_IO 0x0100 #define V4L2_TUNER_CAP_RDS_CONTROLS 0x0200 +#define V4L2_TUNER_CAP_FREQ_BANDS 0x0400 /* Flags for the 'rxsubchans' field */ #define V4L2_TUNER_SUB_MONO 0x0001 @@ -2050,19 +2051,34 @@ struct v4l2_modulator { #define V4L2_TUNER_MODE_LANG1_LANG2 0x0004 struct v4l2_frequency { - __u32 tuner; - __u32 type; /* enum v4l2_tuner_type */ - __u32 frequency; - __u32 reserved[8]; + __u32 tuner; + __u32 type; /* enum v4l2_tuner_type */ + __u32 frequency; + __u32 reserved[8]; +}; + +#define V4L2_BAND_MODULATION_VSB (1 << 1) +#define V4L2_BAND_MODULATION_FM (1 << 2) +#define V4L2_BAND_MODULATION_AM (1 << 3) + +struct v4l2_frequency_band { + __u32 tuner; + __u32 type; /* enum v4l2_tuner_type */ + __u32 index; + __u32 capability; + __u32 rangelow; + __u32 rangehigh; + __u32 modulation; + __u32 reserved[9]; }; struct v4l2_hw_freq_seek { - __u32 tuner; - __u32 type; /* enum v4l2_tuner_type */ - __u32 seek_upward; - __u32 wrap_around; - __u32 spacing; - __u32 reserved[7]; + __u32 tuner; + __u32 type; /* enum v4l2_tuner_type */ + __u32 seek_upward; + __u32 wrap_around; + __u32 spacing; + __u32 reserved[7]; }; /* @@ -2630,6 +2646,10 @@ struct v4l2_create_buffers { #define VIDIOC_QUERY_DV_TIMINGS _IOR('V', 99, struct v4l2_dv_timings) #define VIDIOC_DV_TIMINGS_CAP _IOWR('V', 100, struct v4l2_dv_timings_cap) +/* Experimental, this ioctl may change over the next couple of kernel + versions. */ +#define VIDIOC_ENUM_FREQ_BANDS _IOWR('V', 101, struct v4l2_frequency_band) + /* Reminder: when adding new ioctls please add support for them to drivers/media/video/v4l2-compat-ioctl32.c as well! */ -- cgit v1.2.3 From 82b655bfc32b793344ef0ddd46df8af8b98b55c7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 5 Jul 2012 06:37:08 -0300 Subject: [media] v4l2: add core support for the new VIDIOC_ENUM_FREQ_BANDS ioctl This adds the usual core support code for this new ioctl. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-compat-ioctl32.c | 1 + drivers/media/video/v4l2-dev.c | 2 + drivers/media/video/v4l2-ioctl.c | 83 ++++++++++++++++++++++++++++++- include/media/v4l2-ioctl.h | 2 + 4 files changed, 86 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index ac365cfb3706..9ebd5c540d10 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -1025,6 +1025,7 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_ENUM_DV_TIMINGS: case VIDIOC_QUERY_DV_TIMINGS: case VIDIOC_DV_TIMINGS_CAP: + case VIDIOC_ENUM_FREQ_BANDS: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 1b2f1c52e6c3..625248585c85 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -730,6 +730,8 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event); SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs); SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf); + if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator) + set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls); bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls, BASE_VIDIOC_PRIVATE); } diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 3e4db2c1c3d6..0f54f8efe66b 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -825,6 +825,17 @@ static void v4l_print_sliced_vbi_cap(const void *arg, bool write_only) p->service_lines[1][i]); } +static void v4l_print_freq_band(const void *arg, bool write_only) +{ + const struct v4l2_frequency_band *p = arg; + + pr_cont("tuner=%u, type=%u, index=%u, capability=0x%x, " + "rangelow=%u, rangehigh=%u, modulation=0x%x\n", + p->tuner, p->type, p->index, + p->capability, p->rangelow, + p->rangehigh, p->modulation); +} + static void v4l_print_u32(const void *arg, bool write_only) { pr_cont("value=%u\n", *(const u32 *)arg); @@ -1245,10 +1256,14 @@ static int v4l_g_tuner(const struct v4l2_ioctl_ops *ops, { struct video_device *vfd = video_devdata(file); struct v4l2_tuner *p = arg; + int err; p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - return ops->vidioc_g_tuner(file, fh, p); + err = ops->vidioc_g_tuner(file, fh, p); + if (!err) + p->capability |= V4L2_TUNER_CAP_FREQ_BANDS; + return err; } static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops, @@ -1262,6 +1277,18 @@ static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops, return ops->vidioc_s_tuner(file, fh, p); } +static int v4l_g_modulator(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct v4l2_modulator *p = arg; + int err; + + err = ops->vidioc_g_modulator(file, fh, p); + if (!err) + p->capability |= V4L2_TUNER_CAP_FREQ_BANDS; + return err; +} + static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { @@ -1805,6 +1832,57 @@ static int v4l_g_sliced_vbi_cap(const struct v4l2_ioctl_ops *ops, return ops->vidioc_g_sliced_vbi_cap(file, fh, p); } +static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_frequency_band *p = arg; + enum v4l2_tuner_type type; + int err; + + type = (vfd->vfl_type == VFL_TYPE_RADIO) ? + V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + + if (type != p->type) + return -EINVAL; + if (ops->vidioc_enum_freq_bands) + return ops->vidioc_enum_freq_bands(file, fh, p); + if (ops->vidioc_g_tuner) { + struct v4l2_tuner t = { + .index = p->tuner, + .type = type, + }; + + err = ops->vidioc_g_tuner(file, fh, &t); + if (err) + return err; + p->capability = t.capability | V4L2_TUNER_CAP_FREQ_BANDS; + p->rangelow = t.rangelow; + p->rangehigh = t.rangehigh; + p->modulation = (type == V4L2_TUNER_RADIO) ? + V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB; + return 0; + } + if (ops->vidioc_g_modulator) { + struct v4l2_modulator m = { + .index = p->tuner, + }; + + if (type != V4L2_TUNER_RADIO) + return -EINVAL; + err = ops->vidioc_g_modulator(file, fh, &m); + if (err) + return err; + p->capability = m.capability | V4L2_TUNER_CAP_FREQ_BANDS; + p->rangelow = m.rangelow; + p->rangehigh = m.rangehigh; + p->modulation = (type == V4L2_TUNER_RADIO) ? + V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB; + return 0; + } + return -ENOTTY; +} + struct v4l2_ioctl_info { unsigned int ioctl; u32 flags; @@ -1886,7 +1964,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)), IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0), IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_MODULATOR, vidioc_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)), + IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)), IOCTL_INFO_STD(VIDIOC_S_MODULATOR, vidioc_s_modulator, v4l_print_modulator, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)), IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO), @@ -1933,6 +2011,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0), IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0), IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)), + IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0), }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 19e93523c2d8..e614c9c15e56 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -230,6 +230,8 @@ struct v4l2_ioctl_ops { struct v4l2_frequency *a); int (*vidioc_s_frequency) (struct file *file, void *fh, struct v4l2_frequency *a); + int (*vidioc_enum_freq_bands) (struct file *file, void *fh, + struct v4l2_frequency_band *band); /* Sliced VBI cap */ int (*vidioc_g_sliced_vbi_cap) (struct file *file, void *fh, -- cgit v1.2.3 From e0a9b1770bac048171961625875aaf15118a7ae9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 12 Jul 2012 16:55:45 -0300 Subject: [media] v4l2: Add rangelow and rangehigh fields to the v4l2_hw_freq_seek struct To allow apps to limit a hw-freq-seek to a specific band, for further info see the documentation this patch adds for these new fields. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../DocBook/media/v4l/vidioc-s-hw-freq-seek.xml | 50 ++++++++++++++++++---- include/linux/videodev2.h | 5 ++- 2 files changed, 46 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml b/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml index f4db44d0d95a..3dd1bec6d3c7 100644 --- a/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml +++ b/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml @@ -52,11 +52,23 @@ Start a hardware frequency seek from the current frequency. To do this applications initialize the tuner, type, seek_upward, -spacing and -wrap_around fields, and zero out the -reserved array of a &v4l2-hw-freq-seek; and -call the VIDIOC_S_HW_FREQ_SEEK ioctl with a pointer -to this structure. +wrap_around, spacing, +rangelow and rangehigh +fields, and zero out the reserved array of a +&v4l2-hw-freq-seek; and call the VIDIOC_S_HW_FREQ_SEEK +ioctl with a pointer to this structure. + + The rangelow and +rangehigh fields can be set to a non-zero value to +tell the driver to search a specific band. If the &v4l2-tuner; +capability field has the +V4L2_TUNER_CAP_HWSEEK_PROG_LIM flag set, these values +must fall within one of the bands returned by &VIDIOC-ENUM-FREQ-BANDS;. If +the V4L2_TUNER_CAP_HWSEEK_PROG_LIM flag is not set, +then these values must exactly match those of one of the bands returned by +&VIDIOC-ENUM-FREQ-BANDS;. If the current frequency of the tuner does not fall +within the selected band it will be clamped to fit in the band before the +seek is started. If an error is returned, then the original frequency will be restored. @@ -102,7 +114,27 @@ field and the &v4l2-tuner; index field. __u32 - reserved[7] + rangelow + If non-zero, the lowest tunable frequency of the band to +search in units of 62.5 kHz, or if the &v4l2-tuner; +capability field has the +V4L2_TUNER_CAP_LOW flag set, in units of 62.5 Hz. +If rangelow is zero a reasonable default value +is used. + + + __u32 + rangehigh + If non-zero, the highest tunable frequency of the band to +search in units of 62.5 kHz, or if the &v4l2-tuner; +capability field has the +V4L2_TUNER_CAP_LOW flag set, in units of 62.5 Hz. +If rangehigh is zero a reasonable default value +is used. + + + __u32 + reserved[5] Reserved for future extensions. Applications must set the array to zero. @@ -119,8 +151,10 @@ field and the &v4l2-tuner; index field. EINVAL The tuner index is out of -bounds, the wrap_around value is not supported or the value in the type field is -wrong. +bounds, the wrap_around value is not supported or +one of the values in the type, +rangelow or rangehigh +fields is wrong. diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 63c950f6fcc2..7a147c8299ab 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -2033,6 +2033,7 @@ struct v4l2_modulator { #define V4L2_TUNER_CAP_RDS_BLOCK_IO 0x0100 #define V4L2_TUNER_CAP_RDS_CONTROLS 0x0200 #define V4L2_TUNER_CAP_FREQ_BANDS 0x0400 +#define V4L2_TUNER_CAP_HWSEEK_PROG_LIM 0x0800 /* Flags for the 'rxsubchans' field */ #define V4L2_TUNER_SUB_MONO 0x0001 @@ -2078,7 +2079,9 @@ struct v4l2_hw_freq_seek { __u32 seek_upward; __u32 wrap_around; __u32 spacing; - __u32 reserved[7]; + __u32 rangelow; + __u32 rangehigh; + __u32 reserved[5]; }; /* -- cgit v1.2.3 From 1fe60e51a3744528f3939b1b1167ca909133d9ae Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 30 Jul 2012 16:23:22 -0700 Subject: libceph: move feature bits to separate header This is simply cleanup that will keep things more closely synced with the userland code. Signed-off-by: Sage Weil Reviewed-by: Alex Elder Reviewed-by: Yehuda Sadeh --- fs/ceph/mds_client.c | 1 + fs/ceph/super.c | 1 + include/linux/ceph/ceph_features.h | 24 ++++++++++++++++++++++++ include/linux/ceph/ceph_fs.h | 14 -------------- include/linux/ceph/libceph.h | 6 ------ net/ceph/ceph_common.c | 5 +++-- 6 files changed, 29 insertions(+), 22 deletions(-) create mode 100644 include/linux/ceph/ceph_features.h (limited to 'include') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 418f6a82c90d..39b76d66bc5d 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -10,6 +10,7 @@ #include "super.h" #include "mds_client.h" +#include #include #include #include diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 1e67dd7305a4..2c47ecfe4373 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -18,6 +18,7 @@ #include "super.h" #include "mds_client.h" +#include #include #include #include diff --git a/include/linux/ceph/ceph_features.h b/include/linux/ceph/ceph_features.h new file mode 100644 index 000000000000..342f93dbe162 --- /dev/null +++ b/include/linux/ceph/ceph_features.h @@ -0,0 +1,24 @@ +#ifndef __CEPH_FEATURES +#define __CEPH_FEATURES + +/* + * feature bits + */ +#define CEPH_FEATURE_UID (1<<0) +#define CEPH_FEATURE_NOSRCADDR (1<<1) +#define CEPH_FEATURE_MONCLOCKCHECK (1<<2) +#define CEPH_FEATURE_FLOCK (1<<3) +#define CEPH_FEATURE_SUBSCRIBE2 (1<<4) +#define CEPH_FEATURE_MONNAMES (1<<5) +#define CEPH_FEATURE_RECONNECT_SEQ (1<<6) +#define CEPH_FEATURE_DIRLAYOUTHASH (1<<7) + +/* + * Features supported. + */ +#define CEPH_FEATURES_SUPPORTED_DEFAULT \ + (CEPH_FEATURE_NOSRCADDR) + +#define CEPH_FEATURES_REQUIRED_DEFAULT \ + (CEPH_FEATURE_NOSRCADDR) +#endif diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h index e81ab30d4896..d021610efd65 100644 --- a/include/linux/ceph/ceph_fs.h +++ b/include/linux/ceph/ceph_fs.h @@ -35,20 +35,6 @@ /* arbitrary limit on max # of monitors (cluster of 3 is typical) */ #define CEPH_MAX_MON 31 - -/* - * feature bits - */ -#define CEPH_FEATURE_UID (1<<0) -#define CEPH_FEATURE_NOSRCADDR (1<<1) -#define CEPH_FEATURE_MONCLOCKCHECK (1<<2) -#define CEPH_FEATURE_FLOCK (1<<3) -#define CEPH_FEATURE_SUBSCRIBE2 (1<<4) -#define CEPH_FEATURE_MONNAMES (1<<5) -#define CEPH_FEATURE_RECONNECT_SEQ (1<<6) -#define CEPH_FEATURE_DIRLAYOUTHASH (1<<7) - - /* * ceph_file_layout - describe data layout for a file/inode */ diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h index 98ec36ae8a3b..ea072e1f9db9 100644 --- a/include/linux/ceph/libceph.h +++ b/include/linux/ceph/libceph.h @@ -22,12 +22,6 @@ #include "osd_client.h" #include "ceph_fs.h" -/* - * Supported features - */ -#define CEPH_FEATURE_SUPPORTED_DEFAULT CEPH_FEATURE_NOSRCADDR -#define CEPH_FEATURE_REQUIRED_DEFAULT CEPH_FEATURE_NOSRCADDR - /* * mount options */ diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index 3b45e01fa8d1..69e38db28e5f 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -460,9 +461,9 @@ struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private, client->auth_err = 0; client->extra_mon_dispatch = NULL; - client->supported_features = CEPH_FEATURE_SUPPORTED_DEFAULT | + client->supported_features = CEPH_FEATURES_SUPPORTED_DEFAULT | supported_features; - client->required_features = CEPH_FEATURE_REQUIRED_DEFAULT | + client->required_features = CEPH_FEATURES_REQUIRED_DEFAULT | required_features; /* msgr */ -- cgit v1.2.3 From 54b501992dd2a839e94e76aa392c392b55080ce8 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 30 Jul 2012 14:39:18 -0700 Subject: coredump: warn about unsafe suid_dumpable / core_pattern combo When suid_dumpable=2, detect unsafe core_pattern settings and warn when they are seen. Signed-off-by: Kees Cook Suggested-by: Andrew Morton Cc: Alexander Viro Cc: Alan Cox Cc: "Eric W. Biederman" Cc: Doug Ledford Cc: Serge Hallyn Cc: James Morris Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 10 +++++----- include/linux/sched.h | 5 +++++ kernel/sysctl.c | 37 +++++++++++++++++++++++++++++++++++-- 3 files changed, 45 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/fs/exec.c b/fs/exec.c index 95aae3f9c036..5af8390e0fae 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -2002,17 +2002,17 @@ static void coredump_finish(struct mm_struct *mm) void set_dumpable(struct mm_struct *mm, int value) { switch (value) { - case 0: + case SUID_DUMPABLE_DISABLED: clear_bit(MMF_DUMPABLE, &mm->flags); smp_wmb(); clear_bit(MMF_DUMP_SECURELY, &mm->flags); break; - case 1: + case SUID_DUMPABLE_ENABLED: set_bit(MMF_DUMPABLE, &mm->flags); smp_wmb(); clear_bit(MMF_DUMP_SECURELY, &mm->flags); break; - case 2: + case SUID_DUMPABLE_SAFE: set_bit(MMF_DUMP_SECURELY, &mm->flags); smp_wmb(); set_bit(MMF_DUMPABLE, &mm->flags); @@ -2025,7 +2025,7 @@ static int __get_dumpable(unsigned long mm_flags) int ret; ret = mm_flags & MMF_DUMPABLE_MASK; - return (ret >= 2) ? 2 : ret; + return (ret > SUID_DUMPABLE_ENABLED) ? SUID_DUMPABLE_SAFE : ret; } int get_dumpable(struct mm_struct *mm) @@ -2142,7 +2142,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) * so we dump it as root in mode 2, and only into a controlled * environment (pipe handler or fully qualified path). */ - if (__get_dumpable(cprm.mm_flags) == 2) { + if (__get_dumpable(cprm.mm_flags) == SUID_DUMPABLE_SAFE) { /* Setuid core dump mode */ flag = O_EXCL; /* Stop rewrite attacks */ cred->fsuid = GLOBAL_ROOT_UID; /* Dump root private */ diff --git a/include/linux/sched.h b/include/linux/sched.h index a721cef7e2d4..1e26a5e45aa6 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -406,6 +406,11 @@ static inline void arch_pick_mmap_layout(struct mm_struct *mm) {} extern void set_dumpable(struct mm_struct *mm, int value); extern int get_dumpable(struct mm_struct *mm); +/* get/set_dumpable() values */ +#define SUID_DUMPABLE_DISABLED 0 +#define SUID_DUMPABLE_ENABLED 1 +#define SUID_DUMPABLE_SAFE 2 + /* mm flags */ /* dumpable bits */ #define MMF_DUMPABLE 0 /* core dump is permitted */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 4ab11879aeb4..b46f496405e4 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -174,6 +174,11 @@ static int proc_dointvec_minmax_sysadmin(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); #endif +static int proc_dointvec_minmax_coredump(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); +static int proc_dostring_coredump(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); + #ifdef CONFIG_MAGIC_SYSRQ /* Note: sysrq code uses it's own private copy */ static int __sysrq_enabled = SYSRQ_DEFAULT_ENABLE; @@ -410,7 +415,7 @@ static struct ctl_table kern_table[] = { .data = core_pattern, .maxlen = CORENAME_MAX_SIZE, .mode = 0644, - .proc_handler = proc_dostring, + .proc_handler = proc_dostring_coredump, }, { .procname = "core_pipe_limit", @@ -1498,7 +1503,7 @@ static struct ctl_table fs_table[] = { .data = &suid_dumpable, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_dointvec_minmax_coredump, .extra1 = &zero, .extra2 = &two, }, @@ -2009,6 +2014,34 @@ int proc_dointvec_minmax(struct ctl_table *table, int write, do_proc_dointvec_minmax_conv, ¶m); } +static void validate_coredump_safety(void) +{ + if (suid_dumpable == SUID_DUMPABLE_SAFE && + core_pattern[0] != '/' && core_pattern[0] != '|') { + printk(KERN_WARNING "Unsafe core_pattern used with "\ + "suid_dumpable=2. Pipe handler or fully qualified "\ + "core dump path required.\n"); + } +} + +static int proc_dointvec_minmax_coredump(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int error = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + if (!error) + validate_coredump_safety(); + return error; +} + +static int proc_dostring_coredump(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int error = proc_dostring(table, write, buffer, lenp, ppos); + if (!error) + validate_coredump_safety(); + return error; +} + static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos, -- cgit v1.2.3 From 93abe8e4b13ae9a0428ce940a8a03ac72a7626f1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 30 Jul 2012 14:39:27 -0700 Subject: clk: add non CONFIG_HAVE_CLK routines Many drivers are shared between architectures that may or may not have HAVE_CLK selected for them. To remove compilation errors for them we enclose clk_*() calls in these drivers within #ifdef CONFIG_HAVE_CLK, #endif. This patch removes the need of these CONFIG_HAVE_CLK statements, by introducing dummy routines when HAVE_CLK is not selected by platforms. So, definition of these routines will always be available. These calls will return error for platforms that don't select HAVE_CLK. Signed-off-by: Viresh Kumar Cc: Wolfram Sang Cc: Greg Kroah-Hartman Cc: Jeff Garzik Cc: Andrew Lunn Cc: Bhupesh Sharma Cc: Giuseppe Cavallaro Cc: Russell King Cc: Mike Turquette Cc: Sergei Shtylyov Cc: viresh kumar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/clk.h | 168 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 109 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/include/linux/clk.h b/include/linux/clk.h index 2fd6a4234531..b3ac22d0fc1f 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -84,6 +84,43 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb); #endif +/** + * clk_prepare - prepare a clock source + * @clk: clock source + * + * This prepares the clock source for use. + * + * Must not be called from within atomic context. + */ +#ifdef CONFIG_HAVE_CLK_PREPARE +int clk_prepare(struct clk *clk); +#else +static inline int clk_prepare(struct clk *clk) +{ + might_sleep(); + return 0; +} +#endif + +/** + * clk_unprepare - undo preparation of a clock source + * @clk: clock source + * + * This undoes a previously prepared clock. The caller must balance + * the number of prepare and unprepare calls. + * + * Must not be called from within atomic context. + */ +#ifdef CONFIG_HAVE_CLK_PREPARE +void clk_unprepare(struct clk *clk); +#else +static inline void clk_unprepare(struct clk *clk) +{ + might_sleep(); +} +#endif + +#ifdef CONFIG_HAVE_CLK /** * clk_get - lookup and obtain a reference to a clock producer. * @dev: device for clock "consumer" @@ -121,24 +158,6 @@ struct clk *clk_get(struct device *dev, const char *id); */ struct clk *devm_clk_get(struct device *dev, const char *id); -/** - * clk_prepare - prepare a clock source - * @clk: clock source - * - * This prepares the clock source for use. - * - * Must not be called from within atomic context. - */ -#ifdef CONFIG_HAVE_CLK_PREPARE -int clk_prepare(struct clk *clk); -#else -static inline int clk_prepare(struct clk *clk) -{ - might_sleep(); - return 0; -} -#endif - /** * clk_enable - inform the system when the clock source should be running. * @clk: clock source @@ -167,47 +186,6 @@ int clk_enable(struct clk *clk); */ void clk_disable(struct clk *clk); - -/** - * clk_unprepare - undo preparation of a clock source - * @clk: clock source - * - * This undoes a previously prepared clock. The caller must balance - * the number of prepare and unprepare calls. - * - * Must not be called from within atomic context. - */ -#ifdef CONFIG_HAVE_CLK_PREPARE -void clk_unprepare(struct clk *clk); -#else -static inline void clk_unprepare(struct clk *clk) -{ - might_sleep(); -} -#endif - -/* clk_prepare_enable helps cases using clk_enable in non-atomic context. */ -static inline int clk_prepare_enable(struct clk *clk) -{ - int ret; - - ret = clk_prepare(clk); - if (ret) - return ret; - ret = clk_enable(clk); - if (ret) - clk_unprepare(clk); - - return ret; -} - -/* clk_disable_unprepare helps cases using clk_disable in non-atomic context. */ -static inline void clk_disable_unprepare(struct clk *clk) -{ - clk_disable(clk); - clk_unprepare(clk); -} - /** * clk_get_rate - obtain the current clock rate (in Hz) for a clock source. * This is only valid once the clock source has been enabled. @@ -298,6 +276,78 @@ struct clk *clk_get_parent(struct clk *clk); */ struct clk *clk_get_sys(const char *dev_id, const char *con_id); +#else /* !CONFIG_HAVE_CLK */ + +static inline struct clk *clk_get(struct device *dev, const char *id) +{ + return NULL; +} + +static inline struct clk *devm_clk_get(struct device *dev, const char *id) +{ + return NULL; +} + +static inline void clk_put(struct clk *clk) {} + +static inline void devm_clk_put(struct device *dev, struct clk *clk) {} + +static inline int clk_enable(struct clk *clk) +{ + return 0; +} + +static inline void clk_disable(struct clk *clk) {} + +static inline unsigned long clk_get_rate(struct clk *clk) +{ + return 0; +} + +static inline int clk_set_rate(struct clk *clk, unsigned long rate) +{ + return 0; +} + +static inline long clk_round_rate(struct clk *clk, unsigned long rate) +{ + return 0; +} + +static inline int clk_set_parent(struct clk *clk, struct clk *parent) +{ + return 0; +} + +static inline struct clk *clk_get_parent(struct clk *clk) +{ + return NULL; +} + +#endif + +/* clk_prepare_enable helps cases using clk_enable in non-atomic context. */ +static inline int clk_prepare_enable(struct clk *clk) +{ + int ret; + + ret = clk_prepare(clk); + if (ret) + return ret; + ret = clk_enable(clk); + if (ret) + clk_unprepare(clk); + + return ret; +} + +/* clk_disable_unprepare helps cases using clk_disable in non-atomic context. */ +static inline void clk_disable_unprepare(struct clk *clk) +{ + clk_disable(clk); + clk_unprepare(clk); +} + /** * clk_add_alias - add a new clock alias * @alias: name for clock alias -- cgit v1.2.3 From 714904e1c3f48b17ceff06b827485f8b1b5d8a91 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 30 Jul 2012 14:39:32 -0700 Subject: usb/marvell: remove conditional compilation of clk code With addition of dummy clk_*() calls for non CONFIG_HAVE_CLK cases in clk.h, there is no need to have clk code enclosed in #ifdef CONFIG_HAVE_CLK, #endif macros. Marvell usb also has these dummy macros defined locally. Remove them as they aren't required anymore. Signed-off-by: Viresh Kumar Cc: Greg Kroah-Hartman Cc: Russell King Cc: Mike Turquette Cc: Sergei Shtylyov Cc: viresh kumar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/platform_data/mv_usb.h | 9 --------- 1 file changed, 9 deletions(-) (limited to 'include') diff --git a/include/linux/platform_data/mv_usb.h b/include/linux/platform_data/mv_usb.h index d94804aca764..944b01dd103e 100644 --- a/include/linux/platform_data/mv_usb.h +++ b/include/linux/platform_data/mv_usb.h @@ -52,13 +52,4 @@ struct mv_usb_platform_data { int (*set_vbus)(unsigned int vbus); int (*private_init)(void __iomem *opregs, void __iomem *phyregs); }; - -#ifndef CONFIG_HAVE_CLK -/* Dummy stub for clk framework */ -#define clk_get(dev, id) NULL -#define clk_put(clock) do {} while (0) -#define clk_enable(clock) do {} while (0) -#define clk_disable(clock) do {} while (0) -#endif - #endif -- cgit v1.2.3 From 45226e944ce071d0231949f2fea90969437cd2dc Mon Sep 17 00:00:00 2001 From: Sameer Nanda Date: Mon, 30 Jul 2012 14:40:00 -0700 Subject: NMI watchdog: fix for lockup detector breakage on resume On the suspend/resume path the boot CPU does not go though an offline->online transition. This breaks the NMI detector post-resume since it depends on PMU state that is lost when the system gets suspended. Fix this by forcing a CPU offline->online transition for the lockup detector on the boot CPU during resume. To provide more context, we enable NMI watchdog on Chrome OS. We have seen several reports of systems freezing up completely which indicated that the NMI watchdog was not firing for some reason. Debugging further, we found a simple way of repro'ing system freezes -- issuing the command 'tasket 1 sh -c "echo nmilockup > /proc/breakme"' after the system has been suspended/resumed one or more times. With this patch in place, the system freeze result in panics, as expected. These panics provide a nice stack trace for us to debug the actual issue causing the freeze. [akpm@linux-foundation.org: fiddle with code comment] [akpm@linux-foundation.org: make lockup_detector_bootcpu_resume() conditional on CONFIG_SUSPEND] [akpm@linux-foundation.org: fix section errors] Signed-off-by: Sameer Nanda Cc: Ingo Molnar Cc: Peter Zijlstra Cc: "Rafael J. Wysocki" Cc: Don Zickus Cc: Mandeep Singh Baines Cc: Srivatsa S. Bhat Cc: Anshuman Khandual Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 8 ++++++++ kernel/power/suspend.c | 3 +++ kernel/watchdog.c | 21 +++++++++++++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 1e26a5e45aa6..68dcffaa62a0 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -334,6 +334,14 @@ static inline void lockup_detector_init(void) } #endif +#if defined(CONFIG_LOCKUP_DETECTOR) && defined(CONFIG_SUSPEND) +void lockup_detector_bootcpu_resume(void); +#else +static inline void lockup_detector_bootcpu_resume(void) +{ +} +#endif + #ifdef CONFIG_DETECT_HUNG_TASK extern unsigned int sysctl_hung_task_panic; extern unsigned long sysctl_hung_task_check_count; diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index c8b7446b27df..1da39ea248fd 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -178,6 +178,9 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) arch_suspend_enable_irqs(); BUG_ON(irqs_disabled()); + /* Kick the lockup detector */ + lockup_detector_bootcpu_resume(); + Enable_cpus: enable_nonboot_cpus(); diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 4b1dfba70f7c..69add8a9da68 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -575,7 +575,7 @@ out: /* * Create/destroy watchdog threads as CPUs come and go: */ -static int __cpuinit +static int cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { int hotcpu = (unsigned long)hcpu; @@ -610,10 +610,27 @@ cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) return NOTIFY_OK; } -static struct notifier_block __cpuinitdata cpu_nfb = { +static struct notifier_block cpu_nfb = { .notifier_call = cpu_callback }; +#ifdef CONFIG_SUSPEND +/* + * On exit from suspend we force an offline->online transition on the boot CPU + * so that the PMU state that was lost while in suspended state gets set up + * properly for the boot CPU. This information is required for restarting the + * NMI watchdog. + */ +void lockup_detector_bootcpu_resume(void) +{ + void *cpu = (void *)(long)smp_processor_id(); + + cpu_callback(&cpu_nfb, CPU_DEAD_FROZEN, cpu); + cpu_callback(&cpu_nfb, CPU_UP_PREPARE_FROZEN, cpu); + cpu_callback(&cpu_nfb, CPU_ONLINE_FROZEN, cpu); +} +#endif + void __init lockup_detector_init(void) { void *cpu = (void *)(long)smp_processor_id(); -- cgit v1.2.3 From acc8fa41ad31c576cdbc569cc3e0e443b1b98b44 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 30 Jul 2012 14:40:09 -0700 Subject: printk: add generic functions to find KERN_ headers The current form of a KERN_ is "<.>". Add printk_get_level and printk_skip_level functions to handle these formats. These functions centralize tests of KERN_ so a future modification can change the KERN_ style and shorten the number of bytes consumed by these headers. [akpm@linux-foundation.org: fix build error and warning] Signed-off-by: Joe Perches Cc: Kay Sievers Cc: Wu Fengguang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/printk.h | 26 ++++++++++++++++++++++++++ kernel/printk.c | 14 +++++++++----- 2 files changed, 35 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/printk.h b/include/linux/printk.h index 1bec2f7a2d42..6e12e1f09047 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -24,6 +24,32 @@ extern const char linux_proc_banner[]; */ #define KERN_CONT "" +static inline int printk_get_level(const char *buffer) +{ + if (buffer[0] == '<' && buffer[1] && buffer[2] == '>') { + switch (buffer[1]) { + case '0' ... '7': + case 'd': /* KERN_DEFAULT */ + case 'c': /* KERN_CONT */ + return buffer[1]; + } + } + return 0; +} + +static inline const char *printk_skip_level(const char *buffer) +{ + if (printk_get_level(buffer)) { + switch (buffer[1]) { + case '0' ... '7': + case 'd': /* KERN_DEFAULT */ + case 'c': /* KERN_CONT */ + return buffer + 3; + } + } + return buffer; +} + extern int console_printk[]; #define console_loglevel (console_printk[0]) diff --git a/kernel/printk.c b/kernel/printk.c index 852269adad25..0d882a2f231e 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -1487,6 +1487,7 @@ asmlinkage int vprintk_emit(int facility, int level, size_t text_len; enum log_flags lflags = 0; unsigned long flags; + int kern_level; int this_cpu; int printed_len = 0; @@ -1543,17 +1544,20 @@ asmlinkage int vprintk_emit(int facility, int level, } /* strip syslog prefix and extract log level or control flags */ - if (text[0] == '<' && text[1] && text[2] == '>') { - switch (text[1]) { + kern_level = printk_get_level(text); + if (kern_level) { + const char *end_of_header = printk_skip_level(text); + switch (kern_level) { case '0' ... '7': if (level == -1) - level = text[1] - '0'; + level = kern_level - '0'; case 'd': /* KERN_DEFAULT */ lflags |= LOG_PREFIX; case 'c': /* KERN_CONT */ - text += 3; - text_len -= 3; + break; } + text_len -= end_of_header - text; + text = (char *)end_of_header; } if (level == -1) -- cgit v1.2.3 From 314ba3520e513a78be80e8c2ddbd65c91e78a114 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 30 Jul 2012 14:40:11 -0700 Subject: printk: add kern_levels.h to make KERN_ available for asm use Separate the printk.h file into 2 pieces so the definitions can be used in asm files. Signed-off-by: Joe Perches Cc: Kay Sievers Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kern_levels.h | 22 ++++++++++++++++++++++ include/linux/printk.h | 19 +------------------ 2 files changed, 23 insertions(+), 18 deletions(-) create mode 100644 include/linux/kern_levels.h (limited to 'include') diff --git a/include/linux/kern_levels.h b/include/linux/kern_levels.h new file mode 100644 index 000000000000..337e56ff5332 --- /dev/null +++ b/include/linux/kern_levels.h @@ -0,0 +1,22 @@ +#ifndef __KERN_LEVELS_H__ +#define __KERN_LEVELS_H__ + +#define KERN_EMERG "<0>" /* system is unusable */ +#define KERN_ALERT "<1>" /* action must be taken immediately */ +#define KERN_CRIT "<2>" /* critical conditions */ +#define KERN_ERR "<3>" /* error conditions */ +#define KERN_WARNING "<4>" /* warning conditions */ +#define KERN_NOTICE "<5>" /* normal but significant condition */ +#define KERN_INFO "<6>" /* informational */ +#define KERN_DEBUG "<7>" /* debug-level messages */ + +/* Use the default kernel loglevel */ +#define KERN_DEFAULT "" +/* + * Annotation for a "continued" line of log printout (only done after a + * line that had no enclosing \n). Only to be used by core/arch code + * during early bootup (a continued line is not SMP-safe otherwise). + */ +#define KERN_CONT "" + +#endif diff --git a/include/linux/printk.h b/include/linux/printk.h index 6e12e1f09047..fea2de37b645 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -2,28 +2,11 @@ #define __KERNEL_PRINTK__ #include +#include extern const char linux_banner[]; extern const char linux_proc_banner[]; -#define KERN_EMERG "<0>" /* system is unusable */ -#define KERN_ALERT "<1>" /* action must be taken immediately */ -#define KERN_CRIT "<2>" /* critical conditions */ -#define KERN_ERR "<3>" /* error conditions */ -#define KERN_WARNING "<4>" /* warning conditions */ -#define KERN_NOTICE "<5>" /* normal but significant condition */ -#define KERN_INFO "<6>" /* informational */ -#define KERN_DEBUG "<7>" /* debug-level messages */ - -/* Use the default kernel loglevel */ -#define KERN_DEFAULT "" -/* - * Annotation for a "continued" line of log printout (only done after a - * line that had no enclosing \n). Only to be used by core/arch code - * during early bootup (a continued line is not SMP-safe otherwise). - */ -#define KERN_CONT "" - static inline int printk_get_level(const char *buffer) { if (buffer[0] == '<' && buffer[1] && buffer[2] == '>') { -- cgit v1.2.3 From 04d2c8c83d0e3ac5f78aeede51babb3236200112 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 30 Jul 2012 14:40:17 -0700 Subject: printk: convert the format for KERN_ to a 2 byte pattern Instead of "<.>", use an ASCII SOH for the KERN_ prefix initiator. This saves 1 byte per printk, thousands of bytes in a normal kernel. No output changes are produced as vprintk_emit converts these uses to "<.>". Signed-off-by: Joe Perches Cc: Kay Sievers Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kern_levels.h | 25 ++++++++++++++----------- include/linux/printk.h | 4 ++-- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/kern_levels.h b/include/linux/kern_levels.h index 337e56ff5332..8c719a955b5b 100644 --- a/include/linux/kern_levels.h +++ b/include/linux/kern_levels.h @@ -1,22 +1,25 @@ #ifndef __KERN_LEVELS_H__ #define __KERN_LEVELS_H__ -#define KERN_EMERG "<0>" /* system is unusable */ -#define KERN_ALERT "<1>" /* action must be taken immediately */ -#define KERN_CRIT "<2>" /* critical conditions */ -#define KERN_ERR "<3>" /* error conditions */ -#define KERN_WARNING "<4>" /* warning conditions */ -#define KERN_NOTICE "<5>" /* normal but significant condition */ -#define KERN_INFO "<6>" /* informational */ -#define KERN_DEBUG "<7>" /* debug-level messages */ +#define KERN_SOH "\001" /* ASCII Start Of Header */ +#define KERN_SOH_ASCII '\001' + +#define KERN_EMERG KERN_SOH "0" /* system is unusable */ +#define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */ +#define KERN_CRIT KERN_SOH "2" /* critical conditions */ +#define KERN_ERR KERN_SOH "3" /* error conditions */ +#define KERN_WARNING KERN_SOH "4" /* warning conditions */ +#define KERN_NOTICE KERN_SOH "5" /* normal but significant condition */ +#define KERN_INFO KERN_SOH "6" /* informational */ +#define KERN_DEBUG KERN_SOH "7" /* debug-level messages */ + +#define KERN_DEFAULT KERN_SOH "d" /* the default kernel loglevel */ -/* Use the default kernel loglevel */ -#define KERN_DEFAULT "" /* * Annotation for a "continued" line of log printout (only done after a * line that had no enclosing \n). Only to be used by core/arch code * during early bootup (a continued line is not SMP-safe otherwise). */ -#define KERN_CONT "" +#define KERN_CONT KERN_SOH "c" #endif diff --git a/include/linux/printk.h b/include/linux/printk.h index fea2de37b645..93a231f9835c 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -9,7 +9,7 @@ extern const char linux_proc_banner[]; static inline int printk_get_level(const char *buffer) { - if (buffer[0] == '<' && buffer[1] && buffer[2] == '>') { + if (buffer[0] == KERN_SOH_ASCII && buffer[1]) { switch (buffer[1]) { case '0' ... '7': case 'd': /* KERN_DEFAULT */ @@ -27,7 +27,7 @@ static inline const char *printk_skip_level(const char *buffer) case '0' ... '7': case 'd': /* KERN_DEFAULT */ case 'c': /* KERN_CONT */ - return buffer + 3; + return buffer + 2; } } return buffer; -- cgit v1.2.3 From 61e99ab8e35a88b8c4d0f80d3df9ee16df471be5 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 30 Jul 2012 14:40:21 -0700 Subject: printk: remove the now unnecessary "C" annotation for KERN_CONT Now that all KERN_ uses are prefixed with ASCII SOH, there is no need for a KERN_CONT. Keep it backward compatible by adding #define KERN_CONT "" Reduces kernel image size a thousand bytes. Signed-off-by: Joe Perches Cc: Kay Sievers Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kern_levels.h | 2 +- include/linux/printk.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/kern_levels.h b/include/linux/kern_levels.h index 8c719a955b5b..866caaa9e2bb 100644 --- a/include/linux/kern_levels.h +++ b/include/linux/kern_levels.h @@ -20,6 +20,6 @@ * line that had no enclosing \n). Only to be used by core/arch code * during early bootup (a continued line is not SMP-safe otherwise). */ -#define KERN_CONT KERN_SOH "c" +#define KERN_CONT "" #endif diff --git a/include/linux/printk.h b/include/linux/printk.h index 93a231f9835c..9afc01e5a0a6 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -13,7 +13,6 @@ static inline int printk_get_level(const char *buffer) switch (buffer[1]) { case '0' ... '7': case 'd': /* KERN_DEFAULT */ - case 'c': /* KERN_CONT */ return buffer[1]; } } @@ -26,7 +25,6 @@ static inline const char *printk_skip_level(const char *buffer) switch (buffer[1]) { case '0' ... '7': case 'd': /* KERN_DEFAULT */ - case 'c': /* KERN_CONT */ return buffer + 2; } } -- cgit v1.2.3 From a1fcb2e31822c0617c6e274de4af2a2bb5dc7d3f Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Mon, 30 Jul 2012 14:40:50 -0700 Subject: backlight: move register definitions from header to source ROM boundary definitions do not need to be exported because these are used only internally in the lp855x driver. And few code cosmetic changes Signed-off-by: Milo(Woogyom) Kim Cc: Richard Purdie Cc: Bryan Wu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/lp855x_bl.c | 8 ++++++-- include/linux/lp855x.h | 6 ------ 2 files changed, 6 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c index 72a0e0c917cf..3d24314bc4e1 100644 --- a/drivers/video/backlight/lp855x_bl.c +++ b/drivers/video/backlight/lp855x_bl.c @@ -17,8 +17,12 @@ #include /* Registers */ -#define BRIGHTNESS_CTRL (0x00) -#define DEVICE_CTRL (0x01) +#define BRIGHTNESS_CTRL 0x00 +#define DEVICE_CTRL 0x01 +#define EEPROM_START 0xA0 +#define EEPROM_END 0xA7 +#define EPROM_START 0xA0 +#define EPROM_END 0xAF #define BUF_SIZE 20 #define DEFAULT_BL_NAME "lcd-backlight" diff --git a/include/linux/lp855x.h b/include/linux/lp855x.h index 781a490a451b..cc76f1f18f18 100644 --- a/include/linux/lp855x.h +++ b/include/linux/lp855x.h @@ -47,12 +47,6 @@ (LP8556_I2C_ONLY << BRT_MODE_SHFT)) #define LP8556_COMB2_CONFIG (LP8556_COMBINED2 << BRT_MODE_SHFT) -/* ROM area boundary */ -#define EEPROM_START (0xA0) -#define EEPROM_END (0xA7) -#define EPROM_START (0xA0) -#define EPROM_END (0xAF) - enum lp855x_chip_id { LP8550, LP8551, -- cgit v1.2.3 From f7f95056779eb69c5fc3ac30e5cb6fd28bdbba43 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Mon, 30 Jul 2012 14:40:53 -0700 Subject: backlight: move lp855x header into platform_data directory The lp855x header is used only in the platform side, so it can be moved into platform_data directory Signed-off-by: Milo(Woogyom) Kim Cc: Richard Purdie Cc: Bryan Wu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/lp855x_bl.c | 2 +- include/linux/lp855x.h | 125 ----------------------------------- include/linux/platform_data/lp855x.h | 125 +++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 126 deletions(-) delete mode 100644 include/linux/lp855x.h create mode 100644 include/linux/platform_data/lp855x.h (limited to 'include') diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c index 3d24314bc4e1..aa6d4f71131f 100644 --- a/drivers/video/backlight/lp855x_bl.c +++ b/drivers/video/backlight/lp855x_bl.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include /* Registers */ #define BRIGHTNESS_CTRL 0x00 diff --git a/include/linux/lp855x.h b/include/linux/lp855x.h deleted file mode 100644 index cc76f1f18f18..000000000000 --- a/include/linux/lp855x.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * LP855x Backlight Driver - * - * Copyright (C) 2011 Texas Instruments - * - * 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 _LP855X_H -#define _LP855X_H - -#define BL_CTL_SHFT (0) -#define BRT_MODE_SHFT (1) -#define BRT_MODE_MASK (0x06) - -/* Enable backlight. Only valid when BRT_MODE=10(I2C only) */ -#define ENABLE_BL (1) -#define DISABLE_BL (0) - -#define I2C_CONFIG(id) id ## _I2C_CONFIG -#define PWM_CONFIG(id) id ## _PWM_CONFIG - -/* DEVICE CONTROL register - LP8550 */ -#define LP8550_PWM_CONFIG (LP8550_PWM_ONLY << BRT_MODE_SHFT) -#define LP8550_I2C_CONFIG ((ENABLE_BL << BL_CTL_SHFT) | \ - (LP8550_I2C_ONLY << BRT_MODE_SHFT)) - -/* DEVICE CONTROL register - LP8551 */ -#define LP8551_PWM_CONFIG LP8550_PWM_CONFIG -#define LP8551_I2C_CONFIG LP8550_I2C_CONFIG - -/* DEVICE CONTROL register - LP8552 */ -#define LP8552_PWM_CONFIG LP8550_PWM_CONFIG -#define LP8552_I2C_CONFIG LP8550_I2C_CONFIG - -/* DEVICE CONTROL register - LP8553 */ -#define LP8553_PWM_CONFIG LP8550_PWM_CONFIG -#define LP8553_I2C_CONFIG LP8550_I2C_CONFIG - -/* DEVICE CONTROL register - LP8556 */ -#define LP8556_PWM_CONFIG (LP8556_PWM_ONLY << BRT_MODE_SHFT) -#define LP8556_COMB1_CONFIG (LP8556_COMBINED1 << BRT_MODE_SHFT) -#define LP8556_I2C_CONFIG ((ENABLE_BL << BL_CTL_SHFT) | \ - (LP8556_I2C_ONLY << BRT_MODE_SHFT)) -#define LP8556_COMB2_CONFIG (LP8556_COMBINED2 << BRT_MODE_SHFT) - -enum lp855x_chip_id { - LP8550, - LP8551, - LP8552, - LP8553, - LP8556, -}; - -enum lp855x_brightness_ctrl_mode { - PWM_BASED = 1, - REGISTER_BASED, -}; - -enum lp8550_brighntess_source { - LP8550_PWM_ONLY, - LP8550_I2C_ONLY = 2, -}; - -enum lp8551_brighntess_source { - LP8551_PWM_ONLY = LP8550_PWM_ONLY, - LP8551_I2C_ONLY = LP8550_I2C_ONLY, -}; - -enum lp8552_brighntess_source { - LP8552_PWM_ONLY = LP8550_PWM_ONLY, - LP8552_I2C_ONLY = LP8550_I2C_ONLY, -}; - -enum lp8553_brighntess_source { - LP8553_PWM_ONLY = LP8550_PWM_ONLY, - LP8553_I2C_ONLY = LP8550_I2C_ONLY, -}; - -enum lp8556_brightness_source { - LP8556_PWM_ONLY, - LP8556_COMBINED1, /* pwm + i2c before the shaper block */ - LP8556_I2C_ONLY, - LP8556_COMBINED2, /* pwm + i2c after the shaper block */ -}; - -struct lp855x_pwm_data { - void (*pwm_set_intensity) (int brightness, int max_brightness); - int (*pwm_get_intensity) (int max_brightness); -}; - -struct lp855x_rom_data { - u8 addr; - u8 val; -}; - -/** - * struct lp855x_platform_data - * @name : Backlight driver name. If it is not defined, default name is set. - * @mode : brightness control by pwm or lp855x register - * @device_control : value of DEVICE CONTROL register - * @initial_brightness : initial value of backlight brightness - * @pwm_data : platform specific pwm generation functions. - Only valid when mode is PWM_BASED. - * @load_new_rom_data : - 0 : use default configuration data - 1 : update values of eeprom or eprom registers on loading driver - * @size_program : total size of lp855x_rom_data - * @rom_data : list of new eeprom/eprom registers - */ -struct lp855x_platform_data { - char *name; - enum lp855x_brightness_ctrl_mode mode; - u8 device_control; - int initial_brightness; - struct lp855x_pwm_data pwm_data; - u8 load_new_rom_data; - int size_program; - struct lp855x_rom_data *rom_data; -}; - -#endif diff --git a/include/linux/platform_data/lp855x.h b/include/linux/platform_data/lp855x.h new file mode 100644 index 000000000000..cc76f1f18f18 --- /dev/null +++ b/include/linux/platform_data/lp855x.h @@ -0,0 +1,125 @@ +/* + * LP855x Backlight Driver + * + * Copyright (C) 2011 Texas Instruments + * + * 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 _LP855X_H +#define _LP855X_H + +#define BL_CTL_SHFT (0) +#define BRT_MODE_SHFT (1) +#define BRT_MODE_MASK (0x06) + +/* Enable backlight. Only valid when BRT_MODE=10(I2C only) */ +#define ENABLE_BL (1) +#define DISABLE_BL (0) + +#define I2C_CONFIG(id) id ## _I2C_CONFIG +#define PWM_CONFIG(id) id ## _PWM_CONFIG + +/* DEVICE CONTROL register - LP8550 */ +#define LP8550_PWM_CONFIG (LP8550_PWM_ONLY << BRT_MODE_SHFT) +#define LP8550_I2C_CONFIG ((ENABLE_BL << BL_CTL_SHFT) | \ + (LP8550_I2C_ONLY << BRT_MODE_SHFT)) + +/* DEVICE CONTROL register - LP8551 */ +#define LP8551_PWM_CONFIG LP8550_PWM_CONFIG +#define LP8551_I2C_CONFIG LP8550_I2C_CONFIG + +/* DEVICE CONTROL register - LP8552 */ +#define LP8552_PWM_CONFIG LP8550_PWM_CONFIG +#define LP8552_I2C_CONFIG LP8550_I2C_CONFIG + +/* DEVICE CONTROL register - LP8553 */ +#define LP8553_PWM_CONFIG LP8550_PWM_CONFIG +#define LP8553_I2C_CONFIG LP8550_I2C_CONFIG + +/* DEVICE CONTROL register - LP8556 */ +#define LP8556_PWM_CONFIG (LP8556_PWM_ONLY << BRT_MODE_SHFT) +#define LP8556_COMB1_CONFIG (LP8556_COMBINED1 << BRT_MODE_SHFT) +#define LP8556_I2C_CONFIG ((ENABLE_BL << BL_CTL_SHFT) | \ + (LP8556_I2C_ONLY << BRT_MODE_SHFT)) +#define LP8556_COMB2_CONFIG (LP8556_COMBINED2 << BRT_MODE_SHFT) + +enum lp855x_chip_id { + LP8550, + LP8551, + LP8552, + LP8553, + LP8556, +}; + +enum lp855x_brightness_ctrl_mode { + PWM_BASED = 1, + REGISTER_BASED, +}; + +enum lp8550_brighntess_source { + LP8550_PWM_ONLY, + LP8550_I2C_ONLY = 2, +}; + +enum lp8551_brighntess_source { + LP8551_PWM_ONLY = LP8550_PWM_ONLY, + LP8551_I2C_ONLY = LP8550_I2C_ONLY, +}; + +enum lp8552_brighntess_source { + LP8552_PWM_ONLY = LP8550_PWM_ONLY, + LP8552_I2C_ONLY = LP8550_I2C_ONLY, +}; + +enum lp8553_brighntess_source { + LP8553_PWM_ONLY = LP8550_PWM_ONLY, + LP8553_I2C_ONLY = LP8550_I2C_ONLY, +}; + +enum lp8556_brightness_source { + LP8556_PWM_ONLY, + LP8556_COMBINED1, /* pwm + i2c before the shaper block */ + LP8556_I2C_ONLY, + LP8556_COMBINED2, /* pwm + i2c after the shaper block */ +}; + +struct lp855x_pwm_data { + void (*pwm_set_intensity) (int brightness, int max_brightness); + int (*pwm_get_intensity) (int max_brightness); +}; + +struct lp855x_rom_data { + u8 addr; + u8 val; +}; + +/** + * struct lp855x_platform_data + * @name : Backlight driver name. If it is not defined, default name is set. + * @mode : brightness control by pwm or lp855x register + * @device_control : value of DEVICE CONTROL register + * @initial_brightness : initial value of backlight brightness + * @pwm_data : platform specific pwm generation functions. + Only valid when mode is PWM_BASED. + * @load_new_rom_data : + 0 : use default configuration data + 1 : update values of eeprom or eprom registers on loading driver + * @size_program : total size of lp855x_rom_data + * @rom_data : list of new eeprom/eprom registers + */ +struct lp855x_platform_data { + char *name; + enum lp855x_brightness_ctrl_mode mode; + u8 device_control; + int initial_brightness; + struct lp855x_pwm_data pwm_data; + u8 load_new_rom_data; + int size_program; + struct lp855x_rom_data *rom_data; +}; + +#endif -- cgit v1.2.3 From 639b9e34f15e4b2c30068a4e4485586af0cdf709 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Mon, 30 Jul 2012 14:40:55 -0700 Subject: string: introduce memweight() memweight() is the function that counts the total number of bits set in memory area. Unlike bitmap_weight(), memweight() takes pointer and size in bytes to specify a memory area which does not need to be aligned to long-word boundary. [akpm@linux-foundation.org: rename `w' to `ret'] Signed-off-by: Akinobu Mita Cc: Anders Larsen Cc: Alasdair Kergon Cc: Laurent Pinchart Cc: Mark Fasheh Cc: Joel Becker Cc: Jan Kara Cc: Andreas Dilger Cc: "Theodore Ts'o" Cc: Matthew Wilcox Cc: Mauro Carvalho Chehab Cc: Tony Luck Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/string.h | 3 +++ lib/Makefile | 2 +- lib/memweight.c | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 lib/memweight.c (limited to 'include') diff --git a/include/linux/string.h b/include/linux/string.h index e033564f10ba..ffe0442e18d2 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -145,4 +145,7 @@ static inline bool strstarts(const char *str, const char *prefix) return strncmp(str, prefix, strlen(prefix)) == 0; } #endif + +extern size_t memweight(const void *ptr, size_t bytes); + #endif /* _LINUX_STRING_H_ */ diff --git a/lib/Makefile b/lib/Makefile index 8c31a0cb75e9..df663cc05c73 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -22,7 +22,7 @@ lib-y += kobject.o klist.o obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \ string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o \ - bsearch.o find_last_bit.o find_next_bit.o llist.o + bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o obj-y += kstrtox.o obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o diff --git a/lib/memweight.c b/lib/memweight.c new file mode 100644 index 000000000000..e35fc8771893 --- /dev/null +++ b/lib/memweight.c @@ -0,0 +1,38 @@ +#include +#include +#include + +/** + * memweight - count the total number of bits set in memory area + * @ptr: pointer to the start of the area + * @bytes: the size of the area + */ +size_t memweight(const void *ptr, size_t bytes) +{ + size_t ret = 0; + size_t longs; + const unsigned char *bitmap = ptr; + + for (; bytes > 0 && ((unsigned long)bitmap) % sizeof(long); + bytes--, bitmap++) + ret += hweight8(*bitmap); + + longs = bytes / sizeof(long); + if (longs) { + BUG_ON(longs >= INT_MAX / BITS_PER_LONG); + ret += bitmap_weight((unsigned long *)bitmap, + longs * BITS_PER_LONG); + bytes -= longs * sizeof(long); + bitmap += longs * sizeof(long); + } + /* + * The reason that this last loop is distinct from the preceding + * bitmap_weight() call is to compute 1-bits in the last region smaller + * than sizeof(long) properly on big-endian systems. + */ + for (; bytes > 0; bytes--, bitmap++) + ret += hweight8(*bitmap); + + return ret; +} +EXPORT_SYMBOL(memweight); -- cgit v1.2.3 From 8c74ac0557b4cabc9017c2740c5f62b330192416 Mon Sep 17 00:00:00 2001 From: Vyacheslav Dubeyko Date: Mon, 30 Jul 2012 14:42:09 -0700 Subject: nilfs2: add omitted comments for structures in nilfs2_fs.h Add omitted comments for structures in nilfs2_fs.h. Signed-off-by: Vyacheslav Dubeyko Signed-off-by: Ryusuke Konishi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/nilfs2_fs.h | 63 +++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h index 89bd4a4dcfb4..98755767c7b0 100644 --- a/include/linux/nilfs2_fs.h +++ b/include/linux/nilfs2_fs.h @@ -293,7 +293,7 @@ struct nilfs_dir_entry { __le64 inode; /* Inode number */ __le16 rec_len; /* Directory entry length */ __u8 name_len; /* Name length */ - __u8 file_type; + __u8 file_type; /* Dir entry type (file, dir, etc) */ char name[NILFS_NAME_LEN]; /* File name */ char pad; }; @@ -395,7 +395,7 @@ union nilfs_binfo { }; /** - * struct nilfs_segment_summary - segment summary + * struct nilfs_segment_summary - segment summary header * @ss_datasum: checksum of data * @ss_sumsum: checksum of segment summary * @ss_magic: magic number @@ -683,9 +683,9 @@ struct nilfs_sufile_header { /** * nilfs_suinfo - segment usage information - * @sui_lastmod: - * @sui_nblocks: - * @sui_flags: + * @sui_lastmod: timestamp of last modification + * @sui_nblocks: number of written blocks in segment + * @sui_flags: segment usage flags */ struct nilfs_suinfo { __u64 sui_lastmod; @@ -716,9 +716,10 @@ enum { }; /** - * struct nilfs_cpmode - - * @cc_cno: - * @cc_mode: + * struct nilfs_cpmode - change checkpoint mode structure + * @cm_cno: checkpoint number + * @cm_mode: mode of checkpoint + * @cm_pad: padding */ struct nilfs_cpmode { __u64 cm_cno; @@ -728,11 +729,11 @@ struct nilfs_cpmode { /** * struct nilfs_argv - argument vector - * @v_base: - * @v_nmembs: - * @v_size: - * @v_flags: - * @v_index: + * @v_base: pointer on data array from userspace + * @v_nmembs: number of members in data array + * @v_size: size of data array in bytes + * @v_flags: flags + * @v_index: start number of target data items */ struct nilfs_argv { __u64 v_base; @@ -743,9 +744,9 @@ struct nilfs_argv { }; /** - * struct nilfs_period - - * @p_start: - * @p_end: + * struct nilfs_period - period of checkpoint numbers + * @p_start: start checkpoint number (inclusive) + * @p_end: end checkpoint number (exclusive) */ struct nilfs_period { __u64 p_start; @@ -753,7 +754,7 @@ struct nilfs_period { }; /** - * struct nilfs_cpstat - + * struct nilfs_cpstat - checkpoint statistics * @cs_cno: checkpoint number * @cs_ncps: number of checkpoints * @cs_nsss: number of snapshots @@ -765,7 +766,7 @@ struct nilfs_cpstat { }; /** - * struct nilfs_sustat - + * struct nilfs_sustat - segment usage statistics * @ss_nsegs: number of segments * @ss_ncleansegs: number of clean segments * @ss_ndirtysegs: number of dirty segments @@ -784,10 +785,10 @@ struct nilfs_sustat { /** * struct nilfs_vinfo - virtual block number information - * @vi_vblocknr: - * @vi_start: - * @vi_end: - * @vi_blocknr: + * @vi_vblocknr: virtual block number + * @vi_start: start checkpoint number (inclusive) + * @vi_end: end checkpoint number (exclusive) + * @vi_blocknr: disk block number */ struct nilfs_vinfo { __u64 vi_vblocknr; @@ -797,7 +798,15 @@ struct nilfs_vinfo { }; /** - * struct nilfs_vdesc - + * struct nilfs_vdesc - descriptor of virtual block number + * @vd_ino: inode number + * @vd_cno: checkpoint number + * @vd_vblocknr: virtual block number + * @vd_period: period of checkpoint numbers + * @vd_blocknr: disk block number + * @vd_offset: logical block offset inside a file + * @vd_flags: flags (data or node block) + * @vd_pad: padding */ struct nilfs_vdesc { __u64 vd_ino; @@ -811,7 +820,13 @@ struct nilfs_vdesc { }; /** - * struct nilfs_bdesc - + * struct nilfs_bdesc - descriptor of disk block number + * @bd_ino: inode number + * @bd_oblocknr: disk block address (for skipping dead blocks) + * @bd_blocknr: disk block address + * @bd_offset: logical block offset inside a file + * @bd_level: level in the b-tree organization + * @bd_pad: padding */ struct nilfs_bdesc { __u64 bd_ino; -- cgit v1.2.3 From 079a96ae3871f0ed9083aac2218136ccec5b9877 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 30 Jul 2012 14:42:38 -0700 Subject: ipc: add COMPAT_SHMLBA support If the SHMLBA definition for a native task differs from the definition for a compat task, the do_shmat() function would need to handle both. This patch introduces COMPAT_SHMLBA, which is used by the compat shmat syscall when calling the ipc code and allows architectures such as AArch64 (where the native SHMLBA is 64k but the compat (AArch32) definition is 16k) to provide the correct semantics for compat IPC system calls. Cc: David S. Miller Cc: Chris Zankel Cc: Arnd Bergmann Acked-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/sparc/kernel/sys_sparc_64.c | 2 +- arch/xtensa/kernel/syscall.c | 2 +- include/linux/shm.h | 6 ++++-- ipc/compat.c | 8 ++++++-- ipc/shm.c | 9 +++++---- ipc/syscall.c | 2 +- 6 files changed, 18 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c index c38e5aaae56f..0dc1f5786081 100644 --- a/arch/sparc/kernel/sys_sparc_64.c +++ b/arch/sparc/kernel/sys_sparc_64.c @@ -470,7 +470,7 @@ SYSCALL_DEFINE6(sparc_ipc, unsigned int, call, int, first, unsigned long, second switch (call) { case SHMAT: { ulong raddr; - err = do_shmat(first, ptr, (int)second, &raddr); + err = do_shmat(first, ptr, (int)second, &raddr, SHMLBA); if (!err) { if (put_user(raddr, (ulong __user *) third)) diff --git a/arch/xtensa/kernel/syscall.c b/arch/xtensa/kernel/syscall.c index 816e6d0d686c..05b3f093d5d7 100644 --- a/arch/xtensa/kernel/syscall.c +++ b/arch/xtensa/kernel/syscall.c @@ -44,7 +44,7 @@ asmlinkage long xtensa_shmat(int shmid, char __user *shmaddr, int shmflg) unsigned long ret; long err; - err = do_shmat(shmid, shmaddr, shmflg, &ret); + err = do_shmat(shmid, shmaddr, shmflg, &ret, SHMLBA); if (err) return err; return (long)ret; diff --git a/include/linux/shm.h b/include/linux/shm.h index 92808b86703b..edd086883ccb 100644 --- a/include/linux/shm.h +++ b/include/linux/shm.h @@ -107,12 +107,14 @@ struct shmid_kernel /* private to the kernel */ #define SHM_NORESERVE 010000 /* don't check for reservations */ #ifdef CONFIG_SYSVIPC -long do_shmat(int shmid, char __user *shmaddr, int shmflg, unsigned long *addr); +long do_shmat(int shmid, char __user *shmaddr, int shmflg, unsigned long *addr, + unsigned long shmlba); extern int is_file_shm_hugepages(struct file *file); extern void exit_shm(struct task_struct *task); #else static inline long do_shmat(int shmid, char __user *shmaddr, - int shmflg, unsigned long *addr) + int shmflg, unsigned long *addr, + unsigned long shmlba) { return -ENOSYS; } diff --git a/ipc/compat.c b/ipc/compat.c index a6df704f521e..53cebdf80e3c 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -514,6 +514,10 @@ long compat_sys_msgctl(int first, int second, void __user *uptr) return err; } +#ifndef COMPAT_SHMLBA +#define COMPAT_SHMLBA SHMLBA +#endif + #ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, void __user *uptr) @@ -524,7 +528,7 @@ long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, if (version == 1) return -EINVAL; - err = do_shmat(first, uptr, second, &raddr); + err = do_shmat(first, uptr, second, &raddr, COMPAT_SHMLBA); if (err < 0) return err; uaddr = compat_ptr(third); @@ -536,7 +540,7 @@ long compat_sys_shmat(int shmid, compat_uptr_t shmaddr, int shmflg) unsigned long ret; long err; - err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret); + err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret, COMPAT_SHMLBA); if (err) return err; force_successful_syscall_return(); diff --git a/ipc/shm.c b/ipc/shm.c index 41c1285d697a..00faa05cf72a 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -953,7 +953,8 @@ out: * "raddr" thing points to kernel space, and there has to be a wrapper around * this. */ -long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr) +long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr, + unsigned long shmlba) { struct shmid_kernel *shp; unsigned long addr; @@ -973,9 +974,9 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr) if (shmid < 0) goto out; else if ((addr = (ulong)shmaddr)) { - if (addr & (SHMLBA-1)) { + if (addr & (shmlba - 1)) { if (shmflg & SHM_RND) - addr &= ~(SHMLBA-1); /* round down */ + addr &= ~(shmlba - 1); /* round down */ else #ifndef __ARCH_FORCE_SHMLBA if (addr & ~PAGE_MASK) @@ -1107,7 +1108,7 @@ SYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg) unsigned long ret; long err; - err = do_shmat(shmid, shmaddr, shmflg, &ret); + err = do_shmat(shmid, shmaddr, shmflg, &ret, SHMLBA); if (err) return err; force_successful_syscall_return(); diff --git a/ipc/syscall.c b/ipc/syscall.c index 1d6f53f6b562..0d1e32ce048e 100644 --- a/ipc/syscall.c +++ b/ipc/syscall.c @@ -73,7 +73,7 @@ SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second, default: { unsigned long raddr; ret = do_shmat(first, (char __user *)ptr, - second, &raddr); + second, &raddr, SHMLBA); if (ret) return ret; return put_user(raddr, (unsigned long __user *) third); -- cgit v1.2.3 From b610c04c667f3c056243fd64041c7f152a512ee4 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 30 Jul 2012 14:42:40 -0700 Subject: ipc: allow compat IPC version field parsing if !ARCH_WANT_OLD_COMPAT_IPC Commit 48b25c43e6ee ("ipc: provide generic compat versions of IPC syscalls") added a new ARCH_WANT_OLD_COMPAT_IPC config option for architectures to select if their compat target requires the old IPC syscall interface. For architectures (such as AArch64) that do not require the internal calling conventions provided by this option, but have a compat target where the C library passes the IPC_64 flag explicitly, compat_ipc_parse_version no longer strips out the flag before calling the native system call implementation, resulting in unknown SHM/IPC commands and -EINVAL being returned to userspace. This patch separates the selection of the internal calling conventions for the IPC syscalls from the version parsing, allowing architectures to select __ARCH_WANT_COMPAT_IPC_PARSE_VERSION if they want to use version parsing whilst retaining the newer syscall calling conventions. Acked-by: Chris Metcalf Cc: Arnd Bergmann Acked-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/compat.h | 1 + ipc/compat.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/compat.h b/include/linux/compat.h index 4e890394ef99..9f68e90a14ec 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -256,6 +256,7 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr, compat_size_t __user *len_ptr); #ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC +#define __ARCH_WANT_COMPAT_IPC_PARSE_VERSION long compat_sys_semctl(int first, int second, int third, void __user *uptr); long compat_sys_msgsnd(int first, int second, int third, void __user *uptr); long compat_sys_msgrcv(int first, int second, int msgtyp, int third, diff --git a/ipc/compat.c b/ipc/compat.c index 53cebdf80e3c..a41600f6ba52 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -118,7 +118,7 @@ extern int sem_ctls[]; static inline int compat_ipc_parse_version(int *cmd) { -#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC +#ifdef __ARCH_WANT_COMPAT_IPC_PARSE_VERSION int version = *cmd & IPC_64; /* this is tricky: architectures that have support for the old -- cgit v1.2.3 From 05ba3f1aa1b04e921068249dd52a80bc84c2aeb4 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 30 Jul 2012 14:42:43 -0700 Subject: ipc: compat: use signed size_t types for msgsnd and msgrcv The msgsnd and msgrcv system calls use size_t to represent the size of the message being transferred. POSIX states that values of msgsz greater than SSIZE_MAX cause the result to be implementation-defined. On Linux, this equates to returning -EINVAL if (long) msgsz < 0. For compat tasks where !CONFIG_ARCH_WANT_OLD_COMPAT_IPC and compat_size_t is smaller than size_t, negative size values passed from userspace will be interpreted as positive values by do_msg{rcv,snd} and will fail to exit early with -EINVAL. This patch changes the compat prototypes for msg{rcv,snd} so that the message size is represented as a compat_ssize_t, which we cast to the native ssize_t type for the core IPC code. Cc: Arnd Bergmann Acked-by: Chris Metcalf Acked-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/compat.h | 4 ++-- ipc/compat.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/compat.h b/include/linux/compat.h index 9f68e90a14ec..f2b8fe20cc8e 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -266,9 +266,9 @@ long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, #else long compat_sys_semctl(int semid, int semnum, int cmd, int arg); long compat_sys_msgsnd(int msqid, struct compat_msgbuf __user *msgp, - size_t msgsz, int msgflg); + compat_ssize_t msgsz, int msgflg); long compat_sys_msgrcv(int msqid, struct compat_msgbuf __user *msgp, - size_t msgsz, long msgtyp, int msgflg); + compat_ssize_t msgsz, long msgtyp, int msgflg); long compat_sys_shmat(int shmid, compat_uptr_t shmaddr, int shmflg); #endif long compat_sys_msgctl(int first, int second, void __user *uptr); diff --git a/ipc/compat.c b/ipc/compat.c index a41600f6ba52..20f92b2f2932 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -373,21 +373,21 @@ long compat_sys_semctl(int semid, int semnum, int cmd, int arg) } long compat_sys_msgsnd(int msqid, struct compat_msgbuf __user *msgp, - size_t msgsz, int msgflg) + compat_ssize_t msgsz, int msgflg) { compat_long_t mtype; if (get_user(mtype, &msgp->mtype)) return -EFAULT; - return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg); + return do_msgsnd(msqid, mtype, msgp->mtext, (ssize_t)msgsz, msgflg); } long compat_sys_msgrcv(int msqid, struct compat_msgbuf __user *msgp, - size_t msgsz, long msgtyp, int msgflg) + compat_ssize_t msgsz, long msgtyp, int msgflg) { long err, mtype; - err = do_msgrcv(msqid, &mtype, msgp->mtext, msgsz, msgtyp, msgflg); + err = do_msgrcv(msqid, &mtype, msgp->mtext, (ssize_t)msgsz, msgtyp, msgflg); if (err < 0) goto out; -- cgit v1.2.3 From c1d7e01d7877a397655277a920aeaa3830ed9461 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 30 Jul 2012 14:42:46 -0700 Subject: ipc: use Kconfig options for __ARCH_WANT_[COMPAT_]IPC_PARSE_VERSION Rather than #define the options manually in the architecture code, add Kconfig options for them and select them there instead. This also allows us to select the compat IPC version parsing automatically for platforms using the old compat IPC interface. Reported-by: Andrew Morton Signed-off-by: Will Deacon Cc: Arnd Bergmann Cc: Chris Metcalf Cc: Catalin Marinas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/Kconfig | 7 +++++++ arch/alpha/Kconfig | 1 + arch/alpha/include/asm/unistd.h | 1 - arch/arm/Kconfig | 1 + arch/arm/include/asm/unistd.h | 1 - arch/avr32/Kconfig | 1 + arch/avr32/include/asm/unistd.h | 1 - arch/blackfin/Kconfig | 1 + arch/blackfin/include/asm/unistd.h | 1 - arch/cris/Kconfig | 1 + arch/cris/include/asm/unistd.h | 1 - arch/frv/Kconfig | 1 + arch/frv/include/asm/unistd.h | 1 - arch/h8300/Kconfig | 1 + arch/h8300/include/asm/unistd.h | 1 - arch/m32r/Kconfig | 1 + arch/m32r/include/asm/unistd.h | 1 - arch/m68k/Kconfig | 1 + arch/m68k/include/asm/unistd.h | 1 - arch/microblaze/Kconfig | 1 + arch/microblaze/include/asm/unistd.h | 1 - arch/mips/Kconfig | 1 + arch/mips/include/asm/unistd.h | 1 - arch/mn10300/Kconfig | 1 + arch/mn10300/include/asm/unistd.h | 1 - arch/powerpc/Kconfig | 1 + arch/powerpc/include/asm/unistd.h | 1 - arch/s390/Kconfig | 1 + arch/s390/include/asm/unistd.h | 1 - arch/sh/Kconfig | 2 ++ arch/sh/include/asm/unistd.h | 1 - arch/sparc/Kconfig | 1 + arch/sparc/include/asm/unistd.h | 1 - arch/x86/Kconfig | 1 + arch/x86/include/asm/unistd.h | 1 - include/linux/compat.h | 1 - ipc/compat.c | 2 +- ipc/util.c | 4 ++-- ipc/util.h | 2 +- 39 files changed, 29 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/arch/Kconfig b/arch/Kconfig index 8c3d957fa8e2..72f2fa189cc5 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -248,7 +248,14 @@ config HAVE_CMPXCHG_LOCAL config HAVE_CMPXCHG_DOUBLE bool +config ARCH_WANT_IPC_PARSE_VERSION + bool + +config ARCH_WANT_COMPAT_IPC_PARSE_VERSION + bool + config ARCH_WANT_OLD_COMPAT_IPC + select ARCH_WANT_COMPAT_IPC_PARSE_VERSION bool config HAVE_ARCH_SECCOMP_FILTER diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 3de74c9f9610..d5b9b5e645cc 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -14,6 +14,7 @@ config ALPHA select AUTO_IRQ_AFFINITY if SMP select GENERIC_IRQ_SHOW select ARCH_WANT_OPTIONAL_GPIOLIB + select ARCH_WANT_IPC_PARSE_VERSION select ARCH_HAVE_NMI_SAFE_CMPXCHG select GENERIC_SMP_IDLE_THREAD select GENERIC_CMOS_UPDATE diff --git a/arch/alpha/include/asm/unistd.h b/arch/alpha/include/asm/unistd.h index d1f23b722df4..633b23b0664a 100644 --- a/arch/alpha/include/asm/unistd.h +++ b/arch/alpha/include/asm/unistd.h @@ -470,7 +470,6 @@ #define NR_SYSCALLS 504 -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_GETHOSTNAME diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 4172c3cea228..5df11147be84 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -39,6 +39,7 @@ config ARM select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW select GENERIC_IRQ_PROBE + select ARCH_WANT_IPC_PARSE_VERSION select HARDIRQS_SW_RESEND select CPU_PM if (SUSPEND || CPU_IDLE) select GENERIC_PCI_IOMAP diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h index 512cd1473454..0cab47d4a83f 100644 --- a/arch/arm/include/asm/unistd.h +++ b/arch/arm/include/asm/unistd.h @@ -446,7 +446,6 @@ #ifdef __KERNEL__ -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_GETHOSTNAME #define __ARCH_WANT_SYS_PAUSE diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index 71d38c76726c..5ade51c8a87f 100644 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig @@ -12,6 +12,7 @@ config AVR32 select HARDIRQS_SW_RESEND select GENERIC_IRQ_SHOW select ARCH_HAVE_CUSTOM_GPIO_H + select ARCH_WANT_IPC_PARSE_VERSION select ARCH_HAVE_NMI_SAFE_CMPXCHG select GENERIC_CLOCKEVENTS help diff --git a/arch/avr32/include/asm/unistd.h b/arch/avr32/include/asm/unistd.h index f714544e5560..1358e366f4be 100644 --- a/arch/avr32/include/asm/unistd.h +++ b/arch/avr32/include/asm/unistd.h @@ -318,7 +318,6 @@ /* SMP stuff */ #define __IGNORE_getcpu -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_ALARM #define __ARCH_WANT_SYS_GETHOSTNAME diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index 9b765107e15c..fb9fe00e51a6 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -33,6 +33,7 @@ config BLACKFIN select HAVE_PERF_EVENTS select ARCH_HAVE_CUSTOM_GPIO_H select ARCH_WANT_OPTIONAL_GPIOLIB + select ARCH_WANT_IPC_PARSE_VERSION select HAVE_GENERIC_HARDIRQS select GENERIC_ATOMIC64 select GENERIC_IRQ_PROBE diff --git a/arch/blackfin/include/asm/unistd.h b/arch/blackfin/include/asm/unistd.h index 3287222cba34..5b2a0748d7d3 100644 --- a/arch/blackfin/include/asm/unistd.h +++ b/arch/blackfin/include/asm/unistd.h @@ -434,7 +434,6 @@ #define __IGNORE_getcpu #ifdef __KERNEL__ -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_ALARM #define __ARCH_WANT_SYS_GETHOSTNAME diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig index bb344650a14f..e92215428a37 100644 --- a/arch/cris/Kconfig +++ b/arch/cris/Kconfig @@ -42,6 +42,7 @@ config CRIS select HAVE_IDE select GENERIC_ATOMIC64 select HAVE_GENERIC_HARDIRQS + select ARCH_WANT_IPC_PARSE_VERSION select GENERIC_IRQ_SHOW select GENERIC_IOMAP select GENERIC_SMP_IDLE_THREAD if ETRAX_ARCH_V32 diff --git a/arch/cris/include/asm/unistd.h b/arch/cris/include/asm/unistd.h index f921b8b0f97e..51873a446f87 100644 --- a/arch/cris/include/asm/unistd.h +++ b/arch/cris/include/asm/unistd.h @@ -347,7 +347,6 @@ #include -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT #define __ARCH_WANT_STAT64 diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig index a685910d2d5c..971c0a19facb 100644 --- a/arch/frv/Kconfig +++ b/arch/frv/Kconfig @@ -9,6 +9,7 @@ config FRV select GENERIC_IRQ_SHOW select ARCH_HAVE_NMI_SAFE_CMPXCHG select GENERIC_CPU_DEVICES + select ARCH_WANT_IPC_PARSE_VERSION config ZONE_DMA bool diff --git a/arch/frv/include/asm/unistd.h b/arch/frv/include/asm/unistd.h index a569dff7cd59..67f23a311db6 100644 --- a/arch/frv/include/asm/unistd.h +++ b/arch/frv/include/asm/unistd.h @@ -349,7 +349,6 @@ #define NR_syscalls 338 -#define __ARCH_WANT_IPC_PARSE_VERSION /* #define __ARCH_WANT_OLD_READDIR */ #define __ARCH_WANT_OLD_STAT #define __ARCH_WANT_STAT64 diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig index 56e890df5053..5e8a0d9a09ce 100644 --- a/arch/h8300/Kconfig +++ b/arch/h8300/Kconfig @@ -3,6 +3,7 @@ config H8300 default y select HAVE_IDE select HAVE_GENERIC_HARDIRQS + select ARCH_WANT_IPC_PARSE_VERSION select GENERIC_IRQ_SHOW select GENERIC_CPU_DEVICES diff --git a/arch/h8300/include/asm/unistd.h b/arch/h8300/include/asm/unistd.h index 718511303b4e..5cd882801d79 100644 --- a/arch/h8300/include/asm/unistd.h +++ b/arch/h8300/include/asm/unistd.h @@ -331,7 +331,6 @@ #define NR_syscalls 321 -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT #define __ARCH_WANT_STAT64 diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig index b638d5bfa14d..49498bbb9616 100644 --- a/arch/m32r/Kconfig +++ b/arch/m32r/Kconfig @@ -7,6 +7,7 @@ config M32R select HAVE_KERNEL_GZIP select HAVE_KERNEL_BZIP2 select HAVE_KERNEL_LZMA + select ARCH_WANT_IPC_PARSE_VERSION select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW diff --git a/arch/m32r/include/asm/unistd.h b/arch/m32r/include/asm/unistd.h index 3e1db561aacc..d5e66a480782 100644 --- a/arch/m32r/include/asm/unistd.h +++ b/arch/m32r/include/asm/unistd.h @@ -336,7 +336,6 @@ #define NR_syscalls 326 -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_ALARM #define __ARCH_WANT_SYS_GETHOSTNAME diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index 147120128260..0b0f8b8c4a26 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -10,6 +10,7 @@ config M68K select GENERIC_STRNCPY_FROM_USER if MMU select GENERIC_STRNLEN_USER if MMU select FPU if MMU + select ARCH_WANT_IPC_PARSE_VERSION select ARCH_USES_GETTIMEOFFSET if MMU && !COLDFIRE config RWSEM_GENERIC_SPINLOCK diff --git a/arch/m68k/include/asm/unistd.h b/arch/m68k/include/asm/unistd.h index ea0b502f845e..045cfd6a9e31 100644 --- a/arch/m68k/include/asm/unistd.h +++ b/arch/m68k/include/asm/unistd.h @@ -357,7 +357,6 @@ #define NR_syscalls 347 -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT #define __ARCH_WANT_STAT64 diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig index 0bf44231aaf9..ab9afcaa7f6a 100644 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig @@ -15,6 +15,7 @@ config MICROBLAZE select TRACING_SUPPORT select OF select OF_EARLY_FLATTREE + select ARCH_WANT_IPC_PARSE_VERSION select IRQ_DOMAIN select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_PROBE diff --git a/arch/microblaze/include/asm/unistd.h b/arch/microblaze/include/asm/unistd.h index d20ffbc86beb..6985e6e9d826 100644 --- a/arch/microblaze/include/asm/unistd.h +++ b/arch/microblaze/include/asm/unistd.h @@ -400,7 +400,6 @@ #ifdef __KERNEL__ #ifndef __ASSEMBLY__ -#define __ARCH_WANT_IPC_PARSE_VERSION /* #define __ARCH_WANT_OLD_READDIR */ /* #define __ARCH_WANT_OLD_STAT */ #define __ARCH_WANT_STAT64 diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 5e238d03960d..2d56cd5af336 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -27,6 +27,7 @@ config MIPS select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW select HAVE_ARCH_JUMP_LABEL + select ARCH_WANT_IPC_PARSE_VERSION select IRQ_FORCED_THREADING select HAVE_MEMBLOCK select HAVE_MEMBLOCK_NODE_MAP diff --git a/arch/mips/include/asm/unistd.h b/arch/mips/include/asm/unistd.h index d8dad5340ea3..bebbde01be92 100644 --- a/arch/mips/include/asm/unistd.h +++ b/arch/mips/include/asm/unistd.h @@ -1034,7 +1034,6 @@ #ifndef __ASSEMBLY__ #define __ARCH_OMIT_COMPAT_SYS_GETDENTS64 -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_SYS_ALARM #define __ARCH_WANT_SYS_GETHOSTNAME diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig index 687f9b4a2ed6..5cfb086b3903 100644 --- a/arch/mn10300/Kconfig +++ b/arch/mn10300/Kconfig @@ -3,6 +3,7 @@ config MN10300 select HAVE_OPROFILE select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_SHOW + select ARCH_WANT_IPC_PARSE_VERSION select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_KGDB select HAVE_NMI_WATCHDOG if MN10300_WD_TIMER diff --git a/arch/mn10300/include/asm/unistd.h b/arch/mn10300/include/asm/unistd.h index 9051f921cbc7..866eb14749d7 100644 --- a/arch/mn10300/include/asm/unistd.h +++ b/arch/mn10300/include/asm/unistd.h @@ -358,7 +358,6 @@ /* * specify the deprecated syscalls we want to support on this arch */ -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT #define __ARCH_WANT_STAT64 diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 4f681b78dd8b..352f416269ce 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -121,6 +121,7 @@ config PPC select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_HW_BREAKPOINT if PERF_EVENTS && PPC_BOOK3S_64 select HAVE_GENERIC_HARDIRQS + select ARCH_WANT_IPC_PARSE_VERSION select SPARSE_IRQ select IRQ_PER_CPU select IRQ_DOMAIN diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h index d3d1b5efd7eb..bd377a368611 100644 --- a/arch/powerpc/include/asm/unistd.h +++ b/arch/powerpc/include/asm/unistd.h @@ -389,7 +389,6 @@ #include #include -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_ALARM diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index d0a5e92b6b9e..296cd32466df 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -118,6 +118,7 @@ config S390 select ARCH_INLINE_WRITE_UNLOCK_BH select ARCH_INLINE_WRITE_UNLOCK_IRQ select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE + select ARCH_WANT_IPC_PARSE_VERSION select GENERIC_SMP_IDLE_THREAD select GENERIC_TIME_VSYSCALL select GENERIC_CLOCKEVENTS diff --git a/arch/s390/include/asm/unistd.h b/arch/s390/include/asm/unistd.h index 2e37157ba6a9..6756e78f4808 100644 --- a/arch/s390/include/asm/unistd.h +++ b/arch/s390/include/asm/unistd.h @@ -388,7 +388,6 @@ #define __IGNORE_recvmmsg #define __IGNORE_sendmmsg -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_SYS_ALARM #define __ARCH_WANT_SYS_GETHOSTNAME diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index a24595d83ad6..36f5141e8041 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -21,6 +21,7 @@ config SUPERH select HAVE_KERNEL_LZMA select HAVE_KERNEL_XZ select HAVE_KERNEL_LZO + select ARCH_WANT_IPC_PARSE_VERSION select HAVE_SYSCALL_TRACEPOINTS select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_GENERIC_HARDIRQS @@ -50,6 +51,7 @@ config SUPERH32 select HAVE_DYNAMIC_FTRACE select HAVE_FUNCTION_TRACE_MCOUNT_TEST select HAVE_FTRACE_NMI_ENTER if DYNAMIC_FTRACE + select ARCH_WANT_IPC_PARSE_VERSION select HAVE_FUNCTION_GRAPH_TRACER select HAVE_ARCH_KGDB select HAVE_HW_BREAKPOINT diff --git a/arch/sh/include/asm/unistd.h b/arch/sh/include/asm/unistd.h index e800a38c9f8d..7bc67076baac 100644 --- a/arch/sh/include/asm/unistd.h +++ b/arch/sh/include/asm/unistd.h @@ -6,7 +6,6 @@ # endif # define __ARCH_WANT_SYS_RT_SIGSUSPEND -# define __ARCH_WANT_IPC_PARSE_VERSION # define __ARCH_WANT_OLD_READDIR # define __ARCH_WANT_OLD_STAT # define __ARCH_WANT_STAT64 diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index e74ff1377626..67f1f6f5f4e1 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -27,6 +27,7 @@ config SPARC select HAVE_ARCH_JUMP_LABEL select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_SHOW + select ARCH_WANT_IPC_PARSE_VERSION select USE_GENERIC_SMP_HELPERS if SMP select GENERIC_PCI_IOMAP select HAVE_NMI_WATCHDOG if SPARC64 diff --git a/arch/sparc/include/asm/unistd.h b/arch/sparc/include/asm/unistd.h index c7cb0af0eb59..fb2693464807 100644 --- a/arch/sparc/include/asm/unistd.h +++ b/arch/sparc/include/asm/unistd.h @@ -423,7 +423,6 @@ #endif #ifdef __KERNEL__ -#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_ALARM diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index ca4fdefe79e6..ba2657c49217 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -85,6 +85,7 @@ config X86 select GENERIC_IOMAP select DCACHE_WORD_ACCESS select GENERIC_SMP_IDLE_THREAD + select ARCH_WANT_IPC_PARSE_VERSION if X86_32 select HAVE_ARCH_SECCOMP_FILTER select BUILDTIME_EXTABLE_SORT select GENERIC_CMOS_UPDATE diff --git a/arch/x86/include/asm/unistd.h b/arch/x86/include/asm/unistd.h index 4437001d8e3d..0d9776e9e2dc 100644 --- a/arch/x86/include/asm/unistd.h +++ b/arch/x86/include/asm/unistd.h @@ -15,7 +15,6 @@ # ifdef CONFIG_X86_32 # include -# define __ARCH_WANT_IPC_PARSE_VERSION # define __ARCH_WANT_STAT64 # define __ARCH_WANT_SYS_IPC # define __ARCH_WANT_SYS_OLD_MMAP diff --git a/include/linux/compat.h b/include/linux/compat.h index f2b8fe20cc8e..09b28b7369d7 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -256,7 +256,6 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr, compat_size_t __user *len_ptr); #ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC -#define __ARCH_WANT_COMPAT_IPC_PARSE_VERSION long compat_sys_semctl(int first, int second, int third, void __user *uptr); long compat_sys_msgsnd(int first, int second, int third, void __user *uptr); long compat_sys_msgrcv(int first, int second, int msgtyp, int third, diff --git a/ipc/compat.c b/ipc/compat.c index 20f92b2f2932..ad9518eb26e0 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -118,7 +118,7 @@ extern int sem_ctls[]; static inline int compat_ipc_parse_version(int *cmd) { -#ifdef __ARCH_WANT_COMPAT_IPC_PARSE_VERSION +#ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION int version = *cmd & IPC_64; /* this is tricky: architectures that have support for the old diff --git a/ipc/util.c b/ipc/util.c index 75261a31d48d..eb07fd356f27 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -804,7 +804,7 @@ out_up: return ERR_PTR(err); } -#ifdef __ARCH_WANT_IPC_PARSE_VERSION +#ifdef CONFIG_ARCH_WANT_IPC_PARSE_VERSION /** @@ -826,7 +826,7 @@ int ipc_parse_version (int *cmd) } } -#endif /* __ARCH_WANT_IPC_PARSE_VERSION */ +#endif /* CONFIG_ARCH_WANT_IPC_PARSE_VERSION */ #ifdef CONFIG_PROC_FS struct ipc_proc_iter { diff --git a/ipc/util.h b/ipc/util.h index 6f5c20bedaab..850ef3e962cb 100644 --- a/ipc/util.h +++ b/ipc/util.h @@ -130,7 +130,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, struct ipc_ids *ids, int id, int cmd, struct ipc64_perm *perm, int extra_perm); -#ifndef __ARCH_WANT_IPC_PARSE_VERSION +#ifndef CONFIG_ARCH_WANT_IPC_PARSE_VERSION /* On IA-64, we always use the "64-bit version" of the IPC structures. */ # define ipc_parse_version(cmd) IPC_64 #else -- cgit v1.2.3 From f7e1becb078c2b996420a61f2a411ef19335e2da Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 30 Jul 2012 14:42:56 -0700 Subject: include/linux/aio.h: cpp->C conversions Convert init_sync_kiocb() from a nasty macro into a nice C function. The struct assignment trick takes care of zeroing all unmentioned fields. Shrinks fs/read_write.o's .text from 9857 bytes to 9714. Also demacroize is_sync_kiocb() and aio_ring_avail(). The latter fixes an arg-referenced-multiple-times hand grenade. Cc: Junxiao Bi Cc: Mark Fasheh Acked-by: Jeff Moyer Cc: Joel Becker Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/aio.h | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/aio.h b/include/linux/aio.h index b1a520ec8b59..31ff6dba4872 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -126,22 +126,20 @@ struct kiocb { struct eventfd_ctx *ki_eventfd; }; -#define is_sync_kiocb(iocb) ((iocb)->ki_key == KIOCB_SYNC_KEY) -#define init_sync_kiocb(x, filp) \ - do { \ - struct task_struct *tsk = current; \ - (x)->ki_flags = 0; \ - (x)->ki_users = 1; \ - (x)->ki_key = KIOCB_SYNC_KEY; \ - (x)->ki_filp = (filp); \ - (x)->ki_ctx = NULL; \ - (x)->ki_cancel = NULL; \ - (x)->ki_retry = NULL; \ - (x)->ki_dtor = NULL; \ - (x)->ki_obj.tsk = tsk; \ - (x)->ki_user_data = 0; \ - (x)->private = NULL; \ - } while (0) +static inline bool is_sync_kiocb(struct kiocb *kiocb) +{ + return kiocb->ki_key == KIOCB_SYNC_KEY; +} + +static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp) +{ + *kiocb = (struct kiocb) { + .ki_users = 1, + .ki_key = KIOCB_SYNC_KEY, + .ki_filp = filp, + .ki_obj.tsk = current, + }; +} #define AIO_RING_MAGIC 0xa10a10a1 #define AIO_RING_COMPAT_FEATURES 1 @@ -161,8 +159,6 @@ struct aio_ring { struct io_event io_events[0]; }; /* 128 bytes + ring size */ -#define aio_ring_avail(info, ring) (((ring)->head + (info)->nr - 1 - (ring)->tail) % (info)->nr) - #define AIO_RING_PAGES 8 struct aio_ring_info { unsigned long mmap_base; @@ -177,6 +173,12 @@ struct aio_ring_info { struct page *internal_pages[AIO_RING_PAGES]; }; +static inline unsigned aio_ring_avail(struct aio_ring_info *info, + struct aio_ring *ring) +{ + return (ring->head + info->nr - 1 - ring->tail) % info->nr; +} + struct kioctx { atomic_t users; int dead; -- cgit v1.2.3 From 1d151c337d79fa3de88654d2514f58fbd916a8e0 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Mon, 30 Jul 2012 14:43:00 -0700 Subject: c/r: fcntl: add F_GETOWNER_UIDS option When we restore file descriptors we would like them to look exactly as they were at dumping time. With help of fcntl it's almost possible, the missing snippet is file owners UIDs. To be able to read their values the F_GETOWNER_UIDS is introduced. This option is valid iif CONFIG_CHECKPOINT_RESTORE is turned on, otherwise returning -EINVAL. Signed-off-by: Cyrill Gorcunov Acked-by: "Eric W. Biederman" Cc: "Serge E. Hallyn" Cc: Oleg Nesterov Cc: Pavel Emelyanov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fcntl.c | 29 +++++++++++++++++++++++++++++ include/asm-generic/fcntl.h | 4 ++++ security/selinux/hooks.c | 1 + 3 files changed, 34 insertions(+) (limited to 'include') diff --git a/fs/fcntl.c b/fs/fcntl.c index 81b70e665bf0..887b5ba8c9b5 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -340,6 +341,31 @@ static int f_getown_ex(struct file *filp, unsigned long arg) return ret; } +#ifdef CONFIG_CHECKPOINT_RESTORE +static int f_getowner_uids(struct file *filp, unsigned long arg) +{ + struct user_namespace *user_ns = current_user_ns(); + uid_t * __user dst = (void * __user)arg; + uid_t src[2]; + int err; + + read_lock(&filp->f_owner.lock); + src[0] = from_kuid(user_ns, filp->f_owner.uid); + src[1] = from_kuid(user_ns, filp->f_owner.euid); + read_unlock(&filp->f_owner.lock); + + err = put_user(src[0], &dst[0]); + err |= put_user(src[1], &dst[1]); + + return err; +} +#else +static int f_getowner_uids(struct file *filp, unsigned long arg) +{ + return -EINVAL; +} +#endif + static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, struct file *filp) { @@ -396,6 +422,9 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, case F_SETOWN_EX: err = f_setown_ex(filp, arg); break; + case F_GETOWNER_UIDS: + err = f_getowner_uids(filp, arg); + break; case F_GETSIG: err = filp->f_owner.signum; break; diff --git a/include/asm-generic/fcntl.h b/include/asm-generic/fcntl.h index 9e5b0356e2bb..a48937d4a5ea 100644 --- a/include/asm-generic/fcntl.h +++ b/include/asm-generic/fcntl.h @@ -120,6 +120,10 @@ #define F_GETOWN_EX 16 #endif +#ifndef F_GETOWNER_UIDS +#define F_GETOWNER_UIDS 17 +#endif + #define F_OWNER_TID 0 #define F_OWNER_PID 1 #define F_OWNER_PGRP 2 diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 94c45a1531a4..ec43760a8a03 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3180,6 +3180,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd, case F_GETFL: case F_GETOWN: case F_GETSIG: + case F_GETOWNER_UIDS: /* Just check FD__USE permission */ err = file_has_perm(cred, file, 0); break; -- cgit v1.2.3 From 3f6f49c7854c9294119437a82c5b35be78f9cea6 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Thu, 26 Jul 2012 20:08:54 -0400 Subject: ACPI: delete _GTS/_BFS support _GTS and _BFS were added to the suspend/resume flow in the ACPI 2.0 specification. Linux dutifully implemented _GTS and _BFS. We discovered that it was rarely seen in systems in the field. Further, some of those systems had AML so bogus that it could never work -- proof that no other operating system supports _GTS and _BFS. So we made _GTS and _BFS optional via modparam, and disabled them by default. But we've had to complicate some code to keep this support in the kernel, as these methods are defined to be evaluated very close to sleep entry and exit. Indeed, no other AML is ever evaluated with interrupts off. We have submitted a proposal for _GTS and _BFS to be officially removed from the ACPI specification on the next revision. Here we remove it from Linux. Signed-off-by: Len Brown Acked-by: Ingo Molnar Acked-by: Konrad Rzeszutek Wilk --- drivers/acpi/acpica/achware.h | 12 +++---- drivers/acpi/acpica/hwesleep.c | 19 ++--------- drivers/acpi/acpica/hwsleep.c | 20 ++--------- drivers/acpi/acpica/hwxfsleep.c | 22 ++++++------- drivers/acpi/sleep.c | 73 +++++------------------------------------ include/acpi/acpixf.h | 4 +-- include/acpi/actypes.h | 2 +- 7 files changed, 34 insertions(+), 118 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h index 5ccb99ae3a6f..5de4ec72766d 100644 --- a/drivers/acpi/acpica/achware.h +++ b/drivers/acpi/acpica/achware.h @@ -83,22 +83,22 @@ acpi_status acpi_hw_clear_acpi_status(void); /* * hwsleep - sleep/wake support (Legacy sleep registers) */ -acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags); +acpi_status acpi_hw_legacy_sleep(u8 sleep_state); -acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags); +acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state); -acpi_status acpi_hw_legacy_wake(u8 sleep_state, u8 flags); +acpi_status acpi_hw_legacy_wake(u8 sleep_state); /* * hwesleep - sleep/wake support (Extended FADT-V5 sleep registers) */ void acpi_hw_execute_sleep_method(char *method_name, u32 integer_argument); -acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags); +acpi_status acpi_hw_extended_sleep(u8 sleep_state); -acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags); +acpi_status acpi_hw_extended_wake_prep(u8 sleep_state); -acpi_status acpi_hw_extended_wake(u8 sleep_state, u8 flags); +acpi_status acpi_hw_extended_wake(u8 sleep_state); /* * hwvalid - Port I/O with validation diff --git a/drivers/acpi/acpica/hwesleep.c b/drivers/acpi/acpica/hwesleep.c index 48518dac5342..94996f9ae3ad 100644 --- a/drivers/acpi/acpica/hwesleep.c +++ b/drivers/acpi/acpica/hwesleep.c @@ -90,7 +90,6 @@ void acpi_hw_execute_sleep_method(char *method_pathname, u32 integer_argument) * FUNCTION: acpi_hw_extended_sleep * * PARAMETERS: sleep_state - Which sleep state to enter - * flags - ACPI_EXECUTE_GTS to run optional method * * RETURN: Status * @@ -100,7 +99,7 @@ void acpi_hw_execute_sleep_method(char *method_pathname, u32 integer_argument) * ******************************************************************************/ -acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags) +acpi_status acpi_hw_extended_sleep(u8 sleep_state) { acpi_status status; u8 sleep_type_value; @@ -125,12 +124,6 @@ acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags) acpi_gbl_system_awake_and_running = FALSE; - /* Optionally execute _GTS (Going To Sleep) */ - - if (flags & ACPI_EXECUTE_GTS) { - acpi_hw_execute_sleep_method(METHOD_PATHNAME__GTS, sleep_state); - } - /* Flush caches, as per ACPI specification */ ACPI_FLUSH_CPU_CACHE(); @@ -172,7 +165,6 @@ acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags) * FUNCTION: acpi_hw_extended_wake_prep * * PARAMETERS: sleep_state - Which sleep state we just exited - * flags - ACPI_EXECUTE_BFS to run optional method * * RETURN: Status * @@ -181,7 +173,7 @@ acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags) * ******************************************************************************/ -acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags) +acpi_status acpi_hw_extended_wake_prep(u8 sleep_state) { acpi_status status; u8 sleep_type_value; @@ -200,11 +192,6 @@ acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags) &acpi_gbl_FADT.sleep_control); } - /* Optionally execute _BFS (Back From Sleep) */ - - if (flags & ACPI_EXECUTE_BFS) { - acpi_hw_execute_sleep_method(METHOD_PATHNAME__BFS, sleep_state); - } return_ACPI_STATUS(AE_OK); } @@ -222,7 +209,7 @@ acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags) * ******************************************************************************/ -acpi_status acpi_hw_extended_wake(u8 sleep_state, u8 flags) +acpi_status acpi_hw_extended_wake(u8 sleep_state) { ACPI_FUNCTION_TRACE(hw_extended_wake); diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c index 9960fe9ef533..3fddde056a5e 100644 --- a/drivers/acpi/acpica/hwsleep.c +++ b/drivers/acpi/acpica/hwsleep.c @@ -56,7 +56,6 @@ ACPI_MODULE_NAME("hwsleep") * FUNCTION: acpi_hw_legacy_sleep * * PARAMETERS: sleep_state - Which sleep state to enter - * flags - ACPI_EXECUTE_GTS to run optional method * * RETURN: Status * @@ -64,7 +63,7 @@ ACPI_MODULE_NAME("hwsleep") * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED * ******************************************************************************/ -acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags) +acpi_status acpi_hw_legacy_sleep(u8 sleep_state) { struct acpi_bit_register_info *sleep_type_reg_info; struct acpi_bit_register_info *sleep_enable_reg_info; @@ -110,12 +109,6 @@ acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags) return_ACPI_STATUS(status); } - /* Optionally execute _GTS (Going To Sleep) */ - - if (flags & ACPI_EXECUTE_GTS) { - acpi_hw_execute_sleep_method(METHOD_PATHNAME__GTS, sleep_state); - } - /* Get current value of PM1A control */ status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL, @@ -214,7 +207,6 @@ acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags) * FUNCTION: acpi_hw_legacy_wake_prep * * PARAMETERS: sleep_state - Which sleep state we just exited - * flags - ACPI_EXECUTE_BFS to run optional method * * RETURN: Status * @@ -224,7 +216,7 @@ acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags) * ******************************************************************************/ -acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags) +acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state) { acpi_status status; struct acpi_bit_register_info *sleep_type_reg_info; @@ -275,11 +267,6 @@ acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags) } } - /* Optionally execute _BFS (Back From Sleep) */ - - if (flags & ACPI_EXECUTE_BFS) { - acpi_hw_execute_sleep_method(METHOD_PATHNAME__BFS, sleep_state); - } return_ACPI_STATUS(status); } @@ -288,7 +275,6 @@ acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags) * FUNCTION: acpi_hw_legacy_wake * * PARAMETERS: sleep_state - Which sleep state we just exited - * flags - Reserved, set to zero * * RETURN: Status * @@ -297,7 +283,7 @@ acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags) * ******************************************************************************/ -acpi_status acpi_hw_legacy_wake(u8 sleep_state, u8 flags) +acpi_status acpi_hw_legacy_wake(u8 sleep_state) { acpi_status status; diff --git a/drivers/acpi/acpica/hwxfsleep.c b/drivers/acpi/acpica/hwxfsleep.c index f8684bfe7907..1f165a750ae2 100644 --- a/drivers/acpi/acpica/hwxfsleep.c +++ b/drivers/acpi/acpica/hwxfsleep.c @@ -50,7 +50,7 @@ ACPI_MODULE_NAME("hwxfsleep") /* Local prototypes */ static acpi_status -acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id); +acpi_hw_sleep_dispatch(u8 sleep_state, u32 function_id); /* * Dispatch table used to efficiently branch to the various sleep @@ -235,7 +235,7 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_s4bios) * ******************************************************************************/ static acpi_status -acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id) +acpi_hw_sleep_dispatch(u8 sleep_state, u32 function_id) { acpi_status status; struct acpi_sleep_functions *sleep_functions = @@ -248,11 +248,11 @@ acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id) * use the extended sleep registers */ if (acpi_gbl_reduced_hardware || acpi_gbl_FADT.sleep_control.address) { - status = sleep_functions->extended_function(sleep_state, flags); + status = sleep_functions->extended_function(sleep_state); } else { /* Legacy sleep */ - status = sleep_functions->legacy_function(sleep_state, flags); + status = sleep_functions->legacy_function(sleep_state); } return (status); @@ -262,7 +262,7 @@ acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id) * For the case where reduced-hardware-only code is being generated, * we know that only the extended sleep registers are available */ - status = sleep_functions->extended_function(sleep_state, flags); + status = sleep_functions->extended_function(sleep_state); return (status); #endif /* !ACPI_REDUCED_HARDWARE */ @@ -349,7 +349,6 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_prep) * FUNCTION: acpi_enter_sleep_state * * PARAMETERS: sleep_state - Which sleep state to enter - * flags - ACPI_EXECUTE_GTS to run optional method * * RETURN: Status * @@ -357,7 +356,7 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_prep) * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED * ******************************************************************************/ -acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state, u8 flags) +acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state) { acpi_status status; @@ -371,7 +370,7 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state, u8 flags) } status = - acpi_hw_sleep_dispatch(sleep_state, flags, ACPI_SLEEP_FUNCTION_ID); + acpi_hw_sleep_dispatch(sleep_state, ACPI_SLEEP_FUNCTION_ID); return_ACPI_STATUS(status); } @@ -391,14 +390,14 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state) * Called with interrupts DISABLED. * ******************************************************************************/ -acpi_status acpi_leave_sleep_state_prep(u8 sleep_state, u8 flags) +acpi_status acpi_leave_sleep_state_prep(u8 sleep_state) { acpi_status status; ACPI_FUNCTION_TRACE(acpi_leave_sleep_state_prep); status = - acpi_hw_sleep_dispatch(sleep_state, flags, + acpi_hw_sleep_dispatch(sleep_state, ACPI_WAKE_PREP_FUNCTION_ID); return_ACPI_STATUS(status); } @@ -423,8 +422,7 @@ acpi_status acpi_leave_sleep_state(u8 sleep_state) ACPI_FUNCTION_TRACE(acpi_leave_sleep_state); - - status = acpi_hw_sleep_dispatch(sleep_state, 0, ACPI_WAKE_FUNCTION_ID); + status = acpi_hw_sleep_dispatch(sleep_state, ACPI_WAKE_FUNCTION_ID); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 23a53c013f1e..bd35e3c5e530 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -28,34 +28,6 @@ #include "internal.h" #include "sleep.h" -u8 wake_sleep_flags = ACPI_NO_OPTIONAL_METHODS; -static unsigned int gts, bfs; -static int set_param_wake_flag(const char *val, struct kernel_param *kp) -{ - int ret = param_set_int(val, kp); - - if (ret) - return ret; - - if (kp->arg == (const char *)>s) { - if (gts) - wake_sleep_flags |= ACPI_EXECUTE_GTS; - else - wake_sleep_flags &= ~ACPI_EXECUTE_GTS; - } - if (kp->arg == (const char *)&bfs) { - if (bfs) - wake_sleep_flags |= ACPI_EXECUTE_BFS; - else - wake_sleep_flags &= ~ACPI_EXECUTE_BFS; - } - return ret; -} -module_param_call(gts, set_param_wake_flag, param_get_int, >s, 0644); -module_param_call(bfs, set_param_wake_flag, param_get_int, &bfs, 0644); -MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend."); -MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".); - static u8 sleep_states[ACPI_S_STATE_COUNT]; static bool pwr_btn_event_pending; @@ -305,7 +277,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state) switch (acpi_state) { case ACPI_STATE_S1: barrier(); - status = acpi_enter_sleep_state(acpi_state, wake_sleep_flags); + status = acpi_enter_sleep_state(acpi_state); break; case ACPI_STATE_S3: @@ -319,8 +291,8 @@ static int acpi_suspend_enter(suspend_state_t pm_state) /* This violates the spec but is required for bug compatibility. */ acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1); - /* Reprogram control registers and execute _BFS */ - acpi_leave_sleep_state_prep(acpi_state, wake_sleep_flags); + /* Reprogram control registers */ + acpi_leave_sleep_state_prep(acpi_state); /* ACPI 3.0 specs (P62) says that it's the responsibility * of the OSPM to clear the status bit [ implying that the @@ -603,9 +575,9 @@ static int acpi_hibernation_enter(void) ACPI_FLUSH_CPU_CACHE(); /* This shouldn't return. If it returns, we have a problem */ - status = acpi_enter_sleep_state(ACPI_STATE_S4, wake_sleep_flags); - /* Reprogram control registers and execute _BFS */ - acpi_leave_sleep_state_prep(ACPI_STATE_S4, wake_sleep_flags); + status = acpi_enter_sleep_state(ACPI_STATE_S4); + /* Reprogram control registers */ + acpi_leave_sleep_state_prep(ACPI_STATE_S4); return ACPI_SUCCESS(status) ? 0 : -EFAULT; } @@ -617,8 +589,8 @@ static void acpi_hibernation_leave(void) * enable it here. */ acpi_enable(); - /* Reprogram control registers and execute _BFS */ - acpi_leave_sleep_state_prep(ACPI_STATE_S4, wake_sleep_flags); + /* Reprogram control registers */ + acpi_leave_sleep_state_prep(ACPI_STATE_S4); /* Check the hardware signature */ if (facs && s4_hardware_signature != facs->hardware_signature) { printk(KERN_EMERG "ACPI: Hardware changed while hibernated, " @@ -876,33 +848,7 @@ static void acpi_power_off(void) /* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */ printk(KERN_DEBUG "%s called\n", __func__); local_irq_disable(); - acpi_enter_sleep_state(ACPI_STATE_S5, wake_sleep_flags); -} - -/* - * ACPI 2.0 created the optional _GTS and _BFS, - * but industry adoption has been neither rapid nor broad. - * - * Linux gets into trouble when it executes poorly validated - * paths through the BIOS, so disable _GTS and _BFS by default, - * but do speak up and offer the option to enable them. - */ -static void __init acpi_gts_bfs_check(void) -{ - acpi_handle dummy; - - if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_PATHNAME__GTS, &dummy))) - { - printk(KERN_NOTICE PREFIX "BIOS offers _GTS\n"); - printk(KERN_NOTICE PREFIX "If \"acpi.gts=1\" improves suspend, " - "please notify linux-acpi@vger.kernel.org\n"); - } - if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_PATHNAME__BFS, &dummy))) - { - printk(KERN_NOTICE PREFIX "BIOS offers _BFS\n"); - printk(KERN_NOTICE PREFIX "If \"acpi.bfs=1\" improves resume, " - "please notify linux-acpi@vger.kernel.org\n"); - } + acpi_enter_sleep_state(ACPI_STATE_S5); } int __init acpi_sleep_init(void) @@ -963,6 +909,5 @@ int __init acpi_sleep_init(void) * object can also be evaluated when the system enters S5. */ register_reboot_notifier(&tts_notifier); - acpi_gts_bfs_check(); return 0; } diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 2c744c7a5b3d..26a92fc28a59 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -491,11 +491,11 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 * slp_typ_a, u8 * slp_typ_b); acpi_status acpi_enter_sleep_state_prep(u8 sleep_state); -acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state, u8 flags); +acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state); ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status asmlinkage acpi_enter_sleep_state_s4bios(void)) -acpi_status acpi_leave_sleep_state_prep(u8 sleep_state, u8 flags); +acpi_status acpi_leave_sleep_state_prep(u8 sleep_state); acpi_status acpi_leave_sleep_state(u8 sleep_state); diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 3af87de6a68c..3d00bd5bd7e3 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -803,7 +803,7 @@ typedef u8 acpi_adr_space_type; /* Sleep function dispatch */ -typedef acpi_status(*ACPI_SLEEP_FUNCTION) (u8 sleep_state, u8 flags); +typedef acpi_status(*ACPI_SLEEP_FUNCTION) (u8 sleep_state); struct acpi_sleep_functions { ACPI_SLEEP_FUNCTION legacy_function; -- cgit v1.2.3 From 546f04ef716dd49521774653d8b032a7d64c05d9 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 30 Jul 2012 18:15:23 -0700 Subject: libceph: support crush tunables The server side recently added support for tuning some magic crush variables. Decode these variables if they are present, or use the default values if they are not present. Corresponds to ceph.git commit 89af369c25f274fe62ef730e5e8aad0c54f1e5a5. Signed-off-by: caleb miles Reviewed-by: Sage Weil Reviewed-by: Alex Elder Reviewed-by: Yehuda Sadeh --- include/linux/ceph/ceph_features.h | 5 ++++- include/linux/crush/crush.h | 8 ++++++++ net/ceph/crush/mapper.c | 13 +++++++------ net/ceph/osdmap.c | 39 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/ceph_features.h b/include/linux/ceph/ceph_features.h index 342f93dbe162..dad579b0c0e6 100644 --- a/include/linux/ceph/ceph_features.h +++ b/include/linux/ceph/ceph_features.h @@ -12,12 +12,15 @@ #define CEPH_FEATURE_MONNAMES (1<<5) #define CEPH_FEATURE_RECONNECT_SEQ (1<<6) #define CEPH_FEATURE_DIRLAYOUTHASH (1<<7) +/* bits 8-17 defined by user-space; not supported yet here */ +#define CEPH_FEATURE_CRUSH_TUNABLES (1<<18) /* * Features supported. */ #define CEPH_FEATURES_SUPPORTED_DEFAULT \ - (CEPH_FEATURE_NOSRCADDR) + (CEPH_FEATURE_NOSRCADDR | \ + CEPH_FEATURE_CRUSH_TUNABLES) #define CEPH_FEATURES_REQUIRED_DEFAULT \ (CEPH_FEATURE_NOSRCADDR) diff --git a/include/linux/crush/crush.h b/include/linux/crush/crush.h index 7c4750811b96..25baa287cff7 100644 --- a/include/linux/crush/crush.h +++ b/include/linux/crush/crush.h @@ -154,6 +154,14 @@ struct crush_map { __s32 max_buckets; __u32 max_rules; __s32 max_devices; + + /* choose local retries before re-descent */ + __u32 choose_local_tries; + /* choose local attempts using a fallback permutation before + * re-descent */ + __u32 choose_local_fallback_tries; + /* choose attempts before giving up */ + __u32 choose_total_tries; }; diff --git a/net/ceph/crush/mapper.c b/net/ceph/crush/mapper.c index d7edc24333b8..35fce755ce10 100644 --- a/net/ceph/crush/mapper.c +++ b/net/ceph/crush/mapper.c @@ -306,7 +306,6 @@ static int crush_choose(const struct crush_map *map, int item = 0; int itemtype; int collide, reject; - const unsigned int orig_tries = 5; /* attempts before we fall back to search */ dprintk("CHOOSE%s bucket %d x %d outpos %d numrep %d\n", recurse_to_leaf ? "_LEAF" : "", bucket->id, x, outpos, numrep); @@ -351,8 +350,9 @@ static int crush_choose(const struct crush_map *map, reject = 1; goto reject; } - if (flocal >= (in->size>>1) && - flocal > orig_tries) + if (map->choose_local_fallback_tries > 0 && + flocal >= (in->size>>1) && + flocal > map->choose_local_fallback_tries) item = bucket_perm_choose(in, x, r); else item = crush_bucket_choose(in, x, r); @@ -422,13 +422,14 @@ reject: ftotal++; flocal++; - if (collide && flocal < 3) + if (collide && flocal <= map->choose_local_tries) /* retry locally a few times */ retry_bucket = 1; - else if (flocal <= in->size + orig_tries) + else if (map->choose_local_fallback_tries > 0 && + flocal <= in->size + map->choose_local_fallback_tries) /* exhaustive bucket search */ retry_bucket = 1; - else if (ftotal < 20) + else if (ftotal <= map->choose_total_tries) /* then retry descent */ retry_descent = 1; else diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index 9600674c2c39..3124b71a8883 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -135,6 +135,21 @@ bad: return -EINVAL; } +static int skip_name_map(void **p, void *end) +{ + int len; + ceph_decode_32_safe(p, end, len ,bad); + while (len--) { + int strlen; + *p += sizeof(u32); + ceph_decode_32_safe(p, end, strlen, bad); + *p += strlen; +} + return 0; +bad: + return -EINVAL; +} + static struct crush_map *crush_decode(void *pbyval, void *end) { struct crush_map *c; @@ -143,6 +158,7 @@ static struct crush_map *crush_decode(void *pbyval, void *end) void **p = &pbyval; void *start = pbyval; u32 magic; + u32 num_name_maps; dout("crush_decode %p to %p len %d\n", *p, end, (int)(end - *p)); @@ -150,6 +166,11 @@ static struct crush_map *crush_decode(void *pbyval, void *end) if (c == NULL) return ERR_PTR(-ENOMEM); + /* set tunables to default values */ + c->choose_local_tries = 2; + c->choose_local_fallback_tries = 5; + c->choose_total_tries = 19; + ceph_decode_need(p, end, 4*sizeof(u32), bad); magic = ceph_decode_32(p); if (magic != CRUSH_MAGIC) { @@ -297,7 +318,25 @@ static struct crush_map *crush_decode(void *pbyval, void *end) } /* ignore trailing name maps. */ + for (num_name_maps = 0; num_name_maps < 3; num_name_maps++) { + err = skip_name_map(p, end); + if (err < 0) + goto done; + } + + /* tunables */ + ceph_decode_need(p, end, 3*sizeof(u32), done); + c->choose_local_tries = ceph_decode_32(p); + c->choose_local_fallback_tries = ceph_decode_32(p); + c->choose_total_tries = ceph_decode_32(p); + dout("crush decode tunable choose_local_tries = %d", + c->choose_local_tries); + dout("crush decode tunable choose_local_fallback_tries = %d", + c->choose_local_fallback_tries); + dout("crush decode tunable choose_total_tries = %d", + c->choose_total_tries); +done: dout("crush_decode success\n"); return c; -- cgit v1.2.3 From aa711ee3402ad10ffd5b70ce0417fadc9a95cccf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 13 Jul 2012 20:35:11 -0500 Subject: ceph: define snap counts as u32 everywhere There are two structures in which a count of snapshots are maintained: struct ceph_snap_context { ... u32 num_snaps; ... } and struct ceph_snap_realm { ... u32 num_prior_parent_snaps; /* had prior to parent_since */ ... u32 num_snaps; ... } These fields never take on negative values (e.g., to hold special meaning), and so are really inherently unsigned. Furthermore they take their value from over-the-wire or on-disk formatted 32-bit values. So change their definition to have type u32, and change some spots elsewhere in the code to account for this change. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- fs/ceph/snap.c | 18 ++++++++++-------- fs/ceph/super.h | 4 ++-- include/linux/ceph/libceph.h | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index e5206fc76562..cbb2f54a3019 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c @@ -296,8 +296,7 @@ static int build_snap_context(struct ceph_snap_realm *realm) struct ceph_snap_realm *parent = realm->parent; struct ceph_snap_context *snapc; int err = 0; - int i; - int num = realm->num_prior_parent_snaps + realm->num_snaps; + u32 num = realm->num_prior_parent_snaps + realm->num_snaps; /* * build parent context, if it hasn't been built. @@ -321,11 +320,11 @@ static int build_snap_context(struct ceph_snap_realm *realm) realm->cached_context->seq == realm->seq && (!parent || realm->cached_context->seq >= parent->cached_context->seq)) { - dout("build_snap_context %llx %p: %p seq %lld (%d snaps)" + dout("build_snap_context %llx %p: %p seq %lld (%u snaps)" " (unchanged)\n", realm->ino, realm, realm->cached_context, realm->cached_context->seq, - realm->cached_context->num_snaps); + (unsigned int) realm->cached_context->num_snaps); return 0; } @@ -342,6 +341,8 @@ static int build_snap_context(struct ceph_snap_realm *realm) num = 0; snapc->seq = realm->seq; if (parent) { + u32 i; + /* include any of parent's snaps occurring _after_ my parent became my parent */ for (i = 0; i < parent->cached_context->num_snaps; i++) @@ -361,8 +362,9 @@ static int build_snap_context(struct ceph_snap_realm *realm) sort(snapc->snaps, num, sizeof(u64), cmpu64_rev, NULL); snapc->num_snaps = num; - dout("build_snap_context %llx %p: %p seq %lld (%d snaps)\n", - realm->ino, realm, snapc, snapc->seq, snapc->num_snaps); + dout("build_snap_context %llx %p: %p seq %lld (%u snaps)\n", + realm->ino, realm, snapc, snapc->seq, + (unsigned int) snapc->num_snaps); if (realm->cached_context) ceph_put_snap_context(realm->cached_context); @@ -402,9 +404,9 @@ static void rebuild_snap_realms(struct ceph_snap_realm *realm) * helper to allocate and decode an array of snapids. free prior * instance, if any. */ -static int dup_array(u64 **dst, __le64 *src, int num) +static int dup_array(u64 **dst, __le64 *src, u32 num) { - int i; + u32 i; kfree(*dst); if (num) { diff --git a/fs/ceph/super.h b/fs/ceph/super.h index fc35036d258d..3ea48b7b98b3 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -612,9 +612,9 @@ struct ceph_snap_realm { u64 parent_since; /* snapid when our current parent became so */ u64 *prior_parent_snaps; /* snaps inherited from any parents we */ - int num_prior_parent_snaps; /* had prior to parent_since */ + u32 num_prior_parent_snaps; /* had prior to parent_since */ u64 *snaps; /* snaps specific to this realm */ - int num_snaps; + u32 num_snaps; struct ceph_snap_realm *parent; struct list_head children; /* list of child realms */ diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h index ea072e1f9db9..42624789b06f 100644 --- a/include/linux/ceph/libceph.h +++ b/include/linux/ceph/libceph.h @@ -154,7 +154,7 @@ struct ceph_client { struct ceph_snap_context { atomic_t nref; u64 seq; - int num_snaps; + u32 num_snaps; u64 snaps[]; }; -- cgit v1.2.3 From 8dacc7da69a491c515851e68de6036f21b5663ce Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 20 Jul 2012 17:24:40 -0700 Subject: libceph: replace connection state bits with states Use a simple set of 6 enumerated values for the socket states (CON_STATE_*) and use those instead of the state bits. All of the con->state checks are now under the protection of the con mutex, so this is safe. It also simplifies many of the state checks because we can check for anything other than the expected state instead of various bits for races we can think of. This appears to hold up well to stress testing both with and without socket failure injection on the server side. Signed-off-by: Sage Weil --- include/linux/ceph/messenger.h | 12 ---- net/ceph/messenger.c | 130 +++++++++++++++++++++-------------------- 2 files changed, 68 insertions(+), 74 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index a310d7fe6e29..d9c2b8f5abde 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -116,18 +116,6 @@ struct ceph_msg_pos { #define SOCK_CLOSED 11 /* socket state changed to closed */ #define BACKOFF 15 -/* - * ceph_connection states - */ -#define CONNECTING 1 -#define NEGOTIATING 2 -#define CONNECTED 5 -#define STANDBY 8 /* no outgoing messages, socket closed. we keep - * the ceph_connection around to maintain shared - * state with the peer. */ -#define CLOSED 10 /* we've closed the connection */ -#define OPENING 13 /* open connection w/ (possibly new) peer */ - /* * A single connection with another host. * diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index e7320cd5fdbc..563e46aa4d6d 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -77,6 +77,17 @@ #define CON_SOCK_STATE_CONNECTED 3 /* -> CLOSING or -> CLOSED */ #define CON_SOCK_STATE_CLOSING 4 /* -> CLOSED */ +/* + * connection states + */ +#define CON_STATE_CLOSED 1 /* -> PREOPEN */ +#define CON_STATE_PREOPEN 2 /* -> CONNECTING, CLOSED */ +#define CON_STATE_CONNECTING 3 /* -> NEGOTIATING, CLOSED */ +#define CON_STATE_NEGOTIATING 4 /* -> OPEN, CLOSED */ +#define CON_STATE_OPEN 5 /* -> STANDBY, CLOSED */ +#define CON_STATE_STANDBY 6 /* -> PREOPEN, CLOSED */ + + /* static tag bytes (protocol control messages) */ static char tag_msg = CEPH_MSGR_TAG_MSG; static char tag_ack = CEPH_MSGR_TAG_ACK; @@ -503,11 +514,7 @@ void ceph_con_close(struct ceph_connection *con) mutex_lock(&con->mutex); dout("con_close %p peer %s\n", con, ceph_pr_addr(&con->peer_addr.in_addr)); - clear_bit(NEGOTIATING, &con->state); - clear_bit(CONNECTING, &con->state); - clear_bit(CONNECTED, &con->state); - clear_bit(STANDBY, &con->state); /* avoid connect_seq bump */ - set_bit(CLOSED, &con->state); + con->state = CON_STATE_CLOSED; clear_bit(LOSSYTX, &con->flags); /* so we retry next connect */ clear_bit(KEEPALIVE_PENDING, &con->flags); @@ -530,8 +537,9 @@ void ceph_con_open(struct ceph_connection *con, { mutex_lock(&con->mutex); dout("con_open %p %s\n", con, ceph_pr_addr(&addr->in_addr)); - set_bit(OPENING, &con->state); - WARN_ON(!test_and_clear_bit(CLOSED, &con->state)); + + BUG_ON(con->state != CON_STATE_CLOSED); + con->state = CON_STATE_PREOPEN; con->peer_name.type = (__u8) entity_type; con->peer_name.num = cpu_to_le64(entity_num); @@ -571,7 +579,7 @@ void ceph_con_init(struct ceph_connection *con, void *private, INIT_LIST_HEAD(&con->out_sent); INIT_DELAYED_WORK(&con->work, con_work); - set_bit(CLOSED, &con->state); + con->state = CON_STATE_CLOSED; } EXPORT_SYMBOL(ceph_con_init); @@ -809,27 +817,21 @@ static struct ceph_auth_handshake *get_connect_authorizer(struct ceph_connection if (!con->ops->get_authorizer) { con->out_connect.authorizer_protocol = CEPH_AUTH_UNKNOWN; con->out_connect.authorizer_len = 0; - return NULL; } /* Can't hold the mutex while getting authorizer */ - mutex_unlock(&con->mutex); - auth = con->ops->get_authorizer(con, auth_proto, con->auth_retry); - mutex_lock(&con->mutex); if (IS_ERR(auth)) return auth; - if (test_bit(CLOSED, &con->state) || test_bit(OPENING, &con->flags)) + if (con->state != CON_STATE_NEGOTIATING) return ERR_PTR(-EAGAIN); con->auth_reply_buf = auth->authorizer_reply_buf; con->auth_reply_buf_len = auth->authorizer_reply_buf_len; - - return auth; } @@ -1484,7 +1486,8 @@ static int process_banner(struct ceph_connection *con) static void fail_protocol(struct ceph_connection *con) { reset_connection(con); - set_bit(CLOSED, &con->state); /* in case there's queued work */ + BUG_ON(con->state != CON_STATE_NEGOTIATING); + con->state = CON_STATE_CLOSED; } static int process_connect(struct ceph_connection *con) @@ -1558,8 +1561,7 @@ static int process_connect(struct ceph_connection *con) if (con->ops->peer_reset) con->ops->peer_reset(con); mutex_lock(&con->mutex); - if (test_bit(CLOSED, &con->state) || - test_bit(OPENING, &con->state)) + if (con->state != CON_STATE_NEGOTIATING) return -EAGAIN; break; @@ -1605,8 +1607,10 @@ static int process_connect(struct ceph_connection *con) fail_protocol(con); return -1; } - clear_bit(NEGOTIATING, &con->state); - set_bit(CONNECTED, &con->state); + + BUG_ON(con->state != CON_STATE_NEGOTIATING); + con->state = CON_STATE_OPEN; + con->peer_global_seq = le32_to_cpu(con->in_reply.global_seq); con->connect_seq++; con->peer_features = server_feat; @@ -1994,8 +1998,9 @@ more: dout("try_write out_kvec_bytes %d\n", con->out_kvec_bytes); /* open the socket first? */ - if (con->sock == NULL) { - set_bit(CONNECTING, &con->state); + if (con->state == CON_STATE_PREOPEN) { + BUG_ON(con->sock); + con->state = CON_STATE_CONNECTING; con_out_kvec_reset(con); prepare_write_banner(con); @@ -2046,8 +2051,7 @@ more_kvec: } do_next: - if (!test_bit(CONNECTING, &con->state) && - !test_bit(NEGOTIATING, &con->state)) { + if (con->state == CON_STATE_OPEN) { /* is anything else pending? */ if (!list_empty(&con->out_queue)) { prepare_write_message(con); @@ -2081,29 +2085,19 @@ static int try_read(struct ceph_connection *con) { int ret = -1; - if (!con->sock) - return 0; - - if (test_bit(STANDBY, &con->state)) +more: + dout("try_read start on %p state %lu\n", con, con->state); + if (con->state != CON_STATE_CONNECTING && + con->state != CON_STATE_NEGOTIATING && + con->state != CON_STATE_OPEN) return 0; - dout("try_read start on %p\n", con); + BUG_ON(!con->sock); -more: dout("try_read tag %d in_base_pos %d\n", (int)con->in_tag, con->in_base_pos); - /* - * process_connect and process_message drop and re-take - * con->mutex. make sure we handle a racing close or reopen. - */ - if (test_bit(CLOSED, &con->state) || - test_bit(OPENING, &con->state)) { - ret = -EAGAIN; - goto out; - } - - if (test_bit(CONNECTING, &con->state)) { + if (con->state == CON_STATE_CONNECTING) { dout("try_read connecting\n"); ret = read_partial_banner(con); if (ret <= 0) @@ -2112,8 +2106,8 @@ more: if (ret < 0) goto out; - clear_bit(CONNECTING, &con->state); - set_bit(NEGOTIATING, &con->state); + BUG_ON(con->state != CON_STATE_CONNECTING); + con->state = CON_STATE_NEGOTIATING; /* Banner is good, exchange connection info */ ret = prepare_write_connect(con); @@ -2125,7 +2119,7 @@ more: goto out; } - if (test_bit(NEGOTIATING, &con->state)) { + if (con->state == CON_STATE_NEGOTIATING) { dout("try_read negotiating\n"); ret = read_partial_connect(con); if (ret <= 0) @@ -2136,6 +2130,8 @@ more: goto more; } + BUG_ON(con->state != CON_STATE_OPEN); + if (con->in_base_pos < 0) { /* * skipping + discarding content. @@ -2169,8 +2165,8 @@ more: prepare_read_ack(con); break; case CEPH_MSGR_TAG_CLOSE: - clear_bit(CONNECTED, &con->state); - set_bit(CLOSED, &con->state); /* fixme */ + con_close_socket(con); + con->state = CON_STATE_CLOSED; goto out; default: goto bad_tag; @@ -2246,14 +2242,21 @@ static void con_work(struct work_struct *work) mutex_lock(&con->mutex); restart: if (test_and_clear_bit(SOCK_CLOSED, &con->flags)) { - if (test_and_clear_bit(CONNECTED, &con->state)) - con->error_msg = "socket closed"; - else if (test_and_clear_bit(NEGOTIATING, &con->state)) - con->error_msg = "negotiation failed"; - else if (test_and_clear_bit(CONNECTING, &con->state)) + switch (con->state) { + case CON_STATE_CONNECTING: con->error_msg = "connection failed"; - else + break; + case CON_STATE_NEGOTIATING: + con->error_msg = "negotiation failed"; + break; + case CON_STATE_OPEN: + con->error_msg = "socket closed"; + break; + default: + dout("unrecognized con state %d\n", (int)con->state); con->error_msg = "unrecognized con state"; + BUG(); + } goto fault; } @@ -2271,17 +2274,16 @@ restart: } } - if (test_bit(STANDBY, &con->state)) { + if (con->state == CON_STATE_STANDBY) { dout("con_work %p STANDBY\n", con); goto done; } - if (test_bit(CLOSED, &con->state)) { + if (con->state == CON_STATE_CLOSED) { dout("con_work %p CLOSED\n", con); BUG_ON(con->sock); goto done; } - if (test_and_clear_bit(OPENING, &con->state)) { - /* reopen w/ new peer */ + if (con->state == CON_STATE_PREOPEN) { dout("con_work OPENING\n"); BUG_ON(con->sock); } @@ -2328,13 +2330,15 @@ static void ceph_fault(struct ceph_connection *con) dout("fault %p state %lu to peer %s\n", con, con->state, ceph_pr_addr(&con->peer_addr.in_addr)); - if (test_bit(CLOSED, &con->state)) - goto out_unlock; + BUG_ON(con->state != CON_STATE_CONNECTING && + con->state != CON_STATE_NEGOTIATING && + con->state != CON_STATE_OPEN); con_close_socket(con); if (test_bit(LOSSYTX, &con->flags)) { - dout("fault on LOSSYTX channel\n"); + dout("fault on LOSSYTX channel, marking CLOSED\n"); + con->state = CON_STATE_CLOSED; goto out_unlock; } @@ -2355,9 +2359,10 @@ static void ceph_fault(struct ceph_connection *con) !test_bit(KEEPALIVE_PENDING, &con->flags)) { dout("fault %p setting STANDBY clearing WRITE_PENDING\n", con); clear_bit(WRITE_PENDING, &con->flags); - set_bit(STANDBY, &con->state); + con->state = CON_STATE_STANDBY; } else { /* retry after a delay. */ + con->state = CON_STATE_PREOPEN; if (con->delay == 0) con->delay = BASE_DELAY_INTERVAL; else if (con->delay < MAX_DELAY_INTERVAL) @@ -2431,8 +2436,9 @@ EXPORT_SYMBOL(ceph_messenger_init); static void clear_standby(struct ceph_connection *con) { /* come back from STANDBY? */ - if (test_and_clear_bit(STANDBY, &con->state)) { + if (con->state == CON_STATE_STANDBY) { dout("clear_standby %p and ++connect_seq\n", con); + con->state = CON_STATE_PREOPEN; con->connect_seq++; WARN_ON(test_bit(WRITE_PENDING, &con->flags)); WARN_ON(test_bit(KEEPALIVE_PENDING, &con->flags)); @@ -2451,7 +2457,7 @@ void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg) mutex_lock(&con->mutex); - if (test_bit(CLOSED, &con->state)) { + if (con->state == CON_STATE_CLOSED) { dout("con_send %p closed, dropping %p\n", con, msg); ceph_msg_put(msg); mutex_unlock(&con->mutex); -- cgit v1.2.3 From 4a8616920860920abaa51193146fe36b38ef09aa Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 20 Jul 2012 17:29:55 -0700 Subject: libceph: clean up con flags Rename flags with CON_FLAG prefix, move the definitions into the c file, and (better) document their meaning. Signed-off-by: Sage Weil --- include/linux/ceph/messenger.h | 10 ------- net/ceph/messenger.c | 62 ++++++++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index d9c2b8f5abde..189ae0637634 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -106,16 +106,6 @@ struct ceph_msg_pos { #define BASE_DELAY_INTERVAL (HZ/2) #define MAX_DELAY_INTERVAL (5 * 60 * HZ) -/* - * ceph_connection flag bits - */ - -#define LOSSYTX 0 /* we can close channel or drop messages on errors */ -#define KEEPALIVE_PENDING 3 -#define WRITE_PENDING 4 /* we have data ready to send */ -#define SOCK_CLOSED 11 /* socket state changed to closed */ -#define BACKOFF 15 - /* * A single connection with another host. * diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 563e46aa4d6d..b872db5c4989 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -87,6 +87,15 @@ #define CON_STATE_OPEN 5 /* -> STANDBY, CLOSED */ #define CON_STATE_STANDBY 6 /* -> PREOPEN, CLOSED */ +/* + * ceph_connection flag bits + */ +#define CON_FLAG_LOSSYTX 0 /* we can close channel or drop + * messages on errors */ +#define CON_FLAG_KEEPALIVE_PENDING 1 /* we need to send a keepalive */ +#define CON_FLAG_WRITE_PENDING 2 /* we have data ready to send */ +#define CON_FLAG_SOCK_CLOSED 3 /* socket state changed to closed */ +#define CON_FLAG_BACKOFF 4 /* need to retry queuing delayed work */ /* static tag bytes (protocol control messages) */ static char tag_msg = CEPH_MSGR_TAG_MSG; @@ -288,7 +297,7 @@ static void ceph_sock_write_space(struct sock *sk) * buffer. See net/ipv4/tcp_input.c:tcp_check_space() * and net/core/stream.c:sk_stream_write_space(). */ - if (test_bit(WRITE_PENDING, &con->flags)) { + if (test_bit(CON_FLAG_WRITE_PENDING, &con->flags)) { if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) { dout("%s %p queueing write work\n", __func__, con); clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags); @@ -313,7 +322,7 @@ static void ceph_sock_state_change(struct sock *sk) case TCP_CLOSE_WAIT: dout("%s TCP_CLOSE_WAIT\n", __func__); con_sock_state_closing(con); - set_bit(SOCK_CLOSED, &con->flags); + set_bit(CON_FLAG_SOCK_CLOSED, &con->flags); queue_con(con); break; case TCP_ESTABLISHED: @@ -449,12 +458,12 @@ static int con_close_socket(struct ceph_connection *con) con->sock = NULL; /* - * Forcibly clear the SOCK_CLOSE flag. It gets set + * Forcibly clear the SOCK_CLOSED flag. It gets set * independent of the connection mutex, and we could have * received a socket close event before we had the chance to * shut the socket down. */ - clear_bit(SOCK_CLOSED, &con->flags); + clear_bit(CON_FLAG_SOCK_CLOSED, &con->flags); con_sock_state_closed(con); return rc; } @@ -516,9 +525,9 @@ void ceph_con_close(struct ceph_connection *con) ceph_pr_addr(&con->peer_addr.in_addr)); con->state = CON_STATE_CLOSED; - clear_bit(LOSSYTX, &con->flags); /* so we retry next connect */ - clear_bit(KEEPALIVE_PENDING, &con->flags); - clear_bit(WRITE_PENDING, &con->flags); + clear_bit(CON_FLAG_LOSSYTX, &con->flags); /* so we retry next connect */ + clear_bit(CON_FLAG_KEEPALIVE_PENDING, &con->flags); + clear_bit(CON_FLAG_WRITE_PENDING, &con->flags); reset_connection(con); con->peer_global_seq = 0; @@ -770,7 +779,7 @@ static void prepare_write_message(struct ceph_connection *con) /* no, queue up footer too and be done */ prepare_write_message_footer(con); - set_bit(WRITE_PENDING, &con->flags); + set_bit(CON_FLAG_WRITE_PENDING, &con->flags); } /* @@ -791,7 +800,7 @@ static void prepare_write_ack(struct ceph_connection *con) &con->out_temp_ack); con->out_more = 1; /* more will follow.. eventually.. */ - set_bit(WRITE_PENDING, &con->flags); + set_bit(CON_FLAG_WRITE_PENDING, &con->flags); } /* @@ -802,7 +811,7 @@ static void prepare_write_keepalive(struct ceph_connection *con) dout("prepare_write_keepalive %p\n", con); con_out_kvec_reset(con); con_out_kvec_add(con, sizeof (tag_keepalive), &tag_keepalive); - set_bit(WRITE_PENDING, &con->flags); + set_bit(CON_FLAG_WRITE_PENDING, &con->flags); } /* @@ -845,7 +854,7 @@ static void prepare_write_banner(struct ceph_connection *con) &con->msgr->my_enc_addr); con->out_more = 0; - set_bit(WRITE_PENDING, &con->flags); + set_bit(CON_FLAG_WRITE_PENDING, &con->flags); } static int prepare_write_connect(struct ceph_connection *con) @@ -896,7 +905,7 @@ static int prepare_write_connect(struct ceph_connection *con) auth->authorizer_buf); con->out_more = 0; - set_bit(WRITE_PENDING, &con->flags); + set_bit(CON_FLAG_WRITE_PENDING, &con->flags); return 0; } @@ -1622,7 +1631,7 @@ static int process_connect(struct ceph_connection *con) le32_to_cpu(con->in_reply.connect_seq)); if (con->in_reply.flags & CEPH_MSG_CONNECT_LOSSY) - set_bit(LOSSYTX, &con->flags); + set_bit(CON_FLAG_LOSSYTX, &con->flags); con->delay = 0; /* reset backoff memory */ @@ -2061,14 +2070,15 @@ do_next: prepare_write_ack(con); goto more; } - if (test_and_clear_bit(KEEPALIVE_PENDING, &con->flags)) { + if (test_and_clear_bit(CON_FLAG_KEEPALIVE_PENDING, + &con->flags)) { prepare_write_keepalive(con); goto more; } } /* Nothing to do! */ - clear_bit(WRITE_PENDING, &con->flags); + clear_bit(CON_FLAG_WRITE_PENDING, &con->flags); dout("try_write nothing else to write.\n"); ret = 0; out: @@ -2241,7 +2251,7 @@ static void con_work(struct work_struct *work) mutex_lock(&con->mutex); restart: - if (test_and_clear_bit(SOCK_CLOSED, &con->flags)) { + if (test_and_clear_bit(CON_FLAG_SOCK_CLOSED, &con->flags)) { switch (con->state) { case CON_STATE_CONNECTING: con->error_msg = "connection failed"; @@ -2260,7 +2270,7 @@ restart: goto fault; } - if (test_and_clear_bit(BACKOFF, &con->flags)) { + if (test_and_clear_bit(CON_FLAG_BACKOFF, &con->flags)) { dout("con_work %p backing off\n", con); if (queue_delayed_work(ceph_msgr_wq, &con->work, round_jiffies_relative(con->delay))) { @@ -2336,7 +2346,7 @@ static void ceph_fault(struct ceph_connection *con) con_close_socket(con); - if (test_bit(LOSSYTX, &con->flags)) { + if (test_bit(CON_FLAG_LOSSYTX, &con->flags)) { dout("fault on LOSSYTX channel, marking CLOSED\n"); con->state = CON_STATE_CLOSED; goto out_unlock; @@ -2356,9 +2366,9 @@ static void ceph_fault(struct ceph_connection *con) /* If there are no messages queued or keepalive pending, place * the connection in a STANDBY state */ if (list_empty(&con->out_queue) && - !test_bit(KEEPALIVE_PENDING, &con->flags)) { + !test_bit(CON_FLAG_KEEPALIVE_PENDING, &con->flags)) { dout("fault %p setting STANDBY clearing WRITE_PENDING\n", con); - clear_bit(WRITE_PENDING, &con->flags); + clear_bit(CON_FLAG_WRITE_PENDING, &con->flags); con->state = CON_STATE_STANDBY; } else { /* retry after a delay. */ @@ -2383,7 +2393,7 @@ static void ceph_fault(struct ceph_connection *con) * that when con_work restarts we schedule the * delay then. */ - set_bit(BACKOFF, &con->flags); + set_bit(CON_FLAG_BACKOFF, &con->flags); } } @@ -2440,8 +2450,8 @@ static void clear_standby(struct ceph_connection *con) dout("clear_standby %p and ++connect_seq\n", con); con->state = CON_STATE_PREOPEN; con->connect_seq++; - WARN_ON(test_bit(WRITE_PENDING, &con->flags)); - WARN_ON(test_bit(KEEPALIVE_PENDING, &con->flags)); + WARN_ON(test_bit(CON_FLAG_WRITE_PENDING, &con->flags)); + WARN_ON(test_bit(CON_FLAG_KEEPALIVE_PENDING, &con->flags)); } } @@ -2482,7 +2492,7 @@ void ceph_con_send(struct ceph_connection *con, struct ceph_msg *msg) /* if there wasn't anything waiting to send before, queue * new work */ - if (test_and_set_bit(WRITE_PENDING, &con->flags) == 0) + if (test_and_set_bit(CON_FLAG_WRITE_PENDING, &con->flags) == 0) queue_con(con); } EXPORT_SYMBOL(ceph_con_send); @@ -2571,8 +2581,8 @@ void ceph_con_keepalive(struct ceph_connection *con) mutex_lock(&con->mutex); clear_standby(con); mutex_unlock(&con->mutex); - if (test_and_set_bit(KEEPALIVE_PENDING, &con->flags) == 0 && - test_and_set_bit(WRITE_PENDING, &con->flags) == 0) + if (test_and_set_bit(CON_FLAG_KEEPALIVE_PENDING, &con->flags) == 0 && + test_and_set_bit(CON_FLAG_WRITE_PENDING, &con->flags) == 0) queue_con(con); } EXPORT_SYMBOL(ceph_con_keepalive); -- cgit v1.2.3 From 5accdf82ba25cacefd6c1867f1704beb4d244cdd Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 12 Jun 2012 16:20:34 +0200 Subject: fs: Improve filesystem freezing handling vfs_check_frozen() tests are racy since the filesystem can be frozen just after the test is performed. Thus in write paths we can end up marking some pages or inodes dirty even though the file system is already frozen. This creates problems with flusher thread hanging on frozen filesystem. Another problem is that exclusion between ->page_mkwrite() and filesystem freezing has been handled by setting page dirty and then verifying s_frozen. This guaranteed that either the freezing code sees the faulted page, writes it, and writeprotects it again or we see s_frozen set and bail out of page fault. This works to protect from page being marked writeable while filesystem freezing is running but has an unpleasant artefact of leaving dirty (although unmodified and writeprotected) pages on frozen filesystem resulting in similar problems with flusher thread as the first problem. This patch aims at providing exclusion between write paths and filesystem freezing. We implement a writer-freeze read-write semaphore in the superblock. Actually, there are three such semaphores because of lock ranking reasons - one for page fault handlers (->page_mkwrite), one for all other writers, and one of internal filesystem purposes (used e.g. to track running transactions). Write paths which should block freezing (e.g. directory operations, ->aio_write(), ->page_mkwrite) hold reader side of the semaphore. Code freezing the filesystem takes the writer side. Only that we don't really want to bounce cachelines of the semaphores between CPUs for each write happening. So we implement the reader side of the semaphore as a per-cpu counter and the writer side is implemented using s_writers.frozen superblock field. [AV: microoptimize sb_start_write(); we want it fast in normal case] BugLink: https://bugs.launchpad.net/bugs/897421 Tested-by: Kamal Mostafa Tested-by: Peter M. Petrakis Tested-by: Dann Frazier Tested-by: Massimo Morana Signed-off-by: Jan Kara Signed-off-by: Al Viro --- fs/super.c | 251 ++++++++++++++++++++++++++++++++++++++++++++++++----- include/linux/fs.h | 150 ++++++++++++++++++++++++++++++-- 2 files changed, 373 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/fs/super.c b/fs/super.c index c743fb3be4b8..0f64ecb7b1bf 100644 --- a/fs/super.c +++ b/fs/super.c @@ -33,12 +33,19 @@ #include #include #include +#include #include "internal.h" LIST_HEAD(super_blocks); DEFINE_SPINLOCK(sb_lock); +static char *sb_writers_name[SB_FREEZE_LEVELS] = { + "sb_writers", + "sb_pagefaults", + "sb_internal", +}; + /* * One thing we have to be careful of with a per-sb shrinker is that we don't * drop the last active reference to the superblock from within the shrinker. @@ -102,6 +109,35 @@ static int prune_super(struct shrinker *shrink, struct shrink_control *sc) return total_objects; } +static int init_sb_writers(struct super_block *s, struct file_system_type *type) +{ + int err; + int i; + + for (i = 0; i < SB_FREEZE_LEVELS; i++) { + err = percpu_counter_init(&s->s_writers.counter[i], 0); + if (err < 0) + goto err_out; + lockdep_init_map(&s->s_writers.lock_map[i], sb_writers_name[i], + &type->s_writers_key[i], 0); + } + init_waitqueue_head(&s->s_writers.wait); + init_waitqueue_head(&s->s_writers.wait_unfrozen); + return 0; +err_out: + while (--i >= 0) + percpu_counter_destroy(&s->s_writers.counter[i]); + return err; +} + +static void destroy_sb_writers(struct super_block *s) +{ + int i; + + for (i = 0; i < SB_FREEZE_LEVELS; i++) + percpu_counter_destroy(&s->s_writers.counter[i]); +} + /** * alloc_super - create new superblock * @type: filesystem type superblock should belong to @@ -117,18 +153,19 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags) if (s) { if (security_sb_alloc(s)) { + /* + * We cannot call security_sb_free() without + * security_sb_alloc() succeeding. So bail out manually + */ kfree(s); s = NULL; goto out; } #ifdef CONFIG_SMP s->s_files = alloc_percpu(struct list_head); - if (!s->s_files) { - security_sb_free(s); - kfree(s); - s = NULL; - goto out; - } else { + if (!s->s_files) + goto err_out; + else { int i; for_each_possible_cpu(i) @@ -137,6 +174,8 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags) #else INIT_LIST_HEAD(&s->s_files); #endif + if (init_sb_writers(s, type)) + goto err_out; s->s_flags = flags; s->s_bdi = &default_backing_dev_info; INIT_HLIST_NODE(&s->s_instances); @@ -190,6 +229,16 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags) } out: return s; +err_out: + security_sb_free(s); +#ifdef CONFIG_SMP + if (s->s_files) + free_percpu(s->s_files); +#endif + destroy_sb_writers(s); + kfree(s); + s = NULL; + goto out; } /** @@ -203,6 +252,7 @@ static inline void destroy_super(struct super_block *s) #ifdef CONFIG_SMP free_percpu(s->s_files); #endif + destroy_sb_writers(s); security_sb_free(s); WARN_ON(!list_empty(&s->s_mounts)); kfree(s->s_subtype); @@ -651,10 +701,11 @@ struct super_block *get_super_thawed(struct block_device *bdev) { while (1) { struct super_block *s = get_super(bdev); - if (!s || s->s_frozen == SB_UNFROZEN) + if (!s || s->s_writers.frozen == SB_UNFROZEN) return s; up_read(&s->s_umount); - vfs_check_frozen(s, SB_FREEZE_WRITE); + wait_event(s->s_writers.wait_unfrozen, + s->s_writers.frozen == SB_UNFROZEN); put_super(s); } } @@ -732,7 +783,7 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force) int retval; int remount_ro; - if (sb->s_frozen != SB_UNFROZEN) + if (sb->s_writers.frozen != SB_UNFROZEN) return -EBUSY; #ifdef CONFIG_BLOCK @@ -1163,6 +1214,120 @@ out: return ERR_PTR(error); } +/* + * This is an internal function, please use sb_end_{write,pagefault,intwrite} + * instead. + */ +void __sb_end_write(struct super_block *sb, int level) +{ + percpu_counter_dec(&sb->s_writers.counter[level-1]); + /* + * Make sure s_writers are updated before we wake up waiters in + * freeze_super(). + */ + smp_mb(); + if (waitqueue_active(&sb->s_writers.wait)) + wake_up(&sb->s_writers.wait); + rwsem_release(&sb->s_writers.lock_map[level-1], 1, _RET_IP_); +} +EXPORT_SYMBOL(__sb_end_write); + +#ifdef CONFIG_LOCKDEP +/* + * We want lockdep to tell us about possible deadlocks with freezing but + * it's it bit tricky to properly instrument it. Getting a freeze protection + * works as getting a read lock but there are subtle problems. XFS for example + * gets freeze protection on internal level twice in some cases, which is OK + * only because we already hold a freeze protection also on higher level. Due + * to these cases we have to tell lockdep we are doing trylock when we + * already hold a freeze protection for a higher freeze level. + */ +static void acquire_freeze_lock(struct super_block *sb, int level, bool trylock, + unsigned long ip) +{ + int i; + + if (!trylock) { + for (i = 0; i < level - 1; i++) + if (lock_is_held(&sb->s_writers.lock_map[i])) { + trylock = true; + break; + } + } + rwsem_acquire_read(&sb->s_writers.lock_map[level-1], 0, trylock, ip); +} +#endif + +/* + * This is an internal function, please use sb_start_{write,pagefault,intwrite} + * instead. + */ +int __sb_start_write(struct super_block *sb, int level, bool wait) +{ +retry: + if (unlikely(sb->s_writers.frozen >= level)) { + if (!wait) + return 0; + wait_event(sb->s_writers.wait_unfrozen, + sb->s_writers.frozen < level); + } + +#ifdef CONFIG_LOCKDEP + acquire_freeze_lock(sb, level, !wait, _RET_IP_); +#endif + percpu_counter_inc(&sb->s_writers.counter[level-1]); + /* + * Make sure counter is updated before we check for frozen. + * freeze_super() first sets frozen and then checks the counter. + */ + smp_mb(); + if (unlikely(sb->s_writers.frozen >= level)) { + __sb_end_write(sb, level); + goto retry; + } + return 1; +} +EXPORT_SYMBOL(__sb_start_write); + +/** + * sb_wait_write - wait until all writers to given file system finish + * @sb: the super for which we wait + * @level: type of writers we wait for (normal vs page fault) + * + * This function waits until there are no writers of given type to given file + * system. Caller of this function should make sure there can be no new writers + * of type @level before calling this function. Otherwise this function can + * livelock. + */ +static void sb_wait_write(struct super_block *sb, int level) +{ + s64 writers; + + /* + * We just cycle-through lockdep here so that it does not complain + * about returning with lock to userspace + */ + rwsem_acquire(&sb->s_writers.lock_map[level-1], 0, 0, _THIS_IP_); + rwsem_release(&sb->s_writers.lock_map[level-1], 1, _THIS_IP_); + + do { + DEFINE_WAIT(wait); + + /* + * We use a barrier in prepare_to_wait() to separate setting + * of frozen and checking of the counter + */ + prepare_to_wait(&sb->s_writers.wait, &wait, + TASK_UNINTERRUPTIBLE); + + writers = percpu_counter_sum(&sb->s_writers.counter[level-1]); + if (writers) + schedule(); + + finish_wait(&sb->s_writers.wait, &wait); + } while (writers); +} + /** * freeze_super - lock the filesystem and force it into a consistent state * @sb: the super to lock @@ -1170,6 +1335,31 @@ out: * Syncs the super to make sure the filesystem is consistent and calls the fs's * freeze_fs. Subsequent calls to this without first thawing the fs will return * -EBUSY. + * + * During this function, sb->s_writers.frozen goes through these values: + * + * SB_UNFROZEN: File system is normal, all writes progress as usual. + * + * SB_FREEZE_WRITE: The file system is in the process of being frozen. New + * writes should be blocked, though page faults are still allowed. We wait for + * all writes to complete and then proceed to the next stage. + * + * SB_FREEZE_PAGEFAULT: Freezing continues. Now also page faults are blocked + * but internal fs threads can still modify the filesystem (although they + * should not dirty new pages or inodes), writeback can run etc. After waiting + * for all running page faults we sync the filesystem which will clean all + * dirty pages and inodes (no new dirty pages or inodes can be created when + * sync is running). + * + * SB_FREEZE_FS: The file system is frozen. Now all internal sources of fs + * modification are blocked (e.g. XFS preallocation truncation on inode + * reclaim). This is usually implemented by blocking new transactions for + * filesystems that have them and need this additional guard. After all + * internal writers are finished we call ->freeze_fs() to finish filesystem + * freezing. Then we transition to SB_FREEZE_COMPLETE state. This state is + * mostly auxiliary for filesystems to verify they do not modify frozen fs. + * + * sb->s_writers.frozen is protected by sb->s_umount. */ int freeze_super(struct super_block *sb) { @@ -1177,7 +1367,7 @@ int freeze_super(struct super_block *sb) atomic_inc(&sb->s_active); down_write(&sb->s_umount); - if (sb->s_frozen) { + if (sb->s_writers.frozen != SB_UNFROZEN) { deactivate_locked_super(sb); return -EBUSY; } @@ -1188,33 +1378,53 @@ int freeze_super(struct super_block *sb) } if (sb->s_flags & MS_RDONLY) { - sb->s_frozen = SB_FREEZE_TRANS; - smp_wmb(); + /* Nothing to do really... */ + sb->s_writers.frozen = SB_FREEZE_COMPLETE; up_write(&sb->s_umount); return 0; } - sb->s_frozen = SB_FREEZE_WRITE; + /* From now on, no new normal writers can start */ + sb->s_writers.frozen = SB_FREEZE_WRITE; + smp_wmb(); + + /* Release s_umount to preserve sb_start_write -> s_umount ordering */ + up_write(&sb->s_umount); + + sb_wait_write(sb, SB_FREEZE_WRITE); + + /* Now we go and block page faults... */ + down_write(&sb->s_umount); + sb->s_writers.frozen = SB_FREEZE_PAGEFAULT; smp_wmb(); + sb_wait_write(sb, SB_FREEZE_PAGEFAULT); + + /* All writers are done so after syncing there won't be dirty data */ sync_filesystem(sb); - sb->s_frozen = SB_FREEZE_TRANS; + /* Now wait for internal filesystem counter */ + sb->s_writers.frozen = SB_FREEZE_FS; smp_wmb(); + sb_wait_write(sb, SB_FREEZE_FS); - sync_blockdev(sb->s_bdev); if (sb->s_op->freeze_fs) { ret = sb->s_op->freeze_fs(sb); if (ret) { printk(KERN_ERR "VFS:Filesystem freeze failed\n"); - sb->s_frozen = SB_UNFROZEN; + sb->s_writers.frozen = SB_UNFROZEN; smp_wmb(); - wake_up(&sb->s_wait_unfrozen); + wake_up(&sb->s_writers.wait_unfrozen); deactivate_locked_super(sb); return ret; } } + /* + * This is just for debugging purposes so that fs can warn if it + * sees write activity when frozen is set to SB_FREEZE_COMPLETE. + */ + sb->s_writers.frozen = SB_FREEZE_COMPLETE; up_write(&sb->s_umount); return 0; } @@ -1231,7 +1441,7 @@ int thaw_super(struct super_block *sb) int error; down_write(&sb->s_umount); - if (sb->s_frozen == SB_UNFROZEN) { + if (sb->s_writers.frozen == SB_UNFROZEN) { up_write(&sb->s_umount); return -EINVAL; } @@ -1244,16 +1454,15 @@ int thaw_super(struct super_block *sb) if (error) { printk(KERN_ERR "VFS:Filesystem thaw failed\n"); - sb->s_frozen = SB_FREEZE_TRANS; up_write(&sb->s_umount); return error; } } out: - sb->s_frozen = SB_UNFROZEN; + sb->s_writers.frozen = SB_UNFROZEN; smp_wmb(); - wake_up(&sb->s_wait_unfrozen); + wake_up(&sb->s_writers.wait_unfrozen); deactivate_locked_super(sb); return 0; diff --git a/include/linux/fs.h b/include/linux/fs.h index 80c819cbe272..aefed9426b03 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -412,6 +412,7 @@ struct inodes_stat_t { #include #include #include +#include #include @@ -1439,6 +1440,8 @@ extern void f_delown(struct file *filp); extern pid_t f_getown(struct file *filp); extern int send_sigurg(struct fown_struct *fown); +struct mm_struct; + /* * Umount options */ @@ -1452,6 +1455,32 @@ extern int send_sigurg(struct fown_struct *fown); extern struct list_head super_blocks; extern spinlock_t sb_lock; +/* Possible states of 'frozen' field */ +enum { + SB_UNFROZEN = 0, /* FS is unfrozen */ + SB_FREEZE_WRITE = 1, /* Writes, dir ops, ioctls frozen */ + SB_FREEZE_TRANS = 2, + SB_FREEZE_PAGEFAULT = 2, /* Page faults stopped as well */ + SB_FREEZE_FS = 3, /* For internal FS use (e.g. to stop + * internal threads if needed) */ + SB_FREEZE_COMPLETE = 4, /* ->freeze_fs finished successfully */ +}; + +#define SB_FREEZE_LEVELS (SB_FREEZE_COMPLETE - 1) + +struct sb_writers { + /* Counters for counting writers at each level */ + struct percpu_counter counter[SB_FREEZE_LEVELS]; + wait_queue_head_t wait; /* queue for waiting for + writers / faults to finish */ + int frozen; /* Is sb frozen? */ + wait_queue_head_t wait_unfrozen; /* queue for waiting for + sb to be thawed */ +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map lock_map[SB_FREEZE_LEVELS]; +#endif +}; + struct super_block { struct list_head s_list; /* Keep this first */ dev_t s_dev; /* search index; _not_ kdev_t */ @@ -1501,6 +1530,7 @@ struct super_block { int s_frozen; wait_queue_head_t s_wait_unfrozen; + struct sb_writers s_writers; char s_id[32]; /* Informational name */ u8 s_uuid[16]; /* UUID */ @@ -1555,14 +1585,119 @@ extern struct timespec current_fs_time(struct super_block *sb); /* * Snapshotting support. */ -enum { - SB_UNFROZEN = 0, - SB_FREEZE_WRITE = 1, - SB_FREEZE_TRANS = 2, -}; +/* Will go away when all users are converted */ +#define vfs_check_frozen(sb, level) do { } while (0) + +void __sb_end_write(struct super_block *sb, int level); +int __sb_start_write(struct super_block *sb, int level, bool wait); + +/** + * sb_end_write - drop write access to a superblock + * @sb: the super we wrote to + * + * Decrement number of writers to the filesystem. Wake up possible waiters + * wanting to freeze the filesystem. + */ +static inline void sb_end_write(struct super_block *sb) +{ + __sb_end_write(sb, SB_FREEZE_WRITE); +} + +/** + * sb_end_pagefault - drop write access to a superblock from a page fault + * @sb: the super we wrote to + * + * Decrement number of processes handling write page fault to the filesystem. + * Wake up possible waiters wanting to freeze the filesystem. + */ +static inline void sb_end_pagefault(struct super_block *sb) +{ + __sb_end_write(sb, SB_FREEZE_PAGEFAULT); +} + +/** + * sb_end_intwrite - drop write access to a superblock for internal fs purposes + * @sb: the super we wrote to + * + * Decrement fs-internal number of writers to the filesystem. Wake up possible + * waiters wanting to freeze the filesystem. + */ +static inline void sb_end_intwrite(struct super_block *sb) +{ + __sb_end_write(sb, SB_FREEZE_FS); +} + +/** + * sb_start_write - get write access to a superblock + * @sb: the super we write to + * + * When a process wants to write data or metadata to a file system (i.e. dirty + * a page or an inode), it should embed the operation in a sb_start_write() - + * sb_end_write() pair to get exclusion against file system freezing. This + * function increments number of writers preventing freezing. If the file + * system is already frozen, the function waits until the file system is + * thawed. + * + * Since freeze protection behaves as a lock, users have to preserve + * ordering of freeze protection and other filesystem locks. Generally, + * freeze protection should be the outermost lock. In particular, we have: + * + * sb_start_write + * -> i_mutex (write path, truncate, directory ops, ...) + * -> s_umount (freeze_super, thaw_super) + */ +static inline void sb_start_write(struct super_block *sb) +{ + __sb_start_write(sb, SB_FREEZE_WRITE, true); +} + +static inline int sb_start_write_trylock(struct super_block *sb) +{ + return __sb_start_write(sb, SB_FREEZE_WRITE, false); +} + +/** + * sb_start_pagefault - get write access to a superblock from a page fault + * @sb: the super we write to + * + * When a process starts handling write page fault, it should embed the + * operation into sb_start_pagefault() - sb_end_pagefault() pair to get + * exclusion against file system freezing. This is needed since the page fault + * is going to dirty a page. This function increments number of running page + * faults preventing freezing. If the file system is already frozen, the + * function waits until the file system is thawed. + * + * Since page fault freeze protection behaves as a lock, users have to preserve + * ordering of freeze protection and other filesystem locks. It is advised to + * put sb_start_pagefault() close to mmap_sem in lock ordering. Page fault + * handling code implies lock dependency: + * + * mmap_sem + * -> sb_start_pagefault + */ +static inline void sb_start_pagefault(struct super_block *sb) +{ + __sb_start_write(sb, SB_FREEZE_PAGEFAULT, true); +} + +/* + * sb_start_intwrite - get write access to a superblock for internal fs purposes + * @sb: the super we write to + * + * This is the third level of protection against filesystem freezing. It is + * free for use by a filesystem. The only requirement is that it must rank + * below sb_start_pagefault. + * + * For example filesystem can call sb_start_intwrite() when starting a + * transaction which somewhat eases handling of freezing for internal sources + * of filesystem changes (internal fs threads, discarding preallocation on file + * close, etc.). + */ +static inline void sb_start_intwrite(struct super_block *sb) +{ + __sb_start_write(sb, SB_FREEZE_FS, true); +} -#define vfs_check_frozen(sb, level) \ - wait_event((sb)->s_wait_unfrozen, ((sb)->s_frozen < (level))) extern bool inode_owner_or_capable(const struct inode *inode); @@ -1886,6 +2021,7 @@ struct file_system_type { struct lock_class_key s_lock_key; struct lock_class_key s_umount_key; struct lock_class_key s_vfs_rename_key; + struct lock_class_key s_writers_key[SB_FREEZE_LEVELS]; struct lock_class_key i_lock_key; struct lock_class_key i_mutex_key; -- cgit v1.2.3 From d9c95bdd53a8d9116d269c91ce3d151472e6bcd6 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 12 Jun 2012 16:20:47 +0200 Subject: fs: Remove old freezing mechanism Now that all users are converted, we can remove functions, variables, and constants defined by the old freezing mechanism. BugLink: https://bugs.launchpad.net/bugs/897421 Tested-by: Kamal Mostafa Tested-by: Peter M. Petrakis Tested-by: Dann Frazier Tested-by: Massimo Morana Signed-off-by: Jan Kara Signed-off-by: Al Viro --- fs/super.c | 1 - include/linux/fs.h | 5 ----- 2 files changed, 6 deletions(-) (limited to 'include') diff --git a/fs/super.c b/fs/super.c index 0f64ecb7b1bf..a87dc1b1ac92 100644 --- a/fs/super.c +++ b/fs/super.c @@ -217,7 +217,6 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags) mutex_init(&s->s_dquot.dqio_mutex); mutex_init(&s->s_dquot.dqonoff_mutex); init_rwsem(&s->s_dquot.dqptr_sem); - init_waitqueue_head(&s->s_wait_unfrozen); s->s_maxbytes = MAX_NON_LFS; s->s_op = &default_op; s->s_time_gran = 1000000000; diff --git a/include/linux/fs.h b/include/linux/fs.h index aefed9426b03..0f4b79be8717 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1459,7 +1459,6 @@ extern spinlock_t sb_lock; enum { SB_UNFROZEN = 0, /* FS is unfrozen */ SB_FREEZE_WRITE = 1, /* Writes, dir ops, ioctls frozen */ - SB_FREEZE_TRANS = 2, SB_FREEZE_PAGEFAULT = 2, /* Page faults stopped as well */ SB_FREEZE_FS = 3, /* For internal FS use (e.g. to stop * internal threads if needed) */ @@ -1528,8 +1527,6 @@ struct super_block { struct hlist_node s_instances; struct quota_info s_dquot; /* Diskquota specific options */ - int s_frozen; - wait_queue_head_t s_wait_unfrozen; struct sb_writers s_writers; char s_id[32]; /* Informational name */ @@ -1585,8 +1582,6 @@ extern struct timespec current_fs_time(struct super_block *sb); /* * Snapshotting support. */ -/* Will go away when all users are converted */ -#define vfs_check_frozen(sb, level) do { } while (0) void __sb_end_write(struct super_block *sb, int level); int __sb_start_write(struct super_block *sb, int level, bool wait); -- cgit v1.2.3 From 9cbb17508808f8a6bdd83354b61e126ac4fa6fed Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 31 Jul 2012 09:08:14 +0200 Subject: blk: centralize non-request unplug handling. Both md and umem has similar code for getting notified on an blk_finish_plug event. Centralize this code in block/ and allow each driver to provide its distinctive difference. Signed-off-by: NeilBrown Signed-off-by: Jens Axboe --- block/blk-core.c | 25 ++++++++++++++++++++++ drivers/block/umem.c | 35 ++++++------------------------- drivers/md/md.c | 56 +++++--------------------------------------------- drivers/md/md.h | 8 +++++++- include/linux/blkdev.h | 8 ++++++-- 5 files changed, 49 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index dd134d834d58..177ddcf356e6 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2927,6 +2927,31 @@ static void flush_plug_callbacks(struct blk_plug *plug) } } +struct blk_plug_cb *blk_check_plugged(blk_plug_cb_fn unplug, void *data, + int size) +{ + struct blk_plug *plug = current->plug; + struct blk_plug_cb *cb; + + if (!plug) + return NULL; + + list_for_each_entry(cb, &plug->cb_list, list) + if (cb->callback == unplug && cb->data == data) + return cb; + + /* Not currently on the callback list */ + BUG_ON(size < sizeof(*cb)); + cb = kzalloc(size, GFP_ATOMIC); + if (cb) { + cb->data = data; + cb->callback = unplug; + list_add(&cb->list, &plug->cb_list); + } + return cb; +} +EXPORT_SYMBOL(blk_check_plugged); + void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule) { struct request_queue *q; diff --git a/drivers/block/umem.c b/drivers/block/umem.c index 9a72277a31df..6ef3489568e3 100644 --- a/drivers/block/umem.c +++ b/drivers/block/umem.c @@ -513,42 +513,19 @@ static void process_page(unsigned long data) } } -struct mm_plug_cb { - struct blk_plug_cb cb; - struct cardinfo *card; -}; - static void mm_unplug(struct blk_plug_cb *cb) { - struct mm_plug_cb *mmcb = container_of(cb, struct mm_plug_cb, cb); + struct cardinfo *card = cb->data; - spin_lock_irq(&mmcb->card->lock); - activate(mmcb->card); - spin_unlock_irq(&mmcb->card->lock); - kfree(mmcb); + spin_lock_irq(&card->lock); + activate(card); + spin_unlock_irq(&card->lock); + kfree(cb); } static int mm_check_plugged(struct cardinfo *card) { - struct blk_plug *plug = current->plug; - struct mm_plug_cb *mmcb; - - if (!plug) - return 0; - - list_for_each_entry(mmcb, &plug->cb_list, cb.list) { - if (mmcb->cb.callback == mm_unplug && mmcb->card == card) - return 1; - } - /* Not currently on the callback list */ - mmcb = kmalloc(sizeof(*mmcb), GFP_ATOMIC); - if (!mmcb) - return 0; - - mmcb->card = card; - mmcb->cb.callback = mm_unplug; - list_add(&mmcb->cb.list, &plug->cb_list); - return 1; + return !!blk_check_plugged(mm_unplug, card, sizeof(struct blk_plug_cb)); } static void mm_make_request(struct request_queue *q, struct bio *bio) diff --git a/drivers/md/md.c b/drivers/md/md.c index 34381172a947..b493fa417387 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -498,59 +498,13 @@ void md_flush_request(struct mddev *mddev, struct bio *bio) } EXPORT_SYMBOL(md_flush_request); -/* Support for plugging. - * This mirrors the plugging support in request_queue, but does not - * require having a whole queue or request structures. - * We allocate an md_plug_cb for each md device and each thread it gets - * plugged on. This links tot the private plug_handle structure in the - * personality data where we keep a count of the number of outstanding - * plugs so other code can see if a plug is active. - */ -struct md_plug_cb { - struct blk_plug_cb cb; - struct mddev *mddev; -}; - -static void plugger_unplug(struct blk_plug_cb *cb) +void md_unplug(struct blk_plug_cb *cb) { - struct md_plug_cb *mdcb = container_of(cb, struct md_plug_cb, cb); - md_wakeup_thread(mdcb->mddev->thread); - kfree(mdcb); -} - -/* Check that an unplug wakeup will come shortly. - * If not, wakeup the md thread immediately - */ -int mddev_check_plugged(struct mddev *mddev) -{ - struct blk_plug *plug = current->plug; - struct md_plug_cb *mdcb; - - if (!plug) - return 0; - - list_for_each_entry(mdcb, &plug->cb_list, cb.list) { - if (mdcb->cb.callback == plugger_unplug && - mdcb->mddev == mddev) { - /* Already on the list, move to top */ - if (mdcb != list_first_entry(&plug->cb_list, - struct md_plug_cb, - cb.list)) - list_move(&mdcb->cb.list, &plug->cb_list); - return 1; - } - } - /* Not currently on the callback list */ - mdcb = kmalloc(sizeof(*mdcb), GFP_ATOMIC); - if (!mdcb) - return 0; - - mdcb->mddev = mddev; - mdcb->cb.callback = plugger_unplug; - list_add(&mdcb->cb.list, &plug->cb_list); - return 1; + struct mddev *mddev = cb->data; + md_wakeup_thread(mddev->thread); + kfree(cb); } -EXPORT_SYMBOL_GPL(mddev_check_plugged); +EXPORT_SYMBOL(md_unplug); static inline struct mddev *mddev_get(struct mddev *mddev) { diff --git a/drivers/md/md.h b/drivers/md/md.h index 91786c46b85c..8f998e08fb87 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -627,6 +627,12 @@ extern struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask, struct mddev *mddev); extern struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs, struct mddev *mddev); -extern int mddev_check_plugged(struct mddev *mddev); extern void md_trim_bio(struct bio *bio, int offset, int size); + +extern void md_unplug(struct blk_plug_cb *cb); +static inline int mddev_check_plugged(struct mddev *mddev) +{ + return !!blk_check_plugged(md_unplug, mddev, + sizeof(struct blk_plug_cb)); +} #endif /* _MD_MD_H */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 3816ce8a08fc..607ca228f47e 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -922,11 +922,15 @@ struct blk_plug { }; #define BLK_MAX_REQUEST_COUNT 16 +struct blk_plug_cb; +typedef void (*blk_plug_cb_fn)(struct blk_plug_cb *); struct blk_plug_cb { struct list_head list; - void (*callback)(struct blk_plug_cb *); + blk_plug_cb_fn callback; + void *data; }; - +extern struct blk_plug_cb *blk_check_plugged(blk_plug_cb_fn unplug, + void *data, int size); extern void blk_start_plug(struct blk_plug *); extern void blk_finish_plug(struct blk_plug *); extern void blk_flush_plug_list(struct blk_plug *, bool); -- cgit v1.2.3 From 74018dc3063a2c729fc73041c0a9f03aac995920 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 31 Jul 2012 09:08:15 +0200 Subject: blk: pass from_schedule to non-request unplug functions. This will allow md/raid to know why the unplug was called, and will be able to act according - if !from_schedule it is safe to perform tasks which could themselves schedule. Signed-off-by: NeilBrown Signed-off-by: Jens Axboe --- block/blk-core.c | 6 +++--- drivers/block/umem.c | 2 +- drivers/md/md.c | 2 +- drivers/md/md.h | 2 +- include/linux/blkdev.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 35bf4fe8234c..4b4dbdfbca89 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2909,7 +2909,7 @@ static void queue_unplugged(struct request_queue *q, unsigned int depth, } -static void flush_plug_callbacks(struct blk_plug *plug) +static void flush_plug_callbacks(struct blk_plug *plug, bool from_schedule) { LIST_HEAD(callbacks); @@ -2921,7 +2921,7 @@ static void flush_plug_callbacks(struct blk_plug *plug) struct blk_plug_cb, list); list_del(&cb->list); - cb->callback(cb); + cb->callback(cb, from_schedule); } } } @@ -2961,7 +2961,7 @@ void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule) BUG_ON(plug->magic != PLUG_MAGIC); - flush_plug_callbacks(plug); + flush_plug_callbacks(plug, from_schedule); if (list_empty(&plug->list)) return; diff --git a/drivers/block/umem.c b/drivers/block/umem.c index 6ef3489568e3..eb0d8216f557 100644 --- a/drivers/block/umem.c +++ b/drivers/block/umem.c @@ -513,7 +513,7 @@ static void process_page(unsigned long data) } } -static void mm_unplug(struct blk_plug_cb *cb) +static void mm_unplug(struct blk_plug_cb *cb, bool from_schedule) { struct cardinfo *card = cb->data; diff --git a/drivers/md/md.c b/drivers/md/md.c index b493fa417387..db02d2efb76f 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -498,7 +498,7 @@ void md_flush_request(struct mddev *mddev, struct bio *bio) } EXPORT_SYMBOL(md_flush_request); -void md_unplug(struct blk_plug_cb *cb) +void md_unplug(struct blk_plug_cb *cb, bool from_schedule) { struct mddev *mddev = cb->data; md_wakeup_thread(mddev->thread); diff --git a/drivers/md/md.h b/drivers/md/md.h index 8f998e08fb87..f385b038589d 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -629,7 +629,7 @@ extern struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs, struct mddev *mddev); extern void md_trim_bio(struct bio *bio, int offset, int size); -extern void md_unplug(struct blk_plug_cb *cb); +extern void md_unplug(struct blk_plug_cb *cb, bool from_schedule); static inline int mddev_check_plugged(struct mddev *mddev) { return !!blk_check_plugged(md_unplug, mddev, diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 607ca228f47e..4e72a9d48232 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -923,7 +923,7 @@ struct blk_plug { #define BLK_MAX_REQUEST_COUNT 16 struct blk_plug_cb; -typedef void (*blk_plug_cb_fn)(struct blk_plug_cb *); +typedef void (*blk_plug_cb_fn)(struct blk_plug_cb *, bool); struct blk_plug_cb { struct list_head list; blk_plug_cb_fn callback; -- cgit v1.2.3 From 7bedaa5537604f34d1d63c5ec7891e559d2a61ed Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 13 Apr 2012 12:10:24 +0100 Subject: dmaengine: add OMAP DMA engine driver Tested-by: Tony Lindgren Signed-off-by: Russell King --- drivers/dma/Kconfig | 6 + drivers/dma/Makefile | 1 + drivers/dma/omap-dma.c | 524 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/omap-dma.h | 22 ++ 4 files changed, 553 insertions(+) create mode 100644 drivers/dma/omap-dma.c create mode 100644 include/linux/omap-dma.h (limited to 'include') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index eb2b60e8e1cb..8be3bf68c0bd 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -261,6 +261,12 @@ config DMA_SA11X0 SA-1110 SoCs. This DMA engine can only be used with on-chip devices. +config DMA_OMAP + tristate "OMAP DMA support" + depends on ARCH_OMAP + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index fc05f7ddac7e..ddc291a2116a 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -29,3 +29,4 @@ obj-$(CONFIG_PCH_DMA) += pch_dma.o obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o +obj-$(CONFIG_DMA_OMAP) += omap-dma.o diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c new file mode 100644 index 000000000000..fe33ddd8eb0f --- /dev/null +++ b/drivers/dma/omap-dma.c @@ -0,0 +1,524 @@ +/* + * OMAP DMAengine support + * + * 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 +#include +#include + +#include "virt-dma.h" +#include + +struct omap_dmadev { + struct dma_device ddev; + spinlock_t lock; + struct tasklet_struct task; + struct list_head pending; +}; + +struct omap_chan { + struct virt_dma_chan vc; + struct list_head node; + + struct dma_slave_config cfg; + unsigned dma_sig; + + int dma_ch; + struct omap_desc *desc; + unsigned sgidx; +}; + +struct omap_sg { + dma_addr_t addr; + uint32_t en; /* number of elements (24-bit) */ + uint32_t fn; /* number of frames (16-bit) */ +}; + +struct omap_desc { + struct virt_dma_desc vd; + enum dma_transfer_direction dir; + dma_addr_t dev_addr; + + uint8_t es; /* OMAP_DMA_DATA_TYPE_xxx */ + uint8_t sync_mode; /* OMAP_DMA_SYNC_xxx */ + uint8_t sync_type; /* OMAP_DMA_xxx_SYNC* */ + uint8_t periph_port; /* Peripheral port */ + + unsigned sglen; + struct omap_sg sg[0]; +}; + +static const unsigned es_bytes[] = { + [OMAP_DMA_DATA_TYPE_S8] = 1, + [OMAP_DMA_DATA_TYPE_S16] = 2, + [OMAP_DMA_DATA_TYPE_S32] = 4, +}; + +static inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d) +{ + return container_of(d, struct omap_dmadev, ddev); +} + +static inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c) +{ + return container_of(c, struct omap_chan, vc.chan); +} + +static inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t) +{ + return container_of(t, struct omap_desc, vd.tx); +} + +static void omap_dma_desc_free(struct virt_dma_desc *vd) +{ + kfree(container_of(vd, struct omap_desc, vd)); +} + +static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d, + unsigned idx) +{ + struct omap_sg *sg = d->sg + idx; + + if (d->dir == DMA_DEV_TO_MEM) + omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0); + else + omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0); + + omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn, + d->sync_mode, c->dma_sig, d->sync_type); + + omap_start_dma(c->dma_ch); +} + +static void omap_dma_start_desc(struct omap_chan *c) +{ + struct virt_dma_desc *vd = vchan_next_desc(&c->vc); + struct omap_desc *d; + + if (!vd) { + c->desc = NULL; + return; + } + + list_del(&vd->node); + + c->desc = d = to_omap_dma_desc(&vd->tx); + c->sgidx = 0; + + if (d->dir == DMA_DEV_TO_MEM) + omap_set_dma_src_params(c->dma_ch, d->periph_port, + OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0); + else + omap_set_dma_dest_params(c->dma_ch, d->periph_port, + OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0); + + omap_dma_start_sg(c, d, 0); +} + +static void omap_dma_callback(int ch, u16 status, void *data) +{ + struct omap_chan *c = data; + struct omap_desc *d; + unsigned long flags; + + spin_lock_irqsave(&c->vc.lock, flags); + d = c->desc; + if (d) { + if (++c->sgidx < d->sglen) { + omap_dma_start_sg(c, d, c->sgidx); + } else { + omap_dma_start_desc(c); + vchan_cookie_complete(&d->vd); + } + } + spin_unlock_irqrestore(&c->vc.lock, flags); +} + +/* + * This callback schedules all pending channels. We could be more + * clever here by postponing allocation of the real DMA channels to + * this point, and freeing them when our virtual channel becomes idle. + * + * We would then need to deal with 'all channels in-use' + */ +static void omap_dma_sched(unsigned long data) +{ + struct omap_dmadev *d = (struct omap_dmadev *)data; + LIST_HEAD(head); + + spin_lock_irq(&d->lock); + list_splice_tail_init(&d->pending, &head); + spin_unlock_irq(&d->lock); + + while (!list_empty(&head)) { + struct omap_chan *c = list_first_entry(&head, + struct omap_chan, node); + + spin_lock_irq(&c->vc.lock); + list_del_init(&c->node); + omap_dma_start_desc(c); + spin_unlock_irq(&c->vc.lock); + } +} + +static int omap_dma_alloc_chan_resources(struct dma_chan *chan) +{ + struct omap_chan *c = to_omap_dma_chan(chan); + + dev_info(c->vc.chan.device->dev, "allocating channel for %u\n", c->dma_sig); + + return omap_request_dma(c->dma_sig, "DMA engine", + omap_dma_callback, c, &c->dma_ch); +} + +static void omap_dma_free_chan_resources(struct dma_chan *chan) +{ + struct omap_chan *c = to_omap_dma_chan(chan); + + vchan_free_chan_resources(&c->vc); + omap_free_dma(c->dma_ch); + + dev_info(c->vc.chan.device->dev, "freeing channel for %u\n", c->dma_sig); +} + +static enum dma_status omap_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) +{ + /* + * FIXME: do we need to return pending bytes? + * We have no users of that info at the moment... + */ + return dma_cookie_status(chan, cookie, txstate); +} + +static void omap_dma_issue_pending(struct dma_chan *chan) +{ + struct omap_chan *c = to_omap_dma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&c->vc.lock, flags); + if (vchan_issue_pending(&c->vc) && !c->desc) { + struct omap_dmadev *d = to_omap_dma_dev(chan->device); + spin_lock(&d->lock); + if (list_empty(&c->node)) + list_add_tail(&c->node, &d->pending); + spin_unlock(&d->lock); + tasklet_schedule(&d->task); + } + spin_unlock_irqrestore(&c->vc.lock, flags); +} + +static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen, + enum dma_transfer_direction dir, unsigned long tx_flags, void *context) +{ + struct omap_chan *c = to_omap_dma_chan(chan); + enum dma_slave_buswidth dev_width; + struct scatterlist *sgent; + struct omap_desc *d; + dma_addr_t dev_addr; + unsigned i, j = 0, es, en, frame_bytes, sync_type; + u32 burst; + + if (dir == DMA_DEV_TO_MEM) { + dev_addr = c->cfg.src_addr; + dev_width = c->cfg.src_addr_width; + burst = c->cfg.src_maxburst; + sync_type = OMAP_DMA_SRC_SYNC; + } else if (dir == DMA_MEM_TO_DEV) { + dev_addr = c->cfg.dst_addr; + dev_width = c->cfg.dst_addr_width; + burst = c->cfg.dst_maxburst; + sync_type = OMAP_DMA_DST_SYNC; + } else { + dev_err(chan->device->dev, "%s: bad direction?\n", __func__); + return NULL; + } + + /* Bus width translates to the element size (ES) */ + switch (dev_width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + es = OMAP_DMA_DATA_TYPE_S8; + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + es = OMAP_DMA_DATA_TYPE_S16; + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + es = OMAP_DMA_DATA_TYPE_S32; + break; + default: /* not reached */ + return NULL; + } + + /* Now allocate and setup the descriptor. */ + d = kzalloc(sizeof(*d) + sglen * sizeof(d->sg[0]), GFP_ATOMIC); + if (!d) + return NULL; + + d->dir = dir; + d->dev_addr = dev_addr; + d->es = es; + d->sync_mode = OMAP_DMA_SYNC_FRAME; + d->sync_type = sync_type; + d->periph_port = OMAP_DMA_PORT_TIPB; + + /* + * Build our scatterlist entries: each contains the address, + * the number of elements (EN) in each frame, and the number of + * frames (FN). Number of bytes for this entry = ES * EN * FN. + * + * Burst size translates to number of elements with frame sync. + * Note: DMA engine defines burst to be the number of dev-width + * transfers. + */ + en = burst; + frame_bytes = es_bytes[es] * en; + for_each_sg(sgl, sgent, sglen, i) { + d->sg[j].addr = sg_dma_address(sgent); + d->sg[j].en = en; + d->sg[j].fn = sg_dma_len(sgent) / frame_bytes; + j++; + } + + d->sglen = j; + + return vchan_tx_prep(&c->vc, &d->vd, tx_flags); +} + +static int omap_dma_slave_config(struct omap_chan *c, struct dma_slave_config *cfg) +{ + if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || + cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) + return -EINVAL; + + memcpy(&c->cfg, cfg, sizeof(c->cfg)); + + return 0; +} + +static int omap_dma_terminate_all(struct omap_chan *c) +{ + struct omap_dmadev *d = to_omap_dma_dev(c->vc.chan.device); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&c->vc.lock, flags); + + /* Prevent this channel being scheduled */ + spin_lock(&d->lock); + list_del_init(&c->node); + spin_unlock(&d->lock); + + /* + * Stop DMA activity: we assume the callback will not be called + * after omap_stop_dma() returns (even if it does, it will see + * c->desc is NULL and exit.) + */ + if (c->desc) { + c->desc = NULL; + omap_stop_dma(c->dma_ch); + } + + vchan_get_all_descriptors(&c->vc, &head); + spin_unlock_irqrestore(&c->vc.lock, flags); + vchan_dma_desc_free_list(&c->vc, &head); + + return 0; +} + +static int omap_dma_pause(struct omap_chan *c) +{ + /* FIXME: not supported by platform private API */ + return -EINVAL; +} + +static int omap_dma_resume(struct omap_chan *c) +{ + /* FIXME: not supported by platform private API */ + return -EINVAL; +} + +static int omap_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct omap_chan *c = to_omap_dma_chan(chan); + int ret; + + switch (cmd) { + case DMA_SLAVE_CONFIG: + ret = omap_dma_slave_config(c, (struct dma_slave_config *)arg); + break; + + case DMA_TERMINATE_ALL: + ret = omap_dma_terminate_all(c); + break; + + case DMA_PAUSE: + ret = omap_dma_pause(c); + break; + + case DMA_RESUME: + ret = omap_dma_resume(c); + break; + + default: + ret = -ENXIO; + break; + } + + return ret; +} + +static int omap_dma_chan_init(struct omap_dmadev *od, int dma_sig) +{ + struct omap_chan *c; + + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + return -ENOMEM; + + c->dma_sig = dma_sig; + c->vc.desc_free = omap_dma_desc_free; + vchan_init(&c->vc, &od->ddev); + INIT_LIST_HEAD(&c->node); + + od->ddev.chancnt++; + + return 0; +} + +static void omap_dma_free(struct omap_dmadev *od) +{ + tasklet_kill(&od->task); + while (!list_empty(&od->ddev.channels)) { + struct omap_chan *c = list_first_entry(&od->ddev.channels, + struct omap_chan, vc.chan.device_node); + + list_del(&c->vc.chan.device_node); + tasklet_kill(&c->vc.task); + kfree(c); + } + kfree(od); +} + +static int omap_dma_probe(struct platform_device *pdev) +{ + struct omap_dmadev *od; + int rc, i; + + od = kzalloc(sizeof(*od), GFP_KERNEL); + if (!od) + return -ENOMEM; + + dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); + od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources; + od->ddev.device_free_chan_resources = omap_dma_free_chan_resources; + od->ddev.device_tx_status = omap_dma_tx_status; + od->ddev.device_issue_pending = omap_dma_issue_pending; + od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg; + od->ddev.device_control = omap_dma_control; + od->ddev.dev = &pdev->dev; + INIT_LIST_HEAD(&od->ddev.channels); + INIT_LIST_HEAD(&od->pending); + spin_lock_init(&od->lock); + + tasklet_init(&od->task, omap_dma_sched, (unsigned long)od); + + for (i = 0; i < 127; i++) { + rc = omap_dma_chan_init(od, i); + if (rc) { + omap_dma_free(od); + return rc; + } + } + + rc = dma_async_device_register(&od->ddev); + if (rc) { + pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n", + rc); + omap_dma_free(od); + } else { + platform_set_drvdata(pdev, od); + } + + dev_info(&pdev->dev, "OMAP DMA engine driver\n"); + + return rc; +} + +static int omap_dma_remove(struct platform_device *pdev) +{ + struct omap_dmadev *od = platform_get_drvdata(pdev); + + dma_async_device_unregister(&od->ddev); + omap_dma_free(od); + + return 0; +} + +static struct platform_driver omap_dma_driver = { + .probe = omap_dma_probe, + .remove = omap_dma_remove, + .driver = { + .name = "omap-dma-engine", + .owner = THIS_MODULE, + }, +}; + +bool omap_dma_filter_fn(struct dma_chan *chan, void *param) +{ + if (chan->device->dev->driver == &omap_dma_driver.driver) { + struct omap_chan *c = to_omap_dma_chan(chan); + unsigned req = *(unsigned *)param; + + return req == c->dma_sig; + } + return false; +} +EXPORT_SYMBOL_GPL(omap_dma_filter_fn); + +static struct platform_device *pdev; + +static const struct platform_device_info omap_dma_dev_info = { + .name = "omap-dma-engine", + .id = -1, + .dma_mask = DMA_BIT_MASK(32), +}; + +static int omap_dma_init(void) +{ + int rc = platform_driver_register(&omap_dma_driver); + + if (rc == 0) { + pdev = platform_device_register_full(&omap_dma_dev_info); + if (IS_ERR(pdev)) { + platform_driver_unregister(&omap_dma_driver); + rc = PTR_ERR(pdev); + } + } + return rc; +} +subsys_initcall(omap_dma_init); + +static void __exit omap_dma_exit(void) +{ + platform_device_unregister(pdev); + platform_driver_unregister(&omap_dma_driver); +} +module_exit(omap_dma_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/omap-dma.h b/include/linux/omap-dma.h new file mode 100644 index 000000000000..eb475a8ea25b --- /dev/null +++ b/include/linux/omap-dma.h @@ -0,0 +1,22 @@ +/* + * OMAP DMA Engine support + * + * 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 __LINUX_OMAP_DMA_H +#define __LINUX_OMAP_DMA_H + +struct dma_chan; + +#if defined(CONFIG_DMA_OMAP) || defined(CONFIG_DMA_OMAP_MODULE) +bool omap_dma_filter_fn(struct dma_chan *, void *); +#else +static inline bool omap_dma_filter_fn(struct dma_chan *c, void *d) +{ + return false; +} +#endif + +#endif -- cgit v1.2.3 From 4b1bf5871f7d59de6484cc887e205d6d2f1e6fbd Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 31 Jul 2012 04:39:30 -0700 Subject: thermal: Constify 'type' argument for the registration routine thermal_zone_device_register() does not modify 'type' argument, so it is safe to declare it as const. Otherwise, if we pass a const string, we are getting the ugly warning: CC drivers/power/power_supply_core.o drivers/power/power_supply_core.c: In function 'psy_register_thermal': drivers/power/power_supply_core.c:204:6: warning: passing argument 1 of 'thermal_zone_device_register' discards 'const' qualifier from pointer target type [enabled by default] include/linux/thermal.h:140:29: note: expected 'char *' but argument is of type 'const char *' Signed-off-by: Anton Vorontsov Acked-by: Jean Delvare --- drivers/thermal/thermal_sys.c | 2 +- include/linux/thermal.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 5feb3353213f..32aa66d7d6b3 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -1173,7 +1173,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz) * longer needed. The passive cooling formula uses tc1 and tc2 as described in * section 11.1.5.1 of the ACPI specification 3.0. */ -struct thermal_zone_device *thermal_zone_device_register(char *type, +struct thermal_zone_device *thermal_zone_device_register(const char *type, int trips, int mask, void *devdata, const struct thermal_zone_device_ops *ops, int tc1, int tc2, int passive_delay, int polling_delay) diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 6eaf9146c847..4821735f3e77 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -146,7 +146,7 @@ enum { }; #define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1) -struct thermal_zone_device *thermal_zone_device_register(char *, int, int, +struct thermal_zone_device *thermal_zone_device_register(const char *, int, int, void *, const struct thermal_zone_device_ops *, int tc1, int tc2, int passive_freq, int polling_freq); void thermal_zone_device_unregister(struct thermal_zone_device *); -- cgit v1.2.3 From 0f26d0e0a715556270d85b7946b99546a2f92888 Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Mon, 30 Jul 2012 22:44:41 -0500 Subject: kdb: Remove unused KDB_FLAG_ONLY_DO_DUMP This code cleanup was missed in the original kdb merge, and this code is simply not used at all. The code that was previously used to set the KDB_FLAG_ONLY_DO_DUMP was removed prior to the initial kdb merge. Signed-off-by: Jason Wessel --- include/linux/kdb.h | 2 -- kernel/debug/kdb/kdb_main.c | 15 +-------------- 2 files changed, 1 insertion(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/kdb.h b/include/linux/kdb.h index 064725854db8..42d9e863a313 100644 --- a/include/linux/kdb.h +++ b/include/linux/kdb.h @@ -75,8 +75,6 @@ extern const char *kdb_diemsg; #define KDB_FLAG_CATASTROPHIC (1 << 1) /* A catastrophic event has occurred */ #define KDB_FLAG_CMD_INTERRUPT (1 << 2) /* Previous command was interrupted */ #define KDB_FLAG_NOIPI (1 << 3) /* Do not send IPIs */ -#define KDB_FLAG_ONLY_DO_DUMP (1 << 4) /* Only do a dump, used when - * kdb is off */ #define KDB_FLAG_NO_CONSOLE (1 << 5) /* No console is available, * kdb is disabled */ #define KDB_FLAG_NO_VT_CONSOLE (1 << 6) /* No VT console is available, do diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index 1f91413edb87..31df1706b9a9 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -139,11 +139,10 @@ static const int __nkdb_err = sizeof(kdbmsgs) / sizeof(kdbmsg_t); static char *__env[] = { #if defined(CONFIG_SMP) "PROMPT=[%d]kdb> ", - "MOREPROMPT=[%d]more> ", #else "PROMPT=kdb> ", - "MOREPROMPT=more> ", #endif + "MOREPROMPT=more> ", "RADIX=16", "MDCOUNT=8", /* lines of md output */ KDB_PLATFORM_ENV, @@ -1236,18 +1235,6 @@ static int kdb_local(kdb_reason_t reason, int error, struct pt_regs *regs, *cmdbuf = '\0'; *(cmd_hist[cmd_head]) = '\0'; - if (KDB_FLAG(ONLY_DO_DUMP)) { - /* kdb is off but a catastrophic error requires a dump. - * Take the dump and reboot. - * Turn on logging so the kdb output appears in the log - * buffer in the dump. - */ - const char *setargs[] = { "set", "LOGGING", "1" }; - kdb_set(2, setargs); - kdb_reboot(0, NULL); - /*NOTREACHED*/ - } - do_full_getstr: #if defined(CONFIG_SMP) snprintf(kdb_prompt_str, CMD_BUFLEN, kdbgetenv("PROMPT"), -- cgit v1.2.3 From cba3345cc494ad286ca8823f44b2c16cae496679 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 31 Jul 2012 08:16:22 -0600 Subject: vfio: VFIO core VFIO is a secure user level driver for use with both virtual machines and user level drivers. VFIO makes use of IOMMU groups to ensure the isolation of devices in use, allowing unprivileged user access. It's intended that VFIO will replace KVM device assignment and UIO drivers (in cases where the target platform includes a sufficiently capable IOMMU). New in this version of VFIO is support for IOMMU groups managed through the IOMMU core as well as a rework of the API, removing the group merge interface. We now go back to a model more similar to original VFIO with UIOMMU support where the file descriptor obtained from /dev/vfio/vfio allows access to the IOMMU, but only after a group is added, avoiding the previous privilege issues with this type of model. IOMMU support is also now fully modular as IOMMUs have vastly different interface requirements on different platforms. VFIO users are able to query and initialize the IOMMU model of their choice. Please see the follow-on Documentation commit for further description and usage example. Signed-off-by: Alex Williamson --- Documentation/ioctl/ioctl-number.txt | 1 + MAINTAINERS | 8 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/vfio/Kconfig | 8 + drivers/vfio/Makefile | 1 + drivers/vfio/vfio.c | 1413 ++++++++++++++++++++++++++++++++++ include/linux/vfio.h | 367 +++++++++ 8 files changed, 1801 insertions(+) create mode 100644 drivers/vfio/Kconfig create mode 100644 drivers/vfio/Makefile create mode 100644 drivers/vfio/vfio.c create mode 100644 include/linux/vfio.h (limited to 'include') diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 915f28c470e9..849b771c5e03 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -88,6 +88,7 @@ Code Seq#(hex) Include File Comments and kernel/power/user.c '8' all SNP8023 advanced NIC card +';' 64-7F linux/vfio.h '@' 00-0F linux/radeonfb.h conflict! '@' 00-0F drivers/video/aty/aty128fb.c conflict! 'A' 00-1F linux/apm_bios.h conflict! diff --git a/MAINTAINERS b/MAINTAINERS index fb036a062a5d..eceaab93811e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7411,6 +7411,14 @@ S: Maintained F: Documentation/filesystems/vfat.txt F: fs/fat/ +VFIO DRIVER +M: Alex Williamson +L: kvm@vger.kernel.org +S: Maintained +F: Documentation/vfio.txt +F: drivers/vfio/ +F: include/linux/vfio.h + VIDEOBUF2 FRAMEWORK M: Pawel Osciak M: Marek Szyprowski diff --git a/drivers/Kconfig b/drivers/Kconfig index 805c432c9439..ece958d3762e 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -112,6 +112,8 @@ source "drivers/auxdisplay/Kconfig" source "drivers/uio/Kconfig" +source "drivers/vfio/Kconfig" + source "drivers/vlynq/Kconfig" source "drivers/virtio/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index bd36f09f2246..5b421840c48d 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_FUSION) += message/ obj-y += firewire/ obj-$(CONFIG_UIO) += uio/ +obj-$(CONFIG_VFIO) += vfio/ obj-y += cdrom/ obj-y += auxdisplay/ obj-$(CONFIG_PCCARD) += pcmcia/ diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig new file mode 100644 index 000000000000..9acb1e729bd6 --- /dev/null +++ b/drivers/vfio/Kconfig @@ -0,0 +1,8 @@ +menuconfig VFIO + tristate "VFIO Non-Privileged userspace driver framework" + depends on IOMMU_API + help + VFIO provides a framework for secure userspace device drivers. + See Documentation/vfio.txt for more details. + + If you don't know what to do here, say N. diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile new file mode 100644 index 000000000000..7500a67a42a0 --- /dev/null +++ b/drivers/vfio/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_VFIO) += vfio.o diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c new file mode 100644 index 000000000000..052e310aed72 --- /dev/null +++ b/drivers/vfio/vfio.c @@ -0,0 +1,1413 @@ +/* + * VFIO core + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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. + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "0.3" +#define DRIVER_AUTHOR "Alex Williamson " +#define DRIVER_DESC "VFIO - User Level meta-driver" + +static struct vfio { + struct class *class; + struct list_head iommu_drivers_list; + struct mutex iommu_drivers_lock; + struct list_head group_list; + struct idr group_idr; + struct mutex group_lock; + struct cdev group_cdev; + struct device *dev; + dev_t devt; + struct cdev cdev; + wait_queue_head_t release_q; +} vfio; + +struct vfio_iommu_driver { + const struct vfio_iommu_driver_ops *ops; + struct list_head vfio_next; +}; + +struct vfio_container { + struct kref kref; + struct list_head group_list; + struct mutex group_lock; + struct vfio_iommu_driver *iommu_driver; + void *iommu_data; +}; + +struct vfio_group { + struct kref kref; + int minor; + atomic_t container_users; + struct iommu_group *iommu_group; + struct vfio_container *container; + struct list_head device_list; + struct mutex device_lock; + struct device *dev; + struct notifier_block nb; + struct list_head vfio_next; + struct list_head container_next; +}; + +struct vfio_device { + struct kref kref; + struct device *dev; + const struct vfio_device_ops *ops; + struct vfio_group *group; + struct list_head group_next; + void *device_data; +}; + +/** + * IOMMU driver registration + */ +int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops) +{ + struct vfio_iommu_driver *driver, *tmp; + + driver = kzalloc(sizeof(*driver), GFP_KERNEL); + if (!driver) + return -ENOMEM; + + driver->ops = ops; + + mutex_lock(&vfio.iommu_drivers_lock); + + /* Check for duplicates */ + list_for_each_entry(tmp, &vfio.iommu_drivers_list, vfio_next) { + if (tmp->ops == ops) { + mutex_unlock(&vfio.iommu_drivers_lock); + kfree(driver); + return -EINVAL; + } + } + + list_add(&driver->vfio_next, &vfio.iommu_drivers_list); + + mutex_unlock(&vfio.iommu_drivers_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(vfio_register_iommu_driver); + +void vfio_unregister_iommu_driver(const struct vfio_iommu_driver_ops *ops) +{ + struct vfio_iommu_driver *driver; + + mutex_lock(&vfio.iommu_drivers_lock); + list_for_each_entry(driver, &vfio.iommu_drivers_list, vfio_next) { + if (driver->ops == ops) { + list_del(&driver->vfio_next); + mutex_unlock(&vfio.iommu_drivers_lock); + kfree(driver); + return; + } + } + mutex_unlock(&vfio.iommu_drivers_lock); +} +EXPORT_SYMBOL_GPL(vfio_unregister_iommu_driver); + +/** + * Group minor allocation/free - both called with vfio.group_lock held + */ +static int vfio_alloc_group_minor(struct vfio_group *group) +{ + int ret, minor; + +again: + if (unlikely(idr_pre_get(&vfio.group_idr, GFP_KERNEL) == 0)) + return -ENOMEM; + + /* index 0 is used by /dev/vfio/vfio */ + ret = idr_get_new_above(&vfio.group_idr, group, 1, &minor); + if (ret == -EAGAIN) + goto again; + if (ret || minor > MINORMASK) { + if (minor > MINORMASK) + idr_remove(&vfio.group_idr, minor); + return -ENOSPC; + } + + return minor; +} + +static void vfio_free_group_minor(int minor) +{ + idr_remove(&vfio.group_idr, minor); +} + +static int vfio_iommu_group_notifier(struct notifier_block *nb, + unsigned long action, void *data); +static void vfio_group_get(struct vfio_group *group); + +/** + * Container objects - containers are created when /dev/vfio/vfio is + * opened, but their lifecycle extends until the last user is done, so + * it's freed via kref. Must support container/group/device being + * closed in any order. + */ +static void vfio_container_get(struct vfio_container *container) +{ + kref_get(&container->kref); +} + +static void vfio_container_release(struct kref *kref) +{ + struct vfio_container *container; + container = container_of(kref, struct vfio_container, kref); + + kfree(container); +} + +static void vfio_container_put(struct vfio_container *container) +{ + kref_put(&container->kref, vfio_container_release); +} + +/** + * Group objects - create, release, get, put, search + */ +static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group) +{ + struct vfio_group *group, *tmp; + struct device *dev; + int ret, minor; + + group = kzalloc(sizeof(*group), GFP_KERNEL); + if (!group) + return ERR_PTR(-ENOMEM); + + kref_init(&group->kref); + INIT_LIST_HEAD(&group->device_list); + mutex_init(&group->device_lock); + atomic_set(&group->container_users, 0); + group->iommu_group = iommu_group; + + group->nb.notifier_call = vfio_iommu_group_notifier; + + /* + * blocking notifiers acquire a rwsem around registering and hold + * it around callback. Therefore, need to register outside of + * vfio.group_lock to avoid A-B/B-A contention. Our callback won't + * do anything unless it can find the group in vfio.group_list, so + * no harm in registering early. + */ + ret = iommu_group_register_notifier(iommu_group, &group->nb); + if (ret) { + kfree(group); + return ERR_PTR(ret); + } + + mutex_lock(&vfio.group_lock); + + minor = vfio_alloc_group_minor(group); + if (minor < 0) { + mutex_unlock(&vfio.group_lock); + kfree(group); + return ERR_PTR(minor); + } + + /* Did we race creating this group? */ + list_for_each_entry(tmp, &vfio.group_list, vfio_next) { + if (tmp->iommu_group == iommu_group) { + vfio_group_get(tmp); + vfio_free_group_minor(minor); + mutex_unlock(&vfio.group_lock); + kfree(group); + return tmp; + } + } + + dev = device_create(vfio.class, NULL, MKDEV(MAJOR(vfio.devt), minor), + group, "%d", iommu_group_id(iommu_group)); + if (IS_ERR(dev)) { + vfio_free_group_minor(minor); + mutex_unlock(&vfio.group_lock); + kfree(group); + return (struct vfio_group *)dev; /* ERR_PTR */ + } + + group->minor = minor; + group->dev = dev; + + list_add(&group->vfio_next, &vfio.group_list); + + mutex_unlock(&vfio.group_lock); + + return group; +} + +static void vfio_group_release(struct kref *kref) +{ + struct vfio_group *group = container_of(kref, struct vfio_group, kref); + + WARN_ON(!list_empty(&group->device_list)); + + device_destroy(vfio.class, MKDEV(MAJOR(vfio.devt), group->minor)); + list_del(&group->vfio_next); + vfio_free_group_minor(group->minor); + + mutex_unlock(&vfio.group_lock); + + /* + * Unregister outside of lock. A spurious callback is harmless now + * that the group is no longer in vfio.group_list. + */ + iommu_group_unregister_notifier(group->iommu_group, &group->nb); + + kfree(group); +} + +static void vfio_group_put(struct vfio_group *group) +{ + mutex_lock(&vfio.group_lock); + /* + * Release needs to unlock to unregister the notifier, so only + * unlock if not released. + */ + if (!kref_put(&group->kref, vfio_group_release)) + mutex_unlock(&vfio.group_lock); +} + +/* Assume group_lock or group reference is held */ +static void vfio_group_get(struct vfio_group *group) +{ + kref_get(&group->kref); +} + +/* + * Not really a try as we will sleep for mutex, but we need to make + * sure the group pointer is valid under lock and get a reference. + */ +static struct vfio_group *vfio_group_try_get(struct vfio_group *group) +{ + struct vfio_group *target = group; + + mutex_lock(&vfio.group_lock); + list_for_each_entry(group, &vfio.group_list, vfio_next) { + if (group == target) { + vfio_group_get(group); + mutex_unlock(&vfio.group_lock); + return group; + } + } + mutex_unlock(&vfio.group_lock); + + return NULL; +} + +static +struct vfio_group *vfio_group_get_from_iommu(struct iommu_group *iommu_group) +{ + struct vfio_group *group; + + mutex_lock(&vfio.group_lock); + list_for_each_entry(group, &vfio.group_list, vfio_next) { + if (group->iommu_group == iommu_group) { + vfio_group_get(group); + mutex_unlock(&vfio.group_lock); + return group; + } + } + mutex_unlock(&vfio.group_lock); + + return NULL; +} + +static struct vfio_group *vfio_group_get_from_minor(int minor) +{ + struct vfio_group *group; + + mutex_lock(&vfio.group_lock); + group = idr_find(&vfio.group_idr, minor); + if (!group) { + mutex_unlock(&vfio.group_lock); + return NULL; + } + vfio_group_get(group); + mutex_unlock(&vfio.group_lock); + + return group; +} + +/** + * Device objects - create, release, get, put, search + */ +static +struct vfio_device *vfio_group_create_device(struct vfio_group *group, + struct device *dev, + const struct vfio_device_ops *ops, + void *device_data) +{ + struct vfio_device *device; + int ret; + + device = kzalloc(sizeof(*device), GFP_KERNEL); + if (!device) + return ERR_PTR(-ENOMEM); + + kref_init(&device->kref); + device->dev = dev; + device->group = group; + device->ops = ops; + device->device_data = device_data; + + ret = dev_set_drvdata(dev, device); + if (ret) { + kfree(device); + return ERR_PTR(ret); + } + + /* No need to get group_lock, caller has group reference */ + vfio_group_get(group); + + mutex_lock(&group->device_lock); + list_add(&device->group_next, &group->device_list); + mutex_unlock(&group->device_lock); + + return device; +} + +static void vfio_device_release(struct kref *kref) +{ + struct vfio_device *device = container_of(kref, + struct vfio_device, kref); + struct vfio_group *group = device->group; + + mutex_lock(&group->device_lock); + list_del(&device->group_next); + mutex_unlock(&group->device_lock); + + dev_set_drvdata(device->dev, NULL); + + kfree(device); + + /* vfio_del_group_dev may be waiting for this device */ + wake_up(&vfio.release_q); +} + +/* Device reference always implies a group reference */ +static void vfio_device_put(struct vfio_device *device) +{ + kref_put(&device->kref, vfio_device_release); + vfio_group_put(device->group); +} + +static void vfio_device_get(struct vfio_device *device) +{ + vfio_group_get(device->group); + kref_get(&device->kref); +} + +static struct vfio_device *vfio_group_get_device(struct vfio_group *group, + struct device *dev) +{ + struct vfio_device *device; + + mutex_lock(&group->device_lock); + list_for_each_entry(device, &group->device_list, group_next) { + if (device->dev == dev) { + vfio_device_get(device); + mutex_unlock(&group->device_lock); + return device; + } + } + mutex_unlock(&group->device_lock); + return NULL; +} + +/* + * Whitelist some drivers that we know are safe (no dma) or just sit on + * a device. It's not always practical to leave a device within a group + * driverless as it could get re-bound to something unsafe. + */ +static const char * const vfio_driver_whitelist[] = { "pci-stub" }; + +static bool vfio_whitelisted_driver(struct device_driver *drv) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vfio_driver_whitelist); i++) { + if (!strcmp(drv->name, vfio_driver_whitelist[i])) + return true; + } + + return false; +} + +/* + * A vfio group is viable for use by userspace if all devices are either + * driver-less or bound to a vfio or whitelisted driver. We test the + * latter by the existence of a struct vfio_device matching the dev. + */ +static int vfio_dev_viable(struct device *dev, void *data) +{ + struct vfio_group *group = data; + struct vfio_device *device; + + if (!dev->driver || vfio_whitelisted_driver(dev->driver)) + return 0; + + device = vfio_group_get_device(group, dev); + if (device) { + vfio_device_put(device); + return 0; + } + + return -EINVAL; +} + +/** + * Async device support + */ +static int vfio_group_nb_add_dev(struct vfio_group *group, struct device *dev) +{ + struct vfio_device *device; + + /* Do we already know about it? We shouldn't */ + device = vfio_group_get_device(group, dev); + if (WARN_ON_ONCE(device)) { + vfio_device_put(device); + return 0; + } + + /* Nothing to do for idle groups */ + if (!atomic_read(&group->container_users)) + return 0; + + /* TODO Prevent device auto probing */ + WARN("Device %s added to live group %d!\n", dev_name(dev), + iommu_group_id(group->iommu_group)); + + return 0; +} + +static int vfio_group_nb_del_dev(struct vfio_group *group, struct device *dev) +{ + struct vfio_device *device; + + /* + * Expect to fall out here. If a device was in use, it would + * have been bound to a vfio sub-driver, which would have blocked + * in .remove at vfio_del_group_dev. Sanity check that we no + * longer track the device, so it's safe to remove. + */ + device = vfio_group_get_device(group, dev); + if (likely(!device)) + return 0; + + WARN("Device %s removed from live group %d!\n", dev_name(dev), + iommu_group_id(group->iommu_group)); + + vfio_device_put(device); + return 0; +} + +static int vfio_group_nb_verify(struct vfio_group *group, struct device *dev) +{ + /* We don't care what happens when the group isn't in use */ + if (!atomic_read(&group->container_users)) + return 0; + + return vfio_dev_viable(dev, group); +} + +static int vfio_iommu_group_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct vfio_group *group = container_of(nb, struct vfio_group, nb); + struct device *dev = data; + + /* + * Need to go through a group_lock lookup to get a reference or + * we risk racing a group being removed. Leave a WARN_ON for + * debuging, but if the group no longer exists, a spurious notify + * is harmless. + */ + group = vfio_group_try_get(group); + if (WARN_ON(!group)) + return NOTIFY_OK; + + switch (action) { + case IOMMU_GROUP_NOTIFY_ADD_DEVICE: + vfio_group_nb_add_dev(group, dev); + break; + case IOMMU_GROUP_NOTIFY_DEL_DEVICE: + vfio_group_nb_del_dev(group, dev); + break; + case IOMMU_GROUP_NOTIFY_BIND_DRIVER: + pr_debug("%s: Device %s, group %d binding to driver\n", + __func__, dev_name(dev), + iommu_group_id(group->iommu_group)); + break; + case IOMMU_GROUP_NOTIFY_BOUND_DRIVER: + pr_debug("%s: Device %s, group %d bound to driver %s\n", + __func__, dev_name(dev), + iommu_group_id(group->iommu_group), dev->driver->name); + BUG_ON(vfio_group_nb_verify(group, dev)); + break; + case IOMMU_GROUP_NOTIFY_UNBIND_DRIVER: + pr_debug("%s: Device %s, group %d unbinding from driver %s\n", + __func__, dev_name(dev), + iommu_group_id(group->iommu_group), dev->driver->name); + break; + case IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER: + pr_debug("%s: Device %s, group %d unbound from driver\n", + __func__, dev_name(dev), + iommu_group_id(group->iommu_group)); + /* + * XXX An unbound device in a live group is ok, but we'd + * really like to avoid the above BUG_ON by preventing other + * drivers from binding to it. Once that occurs, we have to + * stop the system to maintain isolation. At a minimum, we'd + * want a toggle to disable driver auto probe for this device. + */ + break; + } + + vfio_group_put(group); + return NOTIFY_OK; +} + +/** + * VFIO driver API + */ +int vfio_add_group_dev(struct device *dev, + const struct vfio_device_ops *ops, void *device_data) +{ + struct iommu_group *iommu_group; + struct vfio_group *group; + struct vfio_device *device; + + iommu_group = iommu_group_get(dev); + if (!iommu_group) + return -EINVAL; + + group = vfio_group_get_from_iommu(iommu_group); + if (!group) { + group = vfio_create_group(iommu_group); + if (IS_ERR(group)) { + iommu_group_put(iommu_group); + return PTR_ERR(group); + } + } + + device = vfio_group_get_device(group, dev); + if (device) { + WARN(1, "Device %s already exists on group %d\n", + dev_name(dev), iommu_group_id(iommu_group)); + vfio_device_put(device); + vfio_group_put(group); + iommu_group_put(iommu_group); + return -EBUSY; + } + + device = vfio_group_create_device(group, dev, ops, device_data); + if (IS_ERR(device)) { + vfio_group_put(group); + iommu_group_put(iommu_group); + return PTR_ERR(device); + } + + /* + * Added device holds reference to iommu_group and vfio_device + * (which in turn holds reference to vfio_group). Drop extra + * group reference used while acquiring device. + */ + vfio_group_put(group); + + return 0; +} +EXPORT_SYMBOL_GPL(vfio_add_group_dev); + +/* Test whether a struct device is present in our tracking */ +static bool vfio_dev_present(struct device *dev) +{ + struct iommu_group *iommu_group; + struct vfio_group *group; + struct vfio_device *device; + + iommu_group = iommu_group_get(dev); + if (!iommu_group) + return false; + + group = vfio_group_get_from_iommu(iommu_group); + if (!group) { + iommu_group_put(iommu_group); + return false; + } + + device = vfio_group_get_device(group, dev); + if (!device) { + vfio_group_put(group); + iommu_group_put(iommu_group); + return false; + } + + vfio_device_put(device); + vfio_group_put(group); + iommu_group_put(iommu_group); + return true; +} + +/* + * Decrement the device reference count and wait for the device to be + * removed. Open file descriptors for the device... */ +void *vfio_del_group_dev(struct device *dev) +{ + struct vfio_device *device = dev_get_drvdata(dev); + struct vfio_group *group = device->group; + struct iommu_group *iommu_group = group->iommu_group; + void *device_data = device->device_data; + + vfio_device_put(device); + + /* TODO send a signal to encourage this to be released */ + wait_event(vfio.release_q, !vfio_dev_present(dev)); + + iommu_group_put(iommu_group); + + return device_data; +} +EXPORT_SYMBOL_GPL(vfio_del_group_dev); + +/** + * VFIO base fd, /dev/vfio/vfio + */ +static long vfio_ioctl_check_extension(struct vfio_container *container, + unsigned long arg) +{ + struct vfio_iommu_driver *driver = container->iommu_driver; + long ret = 0; + + switch (arg) { + /* No base extensions yet */ + default: + /* + * If no driver is set, poll all registered drivers for + * extensions and return the first positive result. If + * a driver is already set, further queries will be passed + * only to that driver. + */ + if (!driver) { + mutex_lock(&vfio.iommu_drivers_lock); + list_for_each_entry(driver, &vfio.iommu_drivers_list, + vfio_next) { + if (!try_module_get(driver->ops->owner)) + continue; + + ret = driver->ops->ioctl(NULL, + VFIO_CHECK_EXTENSION, + arg); + module_put(driver->ops->owner); + if (ret > 0) + break; + } + mutex_unlock(&vfio.iommu_drivers_lock); + } else + ret = driver->ops->ioctl(container->iommu_data, + VFIO_CHECK_EXTENSION, arg); + } + + return ret; +} + +/* hold container->group_lock */ +static int __vfio_container_attach_groups(struct vfio_container *container, + struct vfio_iommu_driver *driver, + void *data) +{ + struct vfio_group *group; + int ret = -ENODEV; + + list_for_each_entry(group, &container->group_list, container_next) { + ret = driver->ops->attach_group(data, group->iommu_group); + if (ret) + goto unwind; + } + + return ret; + +unwind: + list_for_each_entry_continue_reverse(group, &container->group_list, + container_next) { + driver->ops->detach_group(data, group->iommu_group); + } + + return ret; +} + +static long vfio_ioctl_set_iommu(struct vfio_container *container, + unsigned long arg) +{ + struct vfio_iommu_driver *driver; + long ret = -ENODEV; + + mutex_lock(&container->group_lock); + + /* + * The container is designed to be an unprivileged interface while + * the group can be assigned to specific users. Therefore, only by + * adding a group to a container does the user get the privilege of + * enabling the iommu, which may allocate finite resources. There + * is no unset_iommu, but by removing all the groups from a container, + * the container is deprivileged and returns to an unset state. + */ + if (list_empty(&container->group_list) || container->iommu_driver) { + mutex_unlock(&container->group_lock); + return -EINVAL; + } + + mutex_lock(&vfio.iommu_drivers_lock); + list_for_each_entry(driver, &vfio.iommu_drivers_list, vfio_next) { + void *data; + + if (!try_module_get(driver->ops->owner)) + continue; + + /* + * The arg magic for SET_IOMMU is the same as CHECK_EXTENSION, + * so test which iommu driver reported support for this + * extension and call open on them. We also pass them the + * magic, allowing a single driver to support multiple + * interfaces if they'd like. + */ + if (driver->ops->ioctl(NULL, VFIO_CHECK_EXTENSION, arg) <= 0) { + module_put(driver->ops->owner); + continue; + } + + /* module reference holds the driver we're working on */ + mutex_unlock(&vfio.iommu_drivers_lock); + + data = driver->ops->open(arg); + if (IS_ERR(data)) { + ret = PTR_ERR(data); + module_put(driver->ops->owner); + goto skip_drivers_unlock; + } + + ret = __vfio_container_attach_groups(container, driver, data); + if (!ret) { + container->iommu_driver = driver; + container->iommu_data = data; + } else { + driver->ops->release(data); + module_put(driver->ops->owner); + } + + goto skip_drivers_unlock; + } + + mutex_unlock(&vfio.iommu_drivers_lock); +skip_drivers_unlock: + mutex_unlock(&container->group_lock); + + return ret; +} + +static long vfio_fops_unl_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + struct vfio_container *container = filep->private_data; + struct vfio_iommu_driver *driver; + void *data; + long ret = -EINVAL; + + if (!container) + return ret; + + driver = container->iommu_driver; + data = container->iommu_data; + + switch (cmd) { + case VFIO_GET_API_VERSION: + ret = VFIO_API_VERSION; + break; + case VFIO_CHECK_EXTENSION: + ret = vfio_ioctl_check_extension(container, arg); + break; + case VFIO_SET_IOMMU: + ret = vfio_ioctl_set_iommu(container, arg); + break; + default: + if (driver) /* passthrough all unrecognized ioctls */ + ret = driver->ops->ioctl(data, cmd, arg); + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long vfio_fops_compat_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + arg = (unsigned long)compat_ptr(arg); + return vfio_fops_unl_ioctl(filep, cmd, arg); +} +#endif /* CONFIG_COMPAT */ + +static int vfio_fops_open(struct inode *inode, struct file *filep) +{ + struct vfio_container *container; + + container = kzalloc(sizeof(*container), GFP_KERNEL); + if (!container) + return -ENOMEM; + + INIT_LIST_HEAD(&container->group_list); + mutex_init(&container->group_lock); + kref_init(&container->kref); + + filep->private_data = container; + + return 0; +} + +static int vfio_fops_release(struct inode *inode, struct file *filep) +{ + struct vfio_container *container = filep->private_data; + + filep->private_data = NULL; + + vfio_container_put(container); + + return 0; +} + +/* + * Once an iommu driver is set, we optionally pass read/write/mmap + * on to the driver, allowing management interfaces beyond ioctl. + */ +static ssize_t vfio_fops_read(struct file *filep, char __user *buf, + size_t count, loff_t *ppos) +{ + struct vfio_container *container = filep->private_data; + struct vfio_iommu_driver *driver = container->iommu_driver; + + if (unlikely(!driver || !driver->ops->read)) + return -EINVAL; + + return driver->ops->read(container->iommu_data, buf, count, ppos); +} + +static ssize_t vfio_fops_write(struct file *filep, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct vfio_container *container = filep->private_data; + struct vfio_iommu_driver *driver = container->iommu_driver; + + if (unlikely(!driver || !driver->ops->write)) + return -EINVAL; + + return driver->ops->write(container->iommu_data, buf, count, ppos); +} + +static int vfio_fops_mmap(struct file *filep, struct vm_area_struct *vma) +{ + struct vfio_container *container = filep->private_data; + struct vfio_iommu_driver *driver = container->iommu_driver; + + if (unlikely(!driver || !driver->ops->mmap)) + return -EINVAL; + + return driver->ops->mmap(container->iommu_data, vma); +} + +static const struct file_operations vfio_fops = { + .owner = THIS_MODULE, + .open = vfio_fops_open, + .release = vfio_fops_release, + .read = vfio_fops_read, + .write = vfio_fops_write, + .unlocked_ioctl = vfio_fops_unl_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vfio_fops_compat_ioctl, +#endif + .mmap = vfio_fops_mmap, +}; + +/** + * VFIO Group fd, /dev/vfio/$GROUP + */ +static void __vfio_group_unset_container(struct vfio_group *group) +{ + struct vfio_container *container = group->container; + struct vfio_iommu_driver *driver; + + mutex_lock(&container->group_lock); + + driver = container->iommu_driver; + if (driver) + driver->ops->detach_group(container->iommu_data, + group->iommu_group); + + group->container = NULL; + list_del(&group->container_next); + + /* Detaching the last group deprivileges a container, remove iommu */ + if (driver && list_empty(&container->group_list)) { + driver->ops->release(container->iommu_data); + module_put(driver->ops->owner); + container->iommu_driver = NULL; + container->iommu_data = NULL; + } + + mutex_unlock(&container->group_lock); + + vfio_container_put(container); +} + +/* + * VFIO_GROUP_UNSET_CONTAINER should fail if there are other users or + * if there was no container to unset. Since the ioctl is called on + * the group, we know that still exists, therefore the only valid + * transition here is 1->0. + */ +static int vfio_group_unset_container(struct vfio_group *group) +{ + int users = atomic_cmpxchg(&group->container_users, 1, 0); + + if (!users) + return -EINVAL; + if (users != 1) + return -EBUSY; + + __vfio_group_unset_container(group); + + return 0; +} + +/* + * When removing container users, anything that removes the last user + * implicitly removes the group from the container. That is, if the + * group file descriptor is closed, as well as any device file descriptors, + * the group is free. + */ +static void vfio_group_try_dissolve_container(struct vfio_group *group) +{ + if (0 == atomic_dec_if_positive(&group->container_users)) + __vfio_group_unset_container(group); +} + +static int vfio_group_set_container(struct vfio_group *group, int container_fd) +{ + struct file *filep; + struct vfio_container *container; + struct vfio_iommu_driver *driver; + int ret = 0; + + if (atomic_read(&group->container_users)) + return -EINVAL; + + filep = fget(container_fd); + if (!filep) + return -EBADF; + + /* Sanity check, is this really our fd? */ + if (filep->f_op != &vfio_fops) { + fput(filep); + return -EINVAL; + } + + container = filep->private_data; + WARN_ON(!container); /* fget ensures we don't race vfio_release */ + + mutex_lock(&container->group_lock); + + driver = container->iommu_driver; + if (driver) { + ret = driver->ops->attach_group(container->iommu_data, + group->iommu_group); + if (ret) + goto unlock_out; + } + + group->container = container; + list_add(&group->container_next, &container->group_list); + + /* Get a reference on the container and mark a user within the group */ + vfio_container_get(container); + atomic_inc(&group->container_users); + +unlock_out: + mutex_unlock(&container->group_lock); + fput(filep); + + return ret; +} + +static bool vfio_group_viable(struct vfio_group *group) +{ + return (iommu_group_for_each_dev(group->iommu_group, + group, vfio_dev_viable) == 0); +} + +static const struct file_operations vfio_device_fops; + +static int vfio_group_get_device_fd(struct vfio_group *group, char *buf) +{ + struct vfio_device *device; + struct file *filep; + int ret = -ENODEV; + + if (0 == atomic_read(&group->container_users) || + !group->container->iommu_driver || !vfio_group_viable(group)) + return -EINVAL; + + mutex_lock(&group->device_lock); + list_for_each_entry(device, &group->device_list, group_next) { + if (strcmp(dev_name(device->dev), buf)) + continue; + + ret = device->ops->open(device->device_data); + if (ret) + break; + /* + * We can't use anon_inode_getfd() because we need to modify + * the f_mode flags directly to allow more than just ioctls + */ + ret = get_unused_fd(); + if (ret < 0) { + device->ops->release(device->device_data); + break; + } + + filep = anon_inode_getfile("[vfio-device]", &vfio_device_fops, + device, O_RDWR); + if (IS_ERR(filep)) { + put_unused_fd(ret); + ret = PTR_ERR(filep); + device->ops->release(device->device_data); + break; + } + + /* + * TODO: add an anon_inode interface to do this. + * Appears to be missing by lack of need rather than + * explicitly prevented. Now there's need. + */ + filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + + fd_install(ret, filep); + + vfio_device_get(device); + atomic_inc(&group->container_users); + break; + } + mutex_unlock(&group->device_lock); + + return ret; +} + +static long vfio_group_fops_unl_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + struct vfio_group *group = filep->private_data; + long ret = -ENOTTY; + + switch (cmd) { + case VFIO_GROUP_GET_STATUS: + { + struct vfio_group_status status; + unsigned long minsz; + + minsz = offsetofend(struct vfio_group_status, flags); + + if (copy_from_user(&status, (void __user *)arg, minsz)) + return -EFAULT; + + if (status.argsz < minsz) + return -EINVAL; + + status.flags = 0; + + if (vfio_group_viable(group)) + status.flags |= VFIO_GROUP_FLAGS_VIABLE; + + if (group->container) + status.flags |= VFIO_GROUP_FLAGS_CONTAINER_SET; + + if (copy_to_user((void __user *)arg, &status, minsz)) + return -EFAULT; + + ret = 0; + break; + } + case VFIO_GROUP_SET_CONTAINER: + { + int fd; + + if (get_user(fd, (int __user *)arg)) + return -EFAULT; + + if (fd < 0) + return -EINVAL; + + ret = vfio_group_set_container(group, fd); + break; + } + case VFIO_GROUP_UNSET_CONTAINER: + ret = vfio_group_unset_container(group); + break; + case VFIO_GROUP_GET_DEVICE_FD: + { + char *buf; + + buf = strndup_user((const char __user *)arg, PAGE_SIZE); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + ret = vfio_group_get_device_fd(group, buf); + kfree(buf); + break; + } + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long vfio_group_fops_compat_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + arg = (unsigned long)compat_ptr(arg); + return vfio_group_fops_unl_ioctl(filep, cmd, arg); +} +#endif /* CONFIG_COMPAT */ + +static int vfio_group_fops_open(struct inode *inode, struct file *filep) +{ + struct vfio_group *group; + + group = vfio_group_get_from_minor(iminor(inode)); + if (!group) + return -ENODEV; + + if (group->container) { + vfio_group_put(group); + return -EBUSY; + } + + filep->private_data = group; + + return 0; +} + +static int vfio_group_fops_release(struct inode *inode, struct file *filep) +{ + struct vfio_group *group = filep->private_data; + + filep->private_data = NULL; + + vfio_group_try_dissolve_container(group); + + vfio_group_put(group); + + return 0; +} + +static const struct file_operations vfio_group_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = vfio_group_fops_unl_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vfio_group_fops_compat_ioctl, +#endif + .open = vfio_group_fops_open, + .release = vfio_group_fops_release, +}; + +/** + * VFIO Device fd + */ +static int vfio_device_fops_release(struct inode *inode, struct file *filep) +{ + struct vfio_device *device = filep->private_data; + + device->ops->release(device->device_data); + + vfio_group_try_dissolve_container(device->group); + + vfio_device_put(device); + + return 0; +} + +static long vfio_device_fops_unl_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + struct vfio_device *device = filep->private_data; + + if (unlikely(!device->ops->ioctl)) + return -EINVAL; + + return device->ops->ioctl(device->device_data, cmd, arg); +} + +static ssize_t vfio_device_fops_read(struct file *filep, char __user *buf, + size_t count, loff_t *ppos) +{ + struct vfio_device *device = filep->private_data; + + if (unlikely(!device->ops->read)) + return -EINVAL; + + return device->ops->read(device->device_data, buf, count, ppos); +} + +static ssize_t vfio_device_fops_write(struct file *filep, + const char __user *buf, + size_t count, loff_t *ppos) +{ + struct vfio_device *device = filep->private_data; + + if (unlikely(!device->ops->write)) + return -EINVAL; + + return device->ops->write(device->device_data, buf, count, ppos); +} + +static int vfio_device_fops_mmap(struct file *filep, struct vm_area_struct *vma) +{ + struct vfio_device *device = filep->private_data; + + if (unlikely(!device->ops->mmap)) + return -EINVAL; + + return device->ops->mmap(device->device_data, vma); +} + +#ifdef CONFIG_COMPAT +static long vfio_device_fops_compat_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + arg = (unsigned long)compat_ptr(arg); + return vfio_device_fops_unl_ioctl(filep, cmd, arg); +} +#endif /* CONFIG_COMPAT */ + +static const struct file_operations vfio_device_fops = { + .owner = THIS_MODULE, + .release = vfio_device_fops_release, + .read = vfio_device_fops_read, + .write = vfio_device_fops_write, + .unlocked_ioctl = vfio_device_fops_unl_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vfio_device_fops_compat_ioctl, +#endif + .mmap = vfio_device_fops_mmap, +}; + +/** + * Module/class support + */ +static char *vfio_devnode(struct device *dev, umode_t *mode) +{ + return kasprintf(GFP_KERNEL, "vfio/%s", dev_name(dev)); +} + +static int __init vfio_init(void) +{ + int ret; + + idr_init(&vfio.group_idr); + mutex_init(&vfio.group_lock); + mutex_init(&vfio.iommu_drivers_lock); + INIT_LIST_HEAD(&vfio.group_list); + INIT_LIST_HEAD(&vfio.iommu_drivers_list); + init_waitqueue_head(&vfio.release_q); + + vfio.class = class_create(THIS_MODULE, "vfio"); + if (IS_ERR(vfio.class)) { + ret = PTR_ERR(vfio.class); + goto err_class; + } + + vfio.class->devnode = vfio_devnode; + + ret = alloc_chrdev_region(&vfio.devt, 0, MINORMASK, "vfio"); + if (ret) + goto err_base_chrdev; + + cdev_init(&vfio.cdev, &vfio_fops); + ret = cdev_add(&vfio.cdev, vfio.devt, 1); + if (ret) + goto err_base_cdev; + + vfio.dev = device_create(vfio.class, NULL, vfio.devt, NULL, "vfio"); + if (IS_ERR(vfio.dev)) { + ret = PTR_ERR(vfio.dev); + goto err_base_dev; + } + + /* /dev/vfio/$GROUP */ + cdev_init(&vfio.group_cdev, &vfio_group_fops); + ret = cdev_add(&vfio.group_cdev, + MKDEV(MAJOR(vfio.devt), 1), MINORMASK - 1); + if (ret) + goto err_groups_cdev; + + pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); + + return 0; + +err_groups_cdev: + device_destroy(vfio.class, vfio.devt); +err_base_dev: + cdev_del(&vfio.cdev); +err_base_cdev: + unregister_chrdev_region(vfio.devt, MINORMASK); +err_base_chrdev: + class_destroy(vfio.class); + vfio.class = NULL; +err_class: + return ret; +} + +static void __exit vfio_cleanup(void) +{ + WARN_ON(!list_empty(&vfio.group_list)); + + idr_destroy(&vfio.group_idr); + cdev_del(&vfio.group_cdev); + device_destroy(vfio.class, vfio.devt); + cdev_del(&vfio.cdev); + unregister_chrdev_region(vfio.devt, MINORMASK); + class_destroy(vfio.class); + vfio.class = NULL; +} + +module_init(vfio_init); +module_exit(vfio_cleanup); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/include/linux/vfio.h b/include/linux/vfio.h new file mode 100644 index 000000000000..03e56a5154b6 --- /dev/null +++ b/include/linux/vfio.h @@ -0,0 +1,367 @@ +/* + * VFIO API definition + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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 VFIO_H +#define VFIO_H + +#include +#include + +#define VFIO_API_VERSION 0 + +#ifdef __KERNEL__ /* Internal VFIO-core/bus driver API */ + +#include +#include + +/** + * struct vfio_device_ops - VFIO bus driver device callbacks + * + * @open: Called when userspace creates new file descriptor for device + * @release: Called when userspace releases file descriptor for device + * @read: Perform read(2) on device file descriptor + * @write: Perform write(2) on device file descriptor + * @ioctl: Perform ioctl(2) on device file descriptor, supporting VFIO_DEVICE_* + * operations documented below + * @mmap: Perform mmap(2) on a region of the device file descriptor + */ +struct vfio_device_ops { + char *name; + int (*open)(void *device_data); + void (*release)(void *device_data); + ssize_t (*read)(void *device_data, char __user *buf, + size_t count, loff_t *ppos); + ssize_t (*write)(void *device_data, const char __user *buf, + size_t count, loff_t *size); + long (*ioctl)(void *device_data, unsigned int cmd, + unsigned long arg); + int (*mmap)(void *device_data, struct vm_area_struct *vma); +}; + +extern int vfio_add_group_dev(struct device *dev, + const struct vfio_device_ops *ops, + void *device_data); + +extern void *vfio_del_group_dev(struct device *dev); + +/** + * struct vfio_iommu_driver_ops - VFIO IOMMU driver callbacks + */ +struct vfio_iommu_driver_ops { + char *name; + struct module *owner; + void *(*open)(unsigned long arg); + void (*release)(void *iommu_data); + ssize_t (*read)(void *iommu_data, char __user *buf, + size_t count, loff_t *ppos); + ssize_t (*write)(void *iommu_data, const char __user *buf, + size_t count, loff_t *size); + long (*ioctl)(void *iommu_data, unsigned int cmd, + unsigned long arg); + int (*mmap)(void *iommu_data, struct vm_area_struct *vma); + int (*attach_group)(void *iommu_data, + struct iommu_group *group); + void (*detach_group)(void *iommu_data, + struct iommu_group *group); + +}; + +extern int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops); + +extern void vfio_unregister_iommu_driver( + const struct vfio_iommu_driver_ops *ops); + +/** + * offsetofend(TYPE, MEMBER) + * + * @TYPE: The type of the structure + * @MEMBER: The member within the structure to get the end offset of + * + * Simple helper macro for dealing with variable sized structures passed + * from user space. This allows us to easily determine if the provided + * structure is sized to include various fields. + */ +#define offsetofend(TYPE, MEMBER) ({ \ + TYPE tmp; \ + offsetof(TYPE, MEMBER) + sizeof(tmp.MEMBER); }) \ + +#endif /* __KERNEL__ */ + +/* Kernel & User level defines for VFIO IOCTLs. */ + +/* Extensions */ + +/* None yet */ + +/* + * The IOCTL interface is designed for extensibility by embedding the + * structure length (argsz) and flags into structures passed between + * kernel and userspace. We therefore use the _IO() macro for these + * defines to avoid implicitly embedding a size into the ioctl request. + * As structure fields are added, argsz will increase to match and flag + * bits will be defined to indicate additional fields with valid data. + * It's *always* the caller's responsibility to indicate the size of + * the structure passed by setting argsz appropriately. + */ + +#define VFIO_TYPE (';') +#define VFIO_BASE 100 + +/* -------- IOCTLs for VFIO file descriptor (/dev/vfio/vfio) -------- */ + +/** + * VFIO_GET_API_VERSION - _IO(VFIO_TYPE, VFIO_BASE + 0) + * + * Report the version of the VFIO API. This allows us to bump the entire + * API version should we later need to add or change features in incompatible + * ways. + * Return: VFIO_API_VERSION + * Availability: Always + */ +#define VFIO_GET_API_VERSION _IO(VFIO_TYPE, VFIO_BASE + 0) + +/** + * VFIO_CHECK_EXTENSION - _IOW(VFIO_TYPE, VFIO_BASE + 1, __u32) + * + * Check whether an extension is supported. + * Return: 0 if not supported, 1 (or some other positive integer) if supported. + * Availability: Always + */ +#define VFIO_CHECK_EXTENSION _IO(VFIO_TYPE, VFIO_BASE + 1) + +/** + * VFIO_SET_IOMMU - _IOW(VFIO_TYPE, VFIO_BASE + 2, __s32) + * + * Set the iommu to the given type. The type must be supported by an + * iommu driver as verified by calling CHECK_EXTENSION using the same + * type. A group must be set to this file descriptor before this + * ioctl is available. The IOMMU interfaces enabled by this call are + * specific to the value set. + * Return: 0 on success, -errno on failure + * Availability: When VFIO group attached + */ +#define VFIO_SET_IOMMU _IO(VFIO_TYPE, VFIO_BASE + 2) + +/* -------- IOCTLs for GROUP file descriptors (/dev/vfio/$GROUP) -------- */ + +/** + * VFIO_GROUP_GET_STATUS - _IOR(VFIO_TYPE, VFIO_BASE + 3, + * struct vfio_group_status) + * + * Retrieve information about the group. Fills in provided + * struct vfio_group_info. Caller sets argsz. + * Return: 0 on succes, -errno on failure. + * Availability: Always + */ +struct vfio_group_status { + __u32 argsz; + __u32 flags; +#define VFIO_GROUP_FLAGS_VIABLE (1 << 0) +#define VFIO_GROUP_FLAGS_CONTAINER_SET (1 << 1) +}; +#define VFIO_GROUP_GET_STATUS _IO(VFIO_TYPE, VFIO_BASE + 3) + +/** + * VFIO_GROUP_SET_CONTAINER - _IOW(VFIO_TYPE, VFIO_BASE + 4, __s32) + * + * Set the container for the VFIO group to the open VFIO file + * descriptor provided. Groups may only belong to a single + * container. Containers may, at their discretion, support multiple + * groups. Only when a container is set are all of the interfaces + * of the VFIO file descriptor and the VFIO group file descriptor + * available to the user. + * Return: 0 on success, -errno on failure. + * Availability: Always + */ +#define VFIO_GROUP_SET_CONTAINER _IO(VFIO_TYPE, VFIO_BASE + 4) + +/** + * VFIO_GROUP_UNSET_CONTAINER - _IO(VFIO_TYPE, VFIO_BASE + 5) + * + * Remove the group from the attached container. This is the + * opposite of the SET_CONTAINER call and returns the group to + * an initial state. All device file descriptors must be released + * prior to calling this interface. When removing the last group + * from a container, the IOMMU will be disabled and all state lost, + * effectively also returning the VFIO file descriptor to an initial + * state. + * Return: 0 on success, -errno on failure. + * Availability: When attached to container + */ +#define VFIO_GROUP_UNSET_CONTAINER _IO(VFIO_TYPE, VFIO_BASE + 5) + +/** + * VFIO_GROUP_GET_DEVICE_FD - _IOW(VFIO_TYPE, VFIO_BASE + 6, char) + * + * Return a new file descriptor for the device object described by + * the provided string. The string should match a device listed in + * the devices subdirectory of the IOMMU group sysfs entry. The + * group containing the device must already be added to this context. + * Return: new file descriptor on success, -errno on failure. + * Availability: When attached to container + */ +#define VFIO_GROUP_GET_DEVICE_FD _IO(VFIO_TYPE, VFIO_BASE + 6) + +/* --------------- IOCTLs for DEVICE file descriptors --------------- */ + +/** + * VFIO_DEVICE_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 7, + * struct vfio_device_info) + * + * Retrieve information about the device. Fills in provided + * struct vfio_device_info. Caller sets argsz. + * Return: 0 on success, -errno on failure. + */ +struct vfio_device_info { + __u32 argsz; + __u32 flags; +#define VFIO_DEVICE_FLAGS_RESET (1 << 0) /* Device supports reset */ + __u32 num_regions; /* Max region index + 1 */ + __u32 num_irqs; /* Max IRQ index + 1 */ +}; +#define VFIO_DEVICE_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 7) + +/** + * VFIO_DEVICE_GET_REGION_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 8, + * struct vfio_region_info) + * + * Retrieve information about a device region. Caller provides + * struct vfio_region_info with index value set. Caller sets argsz. + * Implementation of region mapping is bus driver specific. This is + * intended to describe MMIO, I/O port, as well as bus specific + * regions (ex. PCI config space). Zero sized regions may be used + * to describe unimplemented regions (ex. unimplemented PCI BARs). + * Return: 0 on success, -errno on failure. + */ +struct vfio_region_info { + __u32 argsz; + __u32 flags; +#define VFIO_REGION_INFO_FLAG_READ (1 << 0) /* Region supports read */ +#define VFIO_REGION_INFO_FLAG_WRITE (1 << 1) /* Region supports write */ +#define VFIO_REGION_INFO_FLAG_MMAP (1 << 2) /* Region supports mmap */ + __u32 index; /* Region index */ + __u32 resv; /* Reserved for alignment */ + __u64 size; /* Region size (bytes) */ + __u64 offset; /* Region offset from start of device fd */ +}; +#define VFIO_DEVICE_GET_REGION_INFO _IO(VFIO_TYPE, VFIO_BASE + 8) + +/** + * VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9, + * struct vfio_irq_info) + * + * Retrieve information about a device IRQ. Caller provides + * struct vfio_irq_info with index value set. Caller sets argsz. + * Implementation of IRQ mapping is bus driver specific. Indexes + * using multiple IRQs are primarily intended to support MSI-like + * interrupt blocks. Zero count irq blocks may be used to describe + * unimplemented interrupt types. + * + * The EVENTFD flag indicates the interrupt index supports eventfd based + * signaling. + * + * The MASKABLE flags indicates the index supports MASK and UNMASK + * actions described below. + * + * AUTOMASKED indicates that after signaling, the interrupt line is + * automatically masked by VFIO and the user needs to unmask the line + * to receive new interrupts. This is primarily intended to distinguish + * level triggered interrupts. + * + * The NORESIZE flag indicates that the interrupt lines within the index + * are setup as a set and new subindexes cannot be enabled without first + * disabling the entire index. This is used for interrupts like PCI MSI + * and MSI-X where the driver may only use a subset of the available + * indexes, but VFIO needs to enable a specific number of vectors + * upfront. In the case of MSI-X, where the user can enable MSI-X and + * then add and unmask vectors, it's up to userspace to make the decision + * whether to allocate the maximum supported number of vectors or tear + * down setup and incrementally increase the vectors as each is enabled. + */ +struct vfio_irq_info { + __u32 argsz; + __u32 flags; +#define VFIO_IRQ_INFO_EVENTFD (1 << 0) +#define VFIO_IRQ_INFO_MASKABLE (1 << 1) +#define VFIO_IRQ_INFO_AUTOMASKED (1 << 2) +#define VFIO_IRQ_INFO_NORESIZE (1 << 3) + __u32 index; /* IRQ index */ + __u32 count; /* Number of IRQs within this index */ +}; +#define VFIO_DEVICE_GET_IRQ_INFO _IO(VFIO_TYPE, VFIO_BASE + 9) + +/** + * VFIO_DEVICE_SET_IRQS - _IOW(VFIO_TYPE, VFIO_BASE + 10, struct vfio_irq_set) + * + * Set signaling, masking, and unmasking of interrupts. Caller provides + * struct vfio_irq_set with all fields set. 'start' and 'count' indicate + * the range of subindexes being specified. + * + * The DATA flags specify the type of data provided. If DATA_NONE, the + * operation performs the specified action immediately on the specified + * interrupt(s). For example, to unmask AUTOMASKED interrupt [0,0]: + * flags = (DATA_NONE|ACTION_UNMASK), index = 0, start = 0, count = 1. + * + * DATA_BOOL allows sparse support for the same on arrays of interrupts. + * For example, to mask interrupts [0,1] and [0,3] (but not [0,2]): + * flags = (DATA_BOOL|ACTION_MASK), index = 0, start = 1, count = 3, + * data = {1,0,1} + * + * DATA_EVENTFD binds the specified ACTION to the provided __s32 eventfd. + * A value of -1 can be used to either de-assign interrupts if already + * assigned or skip un-assigned interrupts. For example, to set an eventfd + * to be trigger for interrupts [0,0] and [0,2]: + * flags = (DATA_EVENTFD|ACTION_TRIGGER), index = 0, start = 0, count = 3, + * data = {fd1, -1, fd2} + * If index [0,1] is previously set, two count = 1 ioctls calls would be + * required to set [0,0] and [0,2] without changing [0,1]. + * + * Once a signaling mechanism is set, DATA_BOOL or DATA_NONE can be used + * with ACTION_TRIGGER to perform kernel level interrupt loopback testing + * from userspace (ie. simulate hardware triggering). + * + * Setting of an event triggering mechanism to userspace for ACTION_TRIGGER + * enables the interrupt index for the device. Individual subindex interrupts + * can be disabled using the -1 value for DATA_EVENTFD or the index can be + * disabled as a whole with: flags = (DATA_NONE|ACTION_TRIGGER), count = 0. + * + * Note that ACTION_[UN]MASK specify user->kernel signaling (irqfds) while + * ACTION_TRIGGER specifies kernel->user signaling. + */ +struct vfio_irq_set { + __u32 argsz; + __u32 flags; +#define VFIO_IRQ_SET_DATA_NONE (1 << 0) /* Data not present */ +#define VFIO_IRQ_SET_DATA_BOOL (1 << 1) /* Data is bool (u8) */ +#define VFIO_IRQ_SET_DATA_EVENTFD (1 << 2) /* Data is eventfd (s32) */ +#define VFIO_IRQ_SET_ACTION_MASK (1 << 3) /* Mask interrupt */ +#define VFIO_IRQ_SET_ACTION_UNMASK (1 << 4) /* Unmask interrupt */ +#define VFIO_IRQ_SET_ACTION_TRIGGER (1 << 5) /* Trigger interrupt */ + __u32 index; + __u32 start; + __u32 count; + __u8 data[]; +}; +#define VFIO_DEVICE_SET_IRQS _IO(VFIO_TYPE, VFIO_BASE + 10) + +#define VFIO_IRQ_SET_DATA_TYPE_MASK (VFIO_IRQ_SET_DATA_NONE | \ + VFIO_IRQ_SET_DATA_BOOL | \ + VFIO_IRQ_SET_DATA_EVENTFD) +#define VFIO_IRQ_SET_ACTION_TYPE_MASK (VFIO_IRQ_SET_ACTION_MASK | \ + VFIO_IRQ_SET_ACTION_UNMASK | \ + VFIO_IRQ_SET_ACTION_TRIGGER) +/** + * VFIO_DEVICE_RESET - _IO(VFIO_TYPE, VFIO_BASE + 11) + * + * Reset a device. + */ +#define VFIO_DEVICE_RESET _IO(VFIO_TYPE, VFIO_BASE + 11) + +#endif /* VFIO_H */ -- cgit v1.2.3 From 73fa0d10d077d9521ee2dace2307ae2c9a965336 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 31 Jul 2012 08:16:23 -0600 Subject: vfio: Type1 IOMMU implementation This VFIO IOMMU backend is designed primarily for AMD-Vi and Intel VT-d hardware, but is potentially usable by anything supporting similar mapping functionality. We arbitrarily call this a Type1 backend for lack of a better name. This backend has no IOVA or host memory mapping restrictions for the user and is optimized for relatively static mappings. Mapped areas are pinned into system memory. Signed-off-by: Alex Williamson --- drivers/vfio/Kconfig | 6 + drivers/vfio/Makefile | 2 + drivers/vfio/vfio.c | 7 + drivers/vfio/vfio_iommu_type1.c | 753 ++++++++++++++++++++++++++++++++++++++++ include/linux/vfio.h | 54 ++- 5 files changed, 821 insertions(+), 1 deletion(-) create mode 100644 drivers/vfio/vfio_iommu_type1.c (limited to 'include') diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index 9acb1e729bd6..128b97910b8e 100644 --- a/drivers/vfio/Kconfig +++ b/drivers/vfio/Kconfig @@ -1,6 +1,12 @@ +config VFIO_IOMMU_TYPE1 + tristate + depends on VFIO + default n + menuconfig VFIO tristate "VFIO Non-Privileged userspace driver framework" depends on IOMMU_API + select VFIO_IOMMU_TYPE1 if X86 help VFIO provides a framework for secure userspace device drivers. See Documentation/vfio.txt for more details. diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile index 7500a67a42a0..2398d4a0e38b 100644 --- a/drivers/vfio/Makefile +++ b/drivers/vfio/Makefile @@ -1 +1,3 @@ obj-$(CONFIG_VFIO) += vfio.o +obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o +obj-$(CONFIG_VFIO_PCI) += pci/ diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 052e310aed72..9591e2b509d7 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -1376,6 +1376,13 @@ static int __init vfio_init(void) pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); + /* + * Attempt to load known iommu-drivers. This gives us a working + * environment without the user needing to explicitly load iommu + * drivers. + */ + request_module_nowait("vfio_iommu_type1"); + return 0; err_groups_cdev: diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c new file mode 100644 index 000000000000..6f3fbc48a6c7 --- /dev/null +++ b/drivers/vfio/vfio_iommu_type1.c @@ -0,0 +1,753 @@ +/* + * VFIO: IOMMU DMA mapping support for Type1 IOMMU + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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. + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + * + * We arbitrarily define a Type1 IOMMU as one matching the below code. + * It could be called the x86 IOMMU as it's designed for AMD-Vi & Intel + * VT-d, but that makes it harder to re-use as theoretically anyone + * implementing a similar IOMMU could make use of this. We expect the + * IOMMU to support the IOMMU API and have few to no restrictions around + * the IOVA range that can be mapped. The Type1 IOMMU is currently + * optimized for relatively static mappings of a userspace process with + * userpsace pages pinned into memory. We also assume devices and IOMMU + * domains are PCI based as the IOMMU API is still centered around a + * device/bus interface rather than a group interface. + */ + +#include +#include +#include +#include +#include +#include +#include /* pci_bus_type */ +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "0.2" +#define DRIVER_AUTHOR "Alex Williamson " +#define DRIVER_DESC "Type1 IOMMU driver for VFIO" + +static bool allow_unsafe_interrupts; +module_param_named(allow_unsafe_interrupts, + allow_unsafe_interrupts, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(allow_unsafe_interrupts, + "Enable VFIO IOMMU support for on platforms without interrupt remapping support."); + +struct vfio_iommu { + struct iommu_domain *domain; + struct mutex lock; + struct list_head dma_list; + struct list_head group_list; + bool cache; +}; + +struct vfio_dma { + struct list_head next; + dma_addr_t iova; /* Device address */ + unsigned long vaddr; /* Process virtual addr */ + long npage; /* Number of pages */ + int prot; /* IOMMU_READ/WRITE */ +}; + +struct vfio_group { + struct iommu_group *iommu_group; + struct list_head next; +}; + +/* + * This code handles mapping and unmapping of user data buffers + * into DMA'ble space using the IOMMU + */ + +#define NPAGE_TO_SIZE(npage) ((size_t)(npage) << PAGE_SHIFT) + +struct vwork { + struct mm_struct *mm; + long npage; + struct work_struct work; +}; + +/* delayed decrement/increment for locked_vm */ +static void vfio_lock_acct_bg(struct work_struct *work) +{ + struct vwork *vwork = container_of(work, struct vwork, work); + struct mm_struct *mm; + + mm = vwork->mm; + down_write(&mm->mmap_sem); + mm->locked_vm += vwork->npage; + up_write(&mm->mmap_sem); + mmput(mm); + kfree(vwork); +} + +static void vfio_lock_acct(long npage) +{ + struct vwork *vwork; + struct mm_struct *mm; + + if (!current->mm) + return; /* process exited */ + + if (down_write_trylock(¤t->mm->mmap_sem)) { + current->mm->locked_vm += npage; + up_write(¤t->mm->mmap_sem); + return; + } + + /* + * Couldn't get mmap_sem lock, so must setup to update + * mm->locked_vm later. If locked_vm were atomic, we + * wouldn't need this silliness + */ + vwork = kmalloc(sizeof(struct vwork), GFP_KERNEL); + if (!vwork) + return; + mm = get_task_mm(current); + if (!mm) { + kfree(vwork); + return; + } + INIT_WORK(&vwork->work, vfio_lock_acct_bg); + vwork->mm = mm; + vwork->npage = npage; + schedule_work(&vwork->work); +} + +/* + * Some mappings aren't backed by a struct page, for example an mmap'd + * MMIO range for our own or another device. These use a different + * pfn conversion and shouldn't be tracked as locked pages. + */ +static bool is_invalid_reserved_pfn(unsigned long pfn) +{ + if (pfn_valid(pfn)) { + bool reserved; + struct page *tail = pfn_to_page(pfn); + struct page *head = compound_trans_head(tail); + reserved = !!(PageReserved(head)); + if (head != tail) { + /* + * "head" is not a dangling pointer + * (compound_trans_head takes care of that) + * but the hugepage may have been split + * from under us (and we may not hold a + * reference count on the head page so it can + * be reused before we run PageReferenced), so + * we've to check PageTail before returning + * what we just read. + */ + smp_rmb(); + if (PageTail(tail)) + return reserved; + } + return PageReserved(tail); + } + + return true; +} + +static int put_pfn(unsigned long pfn, int prot) +{ + if (!is_invalid_reserved_pfn(pfn)) { + struct page *page = pfn_to_page(pfn); + if (prot & IOMMU_WRITE) + SetPageDirty(page); + put_page(page); + return 1; + } + return 0; +} + +/* Unmap DMA region */ +static long __vfio_dma_do_unmap(struct vfio_iommu *iommu, dma_addr_t iova, + long npage, int prot) +{ + long i, unlocked = 0; + + for (i = 0; i < npage; i++, iova += PAGE_SIZE) { + unsigned long pfn; + + pfn = iommu_iova_to_phys(iommu->domain, iova) >> PAGE_SHIFT; + if (pfn) { + iommu_unmap(iommu->domain, iova, PAGE_SIZE); + unlocked += put_pfn(pfn, prot); + } + } + return unlocked; +} + +static void vfio_dma_unmap(struct vfio_iommu *iommu, dma_addr_t iova, + long npage, int prot) +{ + long unlocked; + + unlocked = __vfio_dma_do_unmap(iommu, iova, npage, prot); + vfio_lock_acct(-unlocked); +} + +static int vaddr_get_pfn(unsigned long vaddr, int prot, unsigned long *pfn) +{ + struct page *page[1]; + struct vm_area_struct *vma; + int ret = -EFAULT; + + if (get_user_pages_fast(vaddr, 1, !!(prot & IOMMU_WRITE), page) == 1) { + *pfn = page_to_pfn(page[0]); + return 0; + } + + down_read(¤t->mm->mmap_sem); + + vma = find_vma_intersection(current->mm, vaddr, vaddr + 1); + + if (vma && vma->vm_flags & VM_PFNMAP) { + *pfn = ((vaddr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; + if (is_invalid_reserved_pfn(*pfn)) + ret = 0; + } + + up_read(¤t->mm->mmap_sem); + + return ret; +} + +/* Map DMA region */ +static int __vfio_dma_map(struct vfio_iommu *iommu, dma_addr_t iova, + unsigned long vaddr, long npage, int prot) +{ + dma_addr_t start = iova; + long i, locked = 0; + int ret; + + /* Verify that pages are not already mapped */ + for (i = 0; i < npage; i++, iova += PAGE_SIZE) + if (iommu_iova_to_phys(iommu->domain, iova)) + return -EBUSY; + + iova = start; + + if (iommu->cache) + prot |= IOMMU_CACHE; + + /* + * XXX We break mappings into pages and use get_user_pages_fast to + * pin the pages in memory. It's been suggested that mlock might + * provide a more efficient mechanism, but nothing prevents the + * user from munlocking the pages, which could then allow the user + * access to random host memory. We also have no guarantee from the + * IOMMU API that the iommu driver can unmap sub-pages of previous + * mappings. This means we might lose an entire range if a single + * page within it is unmapped. Single page mappings are inefficient, + * but provide the most flexibility for now. + */ + for (i = 0; i < npage; i++, iova += PAGE_SIZE, vaddr += PAGE_SIZE) { + unsigned long pfn = 0; + + ret = vaddr_get_pfn(vaddr, prot, &pfn); + if (ret) { + __vfio_dma_do_unmap(iommu, start, i, prot); + return ret; + } + + /* + * Only add actual locked pages to accounting + * XXX We're effectively marking a page locked for every + * IOVA page even though it's possible the user could be + * backing multiple IOVAs with the same vaddr. This over- + * penalizes the user process, but we currently have no + * easy way to do this properly. + */ + if (!is_invalid_reserved_pfn(pfn)) + locked++; + + ret = iommu_map(iommu->domain, iova, + (phys_addr_t)pfn << PAGE_SHIFT, + PAGE_SIZE, prot); + if (ret) { + /* Back out mappings on error */ + put_pfn(pfn, prot); + __vfio_dma_do_unmap(iommu, start, i, prot); + return ret; + } + } + vfio_lock_acct(locked); + return 0; +} + +static inline bool ranges_overlap(dma_addr_t start1, size_t size1, + dma_addr_t start2, size_t size2) +{ + if (start1 < start2) + return (start2 - start1 < size1); + else if (start2 < start1) + return (start1 - start2 < size2); + return (size1 > 0 && size2 > 0); +} + +static struct vfio_dma *vfio_find_dma(struct vfio_iommu *iommu, + dma_addr_t start, size_t size) +{ + struct vfio_dma *dma; + + list_for_each_entry(dma, &iommu->dma_list, next) { + if (ranges_overlap(dma->iova, NPAGE_TO_SIZE(dma->npage), + start, size)) + return dma; + } + return NULL; +} + +static long vfio_remove_dma_overlap(struct vfio_iommu *iommu, dma_addr_t start, + size_t size, struct vfio_dma *dma) +{ + struct vfio_dma *split; + long npage_lo, npage_hi; + + /* Existing dma region is completely covered, unmap all */ + if (start <= dma->iova && + start + size >= dma->iova + NPAGE_TO_SIZE(dma->npage)) { + vfio_dma_unmap(iommu, dma->iova, dma->npage, dma->prot); + list_del(&dma->next); + npage_lo = dma->npage; + kfree(dma); + return npage_lo; + } + + /* Overlap low address of existing range */ + if (start <= dma->iova) { + size_t overlap; + + overlap = start + size - dma->iova; + npage_lo = overlap >> PAGE_SHIFT; + + vfio_dma_unmap(iommu, dma->iova, npage_lo, dma->prot); + dma->iova += overlap; + dma->vaddr += overlap; + dma->npage -= npage_lo; + return npage_lo; + } + + /* Overlap high address of existing range */ + if (start + size >= dma->iova + NPAGE_TO_SIZE(dma->npage)) { + size_t overlap; + + overlap = dma->iova + NPAGE_TO_SIZE(dma->npage) - start; + npage_hi = overlap >> PAGE_SHIFT; + + vfio_dma_unmap(iommu, start, npage_hi, dma->prot); + dma->npage -= npage_hi; + return npage_hi; + } + + /* Split existing */ + npage_lo = (start - dma->iova) >> PAGE_SHIFT; + npage_hi = dma->npage - (size >> PAGE_SHIFT) - npage_lo; + + split = kzalloc(sizeof *split, GFP_KERNEL); + if (!split) + return -ENOMEM; + + vfio_dma_unmap(iommu, start, size >> PAGE_SHIFT, dma->prot); + + dma->npage = npage_lo; + + split->npage = npage_hi; + split->iova = start + size; + split->vaddr = dma->vaddr + NPAGE_TO_SIZE(npage_lo) + size; + split->prot = dma->prot; + list_add(&split->next, &iommu->dma_list); + return size >> PAGE_SHIFT; +} + +static int vfio_dma_do_unmap(struct vfio_iommu *iommu, + struct vfio_iommu_type1_dma_unmap *unmap) +{ + long ret = 0, npage = unmap->size >> PAGE_SHIFT; + struct vfio_dma *dma, *tmp; + uint64_t mask; + + mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1; + + if (unmap->iova & mask) + return -EINVAL; + if (unmap->size & mask) + return -EINVAL; + + /* XXX We still break these down into PAGE_SIZE */ + WARN_ON(mask & PAGE_MASK); + + mutex_lock(&iommu->lock); + + list_for_each_entry_safe(dma, tmp, &iommu->dma_list, next) { + if (ranges_overlap(dma->iova, NPAGE_TO_SIZE(dma->npage), + unmap->iova, unmap->size)) { + ret = vfio_remove_dma_overlap(iommu, unmap->iova, + unmap->size, dma); + if (ret > 0) + npage -= ret; + if (ret < 0 || npage == 0) + break; + } + } + mutex_unlock(&iommu->lock); + return ret > 0 ? 0 : (int)ret; +} + +static int vfio_dma_do_map(struct vfio_iommu *iommu, + struct vfio_iommu_type1_dma_map *map) +{ + struct vfio_dma *dma, *pdma = NULL; + dma_addr_t iova = map->iova; + unsigned long locked, lock_limit, vaddr = map->vaddr; + size_t size = map->size; + int ret = 0, prot = 0; + uint64_t mask; + long npage; + + mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1; + + /* READ/WRITE from device perspective */ + if (map->flags & VFIO_DMA_MAP_FLAG_WRITE) + prot |= IOMMU_WRITE; + if (map->flags & VFIO_DMA_MAP_FLAG_READ) + prot |= IOMMU_READ; + + if (!prot) + return -EINVAL; /* No READ/WRITE? */ + + if (vaddr & mask) + return -EINVAL; + if (iova & mask) + return -EINVAL; + if (size & mask) + return -EINVAL; + + /* XXX We still break these down into PAGE_SIZE */ + WARN_ON(mask & PAGE_MASK); + + /* Don't allow IOVA wrap */ + if (iova + size && iova + size < iova) + return -EINVAL; + + /* Don't allow virtual address wrap */ + if (vaddr + size && vaddr + size < vaddr) + return -EINVAL; + + npage = size >> PAGE_SHIFT; + if (!npage) + return -EINVAL; + + mutex_lock(&iommu->lock); + + if (vfio_find_dma(iommu, iova, size)) { + ret = -EBUSY; + goto out_lock; + } + + /* account for locked pages */ + locked = current->mm->locked_vm + npage; + lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + if (locked > lock_limit && !capable(CAP_IPC_LOCK)) { + pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n", + __func__, rlimit(RLIMIT_MEMLOCK)); + ret = -ENOMEM; + goto out_lock; + } + + ret = __vfio_dma_map(iommu, iova, vaddr, npage, prot); + if (ret) + goto out_lock; + + /* Check if we abut a region below - nothing below 0 */ + if (iova) { + dma = vfio_find_dma(iommu, iova - 1, 1); + if (dma && dma->prot == prot && + dma->vaddr + NPAGE_TO_SIZE(dma->npage) == vaddr) { + + dma->npage += npage; + iova = dma->iova; + vaddr = dma->vaddr; + npage = dma->npage; + size = NPAGE_TO_SIZE(npage); + + pdma = dma; + } + } + + /* Check if we abut a region above - nothing above ~0 + 1 */ + if (iova + size) { + dma = vfio_find_dma(iommu, iova + size, 1); + if (dma && dma->prot == prot && + dma->vaddr == vaddr + size) { + + dma->npage += npage; + dma->iova = iova; + dma->vaddr = vaddr; + + /* + * If merged above and below, remove previously + * merged entry. New entry covers it. + */ + if (pdma) { + list_del(&pdma->next); + kfree(pdma); + } + pdma = dma; + } + } + + /* Isolated, new region */ + if (!pdma) { + dma = kzalloc(sizeof *dma, GFP_KERNEL); + if (!dma) { + ret = -ENOMEM; + vfio_dma_unmap(iommu, iova, npage, prot); + goto out_lock; + } + + dma->npage = npage; + dma->iova = iova; + dma->vaddr = vaddr; + dma->prot = prot; + list_add(&dma->next, &iommu->dma_list); + } + +out_lock: + mutex_unlock(&iommu->lock); + return ret; +} + +static int vfio_iommu_type1_attach_group(void *iommu_data, + struct iommu_group *iommu_group) +{ + struct vfio_iommu *iommu = iommu_data; + struct vfio_group *group, *tmp; + int ret; + + group = kzalloc(sizeof(*group), GFP_KERNEL); + if (!group) + return -ENOMEM; + + mutex_lock(&iommu->lock); + + list_for_each_entry(tmp, &iommu->group_list, next) { + if (tmp->iommu_group == iommu_group) { + mutex_unlock(&iommu->lock); + kfree(group); + return -EINVAL; + } + } + + /* + * TODO: Domain have capabilities that might change as we add + * groups (see iommu->cache, currently never set). Check for + * them and potentially disallow groups to be attached when it + * would change capabilities (ugh). + */ + ret = iommu_attach_group(iommu->domain, iommu_group); + if (ret) { + mutex_unlock(&iommu->lock); + kfree(group); + return ret; + } + + group->iommu_group = iommu_group; + list_add(&group->next, &iommu->group_list); + + mutex_unlock(&iommu->lock); + + return 0; +} + +static void vfio_iommu_type1_detach_group(void *iommu_data, + struct iommu_group *iommu_group) +{ + struct vfio_iommu *iommu = iommu_data; + struct vfio_group *group; + + mutex_lock(&iommu->lock); + + list_for_each_entry(group, &iommu->group_list, next) { + if (group->iommu_group == iommu_group) { + iommu_detach_group(iommu->domain, iommu_group); + list_del(&group->next); + kfree(group); + break; + } + } + + mutex_unlock(&iommu->lock); +} + +static void *vfio_iommu_type1_open(unsigned long arg) +{ + struct vfio_iommu *iommu; + + if (arg != VFIO_TYPE1_IOMMU) + return ERR_PTR(-EINVAL); + + iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); + if (!iommu) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&iommu->group_list); + INIT_LIST_HEAD(&iommu->dma_list); + mutex_init(&iommu->lock); + + /* + * Wish we didn't have to know about bus_type here. + */ + iommu->domain = iommu_domain_alloc(&pci_bus_type); + if (!iommu->domain) { + kfree(iommu); + return ERR_PTR(-EIO); + } + + /* + * Wish we could specify required capabilities rather than create + * a domain, see what comes out and hope it doesn't change along + * the way. Fortunately we know interrupt remapping is global for + * our iommus. + */ + if (!allow_unsafe_interrupts && + !iommu_domain_has_cap(iommu->domain, IOMMU_CAP_INTR_REMAP)) { + pr_warn("%s: No interrupt remapping support. Use the module param \"allow_unsafe_interrupts\" to enable VFIO IOMMU support on this platform\n", + __func__); + iommu_domain_free(iommu->domain); + kfree(iommu); + return ERR_PTR(-EPERM); + } + + return iommu; +} + +static void vfio_iommu_type1_release(void *iommu_data) +{ + struct vfio_iommu *iommu = iommu_data; + struct vfio_group *group, *group_tmp; + struct vfio_dma *dma, *dma_tmp; + + list_for_each_entry_safe(group, group_tmp, &iommu->group_list, next) { + iommu_detach_group(iommu->domain, group->iommu_group); + list_del(&group->next); + kfree(group); + } + + list_for_each_entry_safe(dma, dma_tmp, &iommu->dma_list, next) { + vfio_dma_unmap(iommu, dma->iova, dma->npage, dma->prot); + list_del(&dma->next); + kfree(dma); + } + + iommu_domain_free(iommu->domain); + iommu->domain = NULL; + kfree(iommu); +} + +static long vfio_iommu_type1_ioctl(void *iommu_data, + unsigned int cmd, unsigned long arg) +{ + struct vfio_iommu *iommu = iommu_data; + unsigned long minsz; + + if (cmd == VFIO_CHECK_EXTENSION) { + switch (arg) { + case VFIO_TYPE1_IOMMU: + return 1; + default: + return 0; + } + } else if (cmd == VFIO_IOMMU_GET_INFO) { + struct vfio_iommu_type1_info info; + + minsz = offsetofend(struct vfio_iommu_type1_info, iova_pgsizes); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + info.flags = 0; + + info.iova_pgsizes = iommu->domain->ops->pgsize_bitmap; + + return copy_to_user((void __user *)arg, &info, minsz); + + } else if (cmd == VFIO_IOMMU_MAP_DMA) { + struct vfio_iommu_type1_dma_map map; + uint32_t mask = VFIO_DMA_MAP_FLAG_READ | + VFIO_DMA_MAP_FLAG_WRITE; + + minsz = offsetofend(struct vfio_iommu_type1_dma_map, size); + + if (copy_from_user(&map, (void __user *)arg, minsz)) + return -EFAULT; + + if (map.argsz < minsz || map.flags & ~mask) + return -EINVAL; + + return vfio_dma_do_map(iommu, &map); + + } else if (cmd == VFIO_IOMMU_UNMAP_DMA) { + struct vfio_iommu_type1_dma_unmap unmap; + + minsz = offsetofend(struct vfio_iommu_type1_dma_unmap, size); + + if (copy_from_user(&unmap, (void __user *)arg, minsz)) + return -EFAULT; + + if (unmap.argsz < minsz || unmap.flags) + return -EINVAL; + + return vfio_dma_do_unmap(iommu, &unmap); + } + + return -ENOTTY; +} + +static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = { + .name = "vfio-iommu-type1", + .owner = THIS_MODULE, + .open = vfio_iommu_type1_open, + .release = vfio_iommu_type1_release, + .ioctl = vfio_iommu_type1_ioctl, + .attach_group = vfio_iommu_type1_attach_group, + .detach_group = vfio_iommu_type1_detach_group, +}; + +static int __init vfio_iommu_type1_init(void) +{ + if (!iommu_present(&pci_bus_type)) + return -ENODEV; + + return vfio_register_iommu_driver(&vfio_iommu_driver_ops_type1); +} + +static void __exit vfio_iommu_type1_cleanup(void) +{ + vfio_unregister_iommu_driver(&vfio_iommu_driver_ops_type1); +} + +module_init(vfio_iommu_type1_init); +module_exit(vfio_iommu_type1_cleanup); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/include/linux/vfio.h b/include/linux/vfio.h index 03e56a5154b6..acb046fd5b70 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -98,7 +98,7 @@ extern void vfio_unregister_iommu_driver( /* Extensions */ -/* None yet */ +#define VFIO_TYPE1_IOMMU 1 /* * The IOCTL interface is designed for extensibility by embedding the @@ -364,4 +364,56 @@ struct vfio_irq_set { */ #define VFIO_DEVICE_RESET _IO(VFIO_TYPE, VFIO_BASE + 11) +/* -------- API for Type1 VFIO IOMMU -------- */ + +/** + * VFIO_IOMMU_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 12, struct vfio_iommu_info) + * + * Retrieve information about the IOMMU object. Fills in provided + * struct vfio_iommu_info. Caller sets argsz. + * + * XXX Should we do these by CHECK_EXTENSION too? + */ +struct vfio_iommu_type1_info { + __u32 argsz; + __u32 flags; +#define VFIO_IOMMU_INFO_PGSIZES (1 << 0) /* supported page sizes info */ + __u64 iova_pgsizes; /* Bitmap of supported page sizes */ +}; + +#define VFIO_IOMMU_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 12) + +/** + * VFIO_IOMMU_MAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 13, struct vfio_dma_map) + * + * Map process virtual addresses to IO virtual addresses using the + * provided struct vfio_dma_map. Caller sets argsz. READ &/ WRITE required. + */ +struct vfio_iommu_type1_dma_map { + __u32 argsz; + __u32 flags; +#define VFIO_DMA_MAP_FLAG_READ (1 << 0) /* readable from device */ +#define VFIO_DMA_MAP_FLAG_WRITE (1 << 1) /* writable from device */ + __u64 vaddr; /* Process virtual address */ + __u64 iova; /* IO virtual address */ + __u64 size; /* Size of mapping (bytes) */ +}; + +#define VFIO_IOMMU_MAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 13) + +/** + * VFIO_IOMMU_UNMAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 14, struct vfio_dma_unmap) + * + * Unmap IO virtual addresses using the provided struct vfio_dma_unmap. + * Caller sets argsz. + */ +struct vfio_iommu_type1_dma_unmap { + __u32 argsz; + __u32 flags; + __u64 iova; /* IO virtual address */ + __u64 size; /* Size of mapping (bytes) */ +}; + +#define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14) + #endif /* VFIO_H */ -- cgit v1.2.3 From 89e1f7d4c66d85f42c3d52ea3866eb10cadf6153 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 31 Jul 2012 08:16:24 -0600 Subject: vfio: Add PCI device driver Add PCI device support for VFIO. PCI devices expose regions for accessing config space, I/O port space, and MMIO areas of the device. PCI config access is virtualized in the kernel, allowing us to ensure the integrity of the system, by preventing various accesses while reducing duplicate support across various userspace drivers. I/O port supports read/write access while MMIO also supports mmap of sufficiently sized regions. Support for INTx, MSI, and MSI-X interrupts are provided using eventfds to userspace. Signed-off-by: Alex Williamson --- drivers/vfio/Kconfig | 2 + drivers/vfio/pci/Kconfig | 8 + drivers/vfio/pci/Makefile | 4 + drivers/vfio/pci/vfio_pci.c | 579 +++++++++++++ drivers/vfio/pci/vfio_pci_config.c | 1540 +++++++++++++++++++++++++++++++++++ drivers/vfio/pci/vfio_pci_intrs.c | 740 +++++++++++++++++ drivers/vfio/pci/vfio_pci_private.h | 91 +++ drivers/vfio/pci/vfio_pci_rdwr.c | 269 ++++++ include/linux/vfio.h | 26 + 9 files changed, 3259 insertions(+) create mode 100644 drivers/vfio/pci/Kconfig create mode 100644 drivers/vfio/pci/Makefile create mode 100644 drivers/vfio/pci/vfio_pci.c create mode 100644 drivers/vfio/pci/vfio_pci_config.c create mode 100644 drivers/vfio/pci/vfio_pci_intrs.c create mode 100644 drivers/vfio/pci/vfio_pci_private.h create mode 100644 drivers/vfio/pci/vfio_pci_rdwr.c (limited to 'include') diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index 128b97910b8e..7cd5dec0abd1 100644 --- a/drivers/vfio/Kconfig +++ b/drivers/vfio/Kconfig @@ -12,3 +12,5 @@ menuconfig VFIO See Documentation/vfio.txt for more details. If you don't know what to do here, say N. + +source "drivers/vfio/pci/Kconfig" diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig new file mode 100644 index 000000000000..5980758563eb --- /dev/null +++ b/drivers/vfio/pci/Kconfig @@ -0,0 +1,8 @@ +config VFIO_PCI + tristate "VFIO support for PCI devices" + depends on VFIO && PCI && EVENTFD + help + Support for the PCI VFIO bus driver. This is required to make + use of PCI drivers using the VFIO framework. + + If you don't know what to do here, say N. diff --git a/drivers/vfio/pci/Makefile b/drivers/vfio/pci/Makefile new file mode 100644 index 000000000000..131079255fd9 --- /dev/null +++ b/drivers/vfio/pci/Makefile @@ -0,0 +1,4 @@ + +vfio-pci-y := vfio_pci.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o + +obj-$(CONFIG_VFIO_PCI) += vfio-pci.o diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c new file mode 100644 index 000000000000..6968b7232232 --- /dev/null +++ b/drivers/vfio/pci/vfio_pci.c @@ -0,0 +1,579 @@ +/* + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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. + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vfio_pci_private.h" + +#define DRIVER_VERSION "0.2" +#define DRIVER_AUTHOR "Alex Williamson " +#define DRIVER_DESC "VFIO PCI - User Level meta-driver" + +static bool nointxmask; +module_param_named(nointxmask, nointxmask, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(nointxmask, + "Disable support for PCI 2.3 style INTx masking. If this resolves problems for specific devices, report lspci -vvvxxx to linux-pci@vger.kernel.org so the device can be fixed automatically via the broken_intx_masking flag."); + +static int vfio_pci_enable(struct vfio_pci_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + int ret; + u16 cmd; + u8 msix_pos; + + vdev->reset_works = (pci_reset_function(pdev) == 0); + pci_save_state(pdev); + vdev->pci_saved_state = pci_store_saved_state(pdev); + if (!vdev->pci_saved_state) + pr_debug("%s: Couldn't store %s saved state\n", + __func__, dev_name(&pdev->dev)); + + ret = vfio_config_init(vdev); + if (ret) + goto out; + + if (likely(!nointxmask)) + vdev->pci_2_3 = pci_intx_mask_supported(pdev); + + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + if (vdev->pci_2_3 && (cmd & PCI_COMMAND_INTX_DISABLE)) { + cmd &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(pdev, PCI_COMMAND, cmd); + } + + msix_pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); + if (msix_pos) { + u16 flags; + u32 table; + + pci_read_config_word(pdev, msix_pos + PCI_MSIX_FLAGS, &flags); + pci_read_config_dword(pdev, msix_pos + PCI_MSIX_TABLE, &table); + + vdev->msix_bar = table & PCI_MSIX_FLAGS_BIRMASK; + vdev->msix_offset = table & ~PCI_MSIX_FLAGS_BIRMASK; + vdev->msix_size = ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) * 16; + } else + vdev->msix_bar = 0xFF; + + ret = pci_enable_device(pdev); + if (ret) + goto out; + + return ret; + +out: + kfree(vdev->pci_saved_state); + vdev->pci_saved_state = NULL; + vfio_config_free(vdev); + return ret; +} + +static void vfio_pci_disable(struct vfio_pci_device *vdev) +{ + int bar; + + pci_disable_device(vdev->pdev); + + vfio_pci_set_irqs_ioctl(vdev, VFIO_IRQ_SET_DATA_NONE | + VFIO_IRQ_SET_ACTION_TRIGGER, + vdev->irq_type, 0, 0, NULL); + + vdev->virq_disabled = false; + + vfio_config_free(vdev); + + pci_reset_function(vdev->pdev); + + if (pci_load_and_free_saved_state(vdev->pdev, + &vdev->pci_saved_state) == 0) + pci_restore_state(vdev->pdev); + else + pr_info("%s: Couldn't reload %s saved state\n", + __func__, dev_name(&vdev->pdev->dev)); + + for (bar = PCI_STD_RESOURCES; bar <= PCI_STD_RESOURCE_END; bar++) { + if (!vdev->barmap[bar]) + continue; + pci_iounmap(vdev->pdev, vdev->barmap[bar]); + pci_release_selected_regions(vdev->pdev, 1 << bar); + vdev->barmap[bar] = NULL; + } +} + +static void vfio_pci_release(void *device_data) +{ + struct vfio_pci_device *vdev = device_data; + + if (atomic_dec_and_test(&vdev->refcnt)) + vfio_pci_disable(vdev); + + module_put(THIS_MODULE); +} + +static int vfio_pci_open(void *device_data) +{ + struct vfio_pci_device *vdev = device_data; + + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + if (atomic_inc_return(&vdev->refcnt) == 1) { + int ret = vfio_pci_enable(vdev); + if (ret) { + module_put(THIS_MODULE); + return ret; + } + } + + return 0; +} + +static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type) +{ + if (irq_type == VFIO_PCI_INTX_IRQ_INDEX) { + u8 pin; + pci_read_config_byte(vdev->pdev, PCI_INTERRUPT_PIN, &pin); + if (pin) + return 1; + + } else if (irq_type == VFIO_PCI_MSI_IRQ_INDEX) { + u8 pos; + u16 flags; + + pos = pci_find_capability(vdev->pdev, PCI_CAP_ID_MSI); + if (pos) { + pci_read_config_word(vdev->pdev, + pos + PCI_MSI_FLAGS, &flags); + + return 1 << (flags & PCI_MSI_FLAGS_QMASK); + } + } else if (irq_type == VFIO_PCI_MSIX_IRQ_INDEX) { + u8 pos; + u16 flags; + + pos = pci_find_capability(vdev->pdev, PCI_CAP_ID_MSIX); + if (pos) { + pci_read_config_word(vdev->pdev, + pos + PCI_MSIX_FLAGS, &flags); + + return (flags & PCI_MSIX_FLAGS_QSIZE) + 1; + } + } + + return 0; +} + +static long vfio_pci_ioctl(void *device_data, + unsigned int cmd, unsigned long arg) +{ + struct vfio_pci_device *vdev = device_data; + unsigned long minsz; + + if (cmd == VFIO_DEVICE_GET_INFO) { + struct vfio_device_info info; + + minsz = offsetofend(struct vfio_device_info, num_irqs); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + info.flags = VFIO_DEVICE_FLAGS_PCI; + + if (vdev->reset_works) + info.flags |= VFIO_DEVICE_FLAGS_RESET; + + info.num_regions = VFIO_PCI_NUM_REGIONS; + info.num_irqs = VFIO_PCI_NUM_IRQS; + + return copy_to_user((void __user *)arg, &info, minsz); + + } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) { + struct pci_dev *pdev = vdev->pdev; + struct vfio_region_info info; + + minsz = offsetofend(struct vfio_region_info, offset); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + switch (info.index) { + case VFIO_PCI_CONFIG_REGION_INDEX: + info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.size = pdev->cfg_size; + info.flags = VFIO_REGION_INFO_FLAG_READ | + VFIO_REGION_INFO_FLAG_WRITE; + break; + case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: + info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.size = pci_resource_len(pdev, info.index); + if (!info.size) { + info.flags = 0; + break; + } + + info.flags = VFIO_REGION_INFO_FLAG_READ | + VFIO_REGION_INFO_FLAG_WRITE; + if (pci_resource_flags(pdev, info.index) & + IORESOURCE_MEM && info.size >= PAGE_SIZE) + info.flags |= VFIO_REGION_INFO_FLAG_MMAP; + break; + case VFIO_PCI_ROM_REGION_INDEX: + { + void __iomem *io; + size_t size; + + info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.flags = 0; + + /* Report the BAR size, not the ROM size */ + info.size = pci_resource_len(pdev, info.index); + if (!info.size) + break; + + /* Is it really there? */ + io = pci_map_rom(pdev, &size); + if (!io || !size) { + info.size = 0; + break; + } + pci_unmap_rom(pdev, io); + + info.flags = VFIO_REGION_INFO_FLAG_READ; + break; + } + default: + return -EINVAL; + } + + return copy_to_user((void __user *)arg, &info, minsz); + + } else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) { + struct vfio_irq_info info; + + minsz = offsetofend(struct vfio_irq_info, count); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS) + return -EINVAL; + + info.flags = VFIO_IRQ_INFO_EVENTFD; + + info.count = vfio_pci_get_irq_count(vdev, info.index); + + if (info.index == VFIO_PCI_INTX_IRQ_INDEX) + info.flags |= (VFIO_IRQ_INFO_MASKABLE | + VFIO_IRQ_INFO_AUTOMASKED); + else + info.flags |= VFIO_IRQ_INFO_NORESIZE; + + return copy_to_user((void __user *)arg, &info, minsz); + + } else if (cmd == VFIO_DEVICE_SET_IRQS) { + struct vfio_irq_set hdr; + u8 *data = NULL; + int ret = 0; + + minsz = offsetofend(struct vfio_irq_set, count); + + if (copy_from_user(&hdr, (void __user *)arg, minsz)) + return -EFAULT; + + if (hdr.argsz < minsz || hdr.index >= VFIO_PCI_NUM_IRQS || + hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK | + VFIO_IRQ_SET_ACTION_TYPE_MASK)) + return -EINVAL; + + if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) { + size_t size; + + if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL) + size = sizeof(uint8_t); + else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD) + size = sizeof(int32_t); + else + return -EINVAL; + + if (hdr.argsz - minsz < hdr.count * size || + hdr.count > vfio_pci_get_irq_count(vdev, hdr.index)) + return -EINVAL; + + data = kmalloc(hdr.count * size, GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (copy_from_user(data, (void __user *)(arg + minsz), + hdr.count * size)) { + kfree(data); + return -EFAULT; + } + } + + mutex_lock(&vdev->igate); + + ret = vfio_pci_set_irqs_ioctl(vdev, hdr.flags, hdr.index, + hdr.start, hdr.count, data); + + mutex_unlock(&vdev->igate); + kfree(data); + + return ret; + + } else if (cmd == VFIO_DEVICE_RESET) + return vdev->reset_works ? + pci_reset_function(vdev->pdev) : -EINVAL; + + return -ENOTTY; +} + +static ssize_t vfio_pci_read(void *device_data, char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); + struct vfio_pci_device *vdev = device_data; + struct pci_dev *pdev = vdev->pdev; + + if (index >= VFIO_PCI_NUM_REGIONS) + return -EINVAL; + + if (index == VFIO_PCI_CONFIG_REGION_INDEX) + return vfio_pci_config_readwrite(vdev, buf, count, ppos, false); + else if (index == VFIO_PCI_ROM_REGION_INDEX) + return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false); + else if (pci_resource_flags(pdev, index) & IORESOURCE_IO) + return vfio_pci_io_readwrite(vdev, buf, count, ppos, false); + else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) + return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false); + + return -EINVAL; +} + +static ssize_t vfio_pci_write(void *device_data, const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); + struct vfio_pci_device *vdev = device_data; + struct pci_dev *pdev = vdev->pdev; + + if (index >= VFIO_PCI_NUM_REGIONS) + return -EINVAL; + + if (index == VFIO_PCI_CONFIG_REGION_INDEX) + return vfio_pci_config_readwrite(vdev, (char __user *)buf, + count, ppos, true); + else if (index == VFIO_PCI_ROM_REGION_INDEX) + return -EINVAL; + else if (pci_resource_flags(pdev, index) & IORESOURCE_IO) + return vfio_pci_io_readwrite(vdev, (char __user *)buf, + count, ppos, true); + else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) { + return vfio_pci_mem_readwrite(vdev, (char __user *)buf, + count, ppos, true); + } + + return -EINVAL; +} + +static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma) +{ + struct vfio_pci_device *vdev = device_data; + struct pci_dev *pdev = vdev->pdev; + unsigned int index; + u64 phys_len, req_len, pgoff, req_start, phys; + int ret; + + index = vma->vm_pgoff >> (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT); + + if (vma->vm_end < vma->vm_start) + return -EINVAL; + if ((vma->vm_flags & VM_SHARED) == 0) + return -EINVAL; + if (index >= VFIO_PCI_ROM_REGION_INDEX) + return -EINVAL; + if (!(pci_resource_flags(pdev, index) & IORESOURCE_MEM)) + return -EINVAL; + + phys_len = pci_resource_len(pdev, index); + req_len = vma->vm_end - vma->vm_start; + pgoff = vma->vm_pgoff & + ((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1); + req_start = pgoff << PAGE_SHIFT; + + if (phys_len < PAGE_SIZE || req_start + req_len > phys_len) + return -EINVAL; + + if (index == vdev->msix_bar) { + /* + * Disallow mmaps overlapping the MSI-X table; users don't + * get to touch this directly. We could find somewhere + * else to map the overlap, but page granularity is only + * a recommendation, not a requirement, so the user needs + * to know which bits are real. Requiring them to mmap + * around the table makes that clear. + */ + + /* If neither entirely above nor below, then it overlaps */ + if (!(req_start >= vdev->msix_offset + vdev->msix_size || + req_start + req_len <= vdev->msix_offset)) + return -EINVAL; + } + + /* + * Even though we don't make use of the barmap for the mmap, + * we need to request the region and the barmap tracks that. + */ + if (!vdev->barmap[index]) { + ret = pci_request_selected_regions(pdev, + 1 << index, "vfio-pci"); + if (ret) + return ret; + + vdev->barmap[index] = pci_iomap(pdev, index, 0); + } + + vma->vm_private_data = vdev; + vma->vm_flags |= (VM_IO | VM_RESERVED); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + phys = (pci_resource_start(pdev, index) >> PAGE_SHIFT) + pgoff; + + return remap_pfn_range(vma, vma->vm_start, phys, + req_len, vma->vm_page_prot); +} + +static const struct vfio_device_ops vfio_pci_ops = { + .name = "vfio-pci", + .open = vfio_pci_open, + .release = vfio_pci_release, + .ioctl = vfio_pci_ioctl, + .read = vfio_pci_read, + .write = vfio_pci_write, + .mmap = vfio_pci_mmap, +}; + +static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + u8 type; + struct vfio_pci_device *vdev; + struct iommu_group *group; + int ret; + + pci_read_config_byte(pdev, PCI_HEADER_TYPE, &type); + if ((type & PCI_HEADER_TYPE) != PCI_HEADER_TYPE_NORMAL) + return -EINVAL; + + group = iommu_group_get(&pdev->dev); + if (!group) + return -EINVAL; + + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) { + iommu_group_put(group); + return -ENOMEM; + } + + vdev->pdev = pdev; + vdev->irq_type = VFIO_PCI_NUM_IRQS; + mutex_init(&vdev->igate); + spin_lock_init(&vdev->irqlock); + atomic_set(&vdev->refcnt, 0); + + ret = vfio_add_group_dev(&pdev->dev, &vfio_pci_ops, vdev); + if (ret) { + iommu_group_put(group); + kfree(vdev); + } + + return ret; +} + +static void vfio_pci_remove(struct pci_dev *pdev) +{ + struct vfio_pci_device *vdev; + + vdev = vfio_del_group_dev(&pdev->dev); + if (!vdev) + return; + + iommu_group_put(pdev->dev.iommu_group); + kfree(vdev); +} + +static struct pci_driver vfio_pci_driver = { + .name = "vfio-pci", + .id_table = NULL, /* only dynamic ids */ + .probe = vfio_pci_probe, + .remove = vfio_pci_remove, +}; + +static void __exit vfio_pci_cleanup(void) +{ + pci_unregister_driver(&vfio_pci_driver); + vfio_pci_virqfd_exit(); + vfio_pci_uninit_perm_bits(); +} + +static int __init vfio_pci_init(void) +{ + int ret; + + /* Allocate shared config space permision data used by all devices */ + ret = vfio_pci_init_perm_bits(); + if (ret) + return ret; + + /* Start the virqfd cleanup handler */ + ret = vfio_pci_virqfd_init(); + if (ret) + goto out_virqfd; + + /* Register and scan for devices */ + ret = pci_register_driver(&vfio_pci_driver); + if (ret) + goto out_driver; + + return 0; + +out_virqfd: + vfio_pci_virqfd_exit(); +out_driver: + vfio_pci_uninit_perm_bits(); + return ret; +} + +module_init(vfio_pci_init); +module_exit(vfio_pci_cleanup); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c new file mode 100644 index 000000000000..8b8f7d11e102 --- /dev/null +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -0,0 +1,1540 @@ +/* + * VFIO PCI config space virtualization + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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. + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + */ + +/* + * This code handles reading and writing of PCI configuration registers. + * This is hairy because we want to allow a lot of flexibility to the + * user driver, but cannot trust it with all of the config fields. + * Tables determine which fields can be read and written, as well as + * which fields are 'virtualized' - special actions and translations to + * make it appear to the user that he has control, when in fact things + * must be negotiated with the underlying OS. + */ + +#include +#include +#include +#include + +#include "vfio_pci_private.h" + +#define PCI_CFG_SPACE_SIZE 256 + +/* Useful "pseudo" capabilities */ +#define PCI_CAP_ID_BASIC 0 +#define PCI_CAP_ID_INVALID 0xFF + +#define is_bar(offset) \ + ((offset >= PCI_BASE_ADDRESS_0 && offset < PCI_BASE_ADDRESS_5 + 4) || \ + (offset >= PCI_ROM_ADDRESS && offset < PCI_ROM_ADDRESS + 4)) + +/* + * Lengths of PCI Config Capabilities + * 0: Removed from the user visible capability list + * FF: Variable length + */ +static u8 pci_cap_length[] = { + [PCI_CAP_ID_BASIC] = PCI_STD_HEADER_SIZEOF, /* pci config header */ + [PCI_CAP_ID_PM] = PCI_PM_SIZEOF, + [PCI_CAP_ID_AGP] = PCI_AGP_SIZEOF, + [PCI_CAP_ID_VPD] = PCI_CAP_VPD_SIZEOF, + [PCI_CAP_ID_SLOTID] = 0, /* bridge - don't care */ + [PCI_CAP_ID_MSI] = 0xFF, /* 10, 14, 20, or 24 */ + [PCI_CAP_ID_CHSWP] = 0, /* cpci - not yet */ + [PCI_CAP_ID_PCIX] = 0xFF, /* 8 or 24 */ + [PCI_CAP_ID_HT] = 0xFF, /* hypertransport */ + [PCI_CAP_ID_VNDR] = 0xFF, /* variable */ + [PCI_CAP_ID_DBG] = 0, /* debug - don't care */ + [PCI_CAP_ID_CCRC] = 0, /* cpci - not yet */ + [PCI_CAP_ID_SHPC] = 0, /* hotswap - not yet */ + [PCI_CAP_ID_SSVID] = 0, /* bridge - don't care */ + [PCI_CAP_ID_AGP3] = 0, /* AGP8x - not yet */ + [PCI_CAP_ID_SECDEV] = 0, /* secure device not yet */ + [PCI_CAP_ID_EXP] = 0xFF, /* 20 or 44 */ + [PCI_CAP_ID_MSIX] = PCI_CAP_MSIX_SIZEOF, + [PCI_CAP_ID_SATA] = 0xFF, + [PCI_CAP_ID_AF] = PCI_CAP_AF_SIZEOF, +}; + +/* + * Lengths of PCIe/PCI-X Extended Config Capabilities + * 0: Removed or masked from the user visible capabilty list + * FF: Variable length + */ +static u16 pci_ext_cap_length[] = { + [PCI_EXT_CAP_ID_ERR] = PCI_ERR_ROOT_COMMAND, + [PCI_EXT_CAP_ID_VC] = 0xFF, + [PCI_EXT_CAP_ID_DSN] = PCI_EXT_CAP_DSN_SIZEOF, + [PCI_EXT_CAP_ID_PWR] = PCI_EXT_CAP_PWR_SIZEOF, + [PCI_EXT_CAP_ID_RCLD] = 0, /* root only - don't care */ + [PCI_EXT_CAP_ID_RCILC] = 0, /* root only - don't care */ + [PCI_EXT_CAP_ID_RCEC] = 0, /* root only - don't care */ + [PCI_EXT_CAP_ID_MFVC] = 0xFF, + [PCI_EXT_CAP_ID_VC9] = 0xFF, /* same as CAP_ID_VC */ + [PCI_EXT_CAP_ID_RCRB] = 0, /* root only - don't care */ + [PCI_EXT_CAP_ID_VNDR] = 0xFF, + [PCI_EXT_CAP_ID_CAC] = 0, /* obsolete */ + [PCI_EXT_CAP_ID_ACS] = 0xFF, + [PCI_EXT_CAP_ID_ARI] = PCI_EXT_CAP_ARI_SIZEOF, + [PCI_EXT_CAP_ID_ATS] = PCI_EXT_CAP_ATS_SIZEOF, + [PCI_EXT_CAP_ID_SRIOV] = PCI_EXT_CAP_SRIOV_SIZEOF, + [PCI_EXT_CAP_ID_MRIOV] = 0, /* not yet */ + [PCI_EXT_CAP_ID_MCAST] = PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF, + [PCI_EXT_CAP_ID_PRI] = PCI_EXT_CAP_PRI_SIZEOF, + [PCI_EXT_CAP_ID_AMD_XXX] = 0, /* not yet */ + [PCI_EXT_CAP_ID_REBAR] = 0xFF, + [PCI_EXT_CAP_ID_DPA] = 0xFF, + [PCI_EXT_CAP_ID_TPH] = 0xFF, + [PCI_EXT_CAP_ID_LTR] = PCI_EXT_CAP_LTR_SIZEOF, + [PCI_EXT_CAP_ID_SECPCI] = 0, /* not yet */ + [PCI_EXT_CAP_ID_PMUX] = 0, /* not yet */ + [PCI_EXT_CAP_ID_PASID] = 0, /* not yet */ +}; + +/* + * Read/Write Permission Bits - one bit for each bit in capability + * Any field can be read if it exists, but what is read depends on + * whether the field is 'virtualized', or just pass thru to the + * hardware. Any virtualized field is also virtualized for writes. + * Writes are only permitted if they have a 1 bit here. + */ +struct perm_bits { + u8 *virt; /* read/write virtual data, not hw */ + u8 *write; /* writeable bits */ + int (*readfn)(struct vfio_pci_device *vdev, int pos, int count, + struct perm_bits *perm, int offset, __le32 *val); + int (*writefn)(struct vfio_pci_device *vdev, int pos, int count, + struct perm_bits *perm, int offset, __le32 val); +}; + +#define NO_VIRT 0 +#define ALL_VIRT 0xFFFFFFFFU +#define NO_WRITE 0 +#define ALL_WRITE 0xFFFFFFFFU + +static int vfio_user_config_read(struct pci_dev *pdev, int offset, + __le32 *val, int count) +{ + int ret = -EINVAL; + u32 tmp_val = 0; + + switch (count) { + case 1: + { + u8 tmp; + ret = pci_user_read_config_byte(pdev, offset, &tmp); + tmp_val = tmp; + break; + } + case 2: + { + u16 tmp; + ret = pci_user_read_config_word(pdev, offset, &tmp); + tmp_val = tmp; + break; + } + case 4: + ret = pci_user_read_config_dword(pdev, offset, &tmp_val); + break; + } + + *val = cpu_to_le32(tmp_val); + + return pcibios_err_to_errno(ret); +} + +static int vfio_user_config_write(struct pci_dev *pdev, int offset, + __le32 val, int count) +{ + int ret = -EINVAL; + u32 tmp_val = le32_to_cpu(val); + + switch (count) { + case 1: + ret = pci_user_write_config_byte(pdev, offset, tmp_val); + break; + case 2: + ret = pci_user_write_config_word(pdev, offset, tmp_val); + break; + case 4: + ret = pci_user_write_config_dword(pdev, offset, tmp_val); + break; + } + + return pcibios_err_to_errno(ret); +} + +static int vfio_default_config_read(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 *val) +{ + __le32 virt = 0; + + memcpy(val, vdev->vconfig + pos, count); + + memcpy(&virt, perm->virt + offset, count); + + /* Any non-virtualized bits? */ + if (cpu_to_le32(~0U >> (32 - (count * 8))) != virt) { + struct pci_dev *pdev = vdev->pdev; + __le32 phys_val = 0; + int ret; + + ret = vfio_user_config_read(pdev, pos, &phys_val, count); + if (ret) + return ret; + + *val = (phys_val & ~virt) | (*val & virt); + } + + return count; +} + +static int vfio_default_config_write(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 val) +{ + __le32 virt = 0, write = 0; + + memcpy(&write, perm->write + offset, count); + + if (!write) + return count; /* drop, no writable bits */ + + memcpy(&virt, perm->virt + offset, count); + + /* Virtualized and writable bits go to vconfig */ + if (write & virt) { + __le32 virt_val = 0; + + memcpy(&virt_val, vdev->vconfig + pos, count); + + virt_val &= ~(write & virt); + virt_val |= (val & (write & virt)); + + memcpy(vdev->vconfig + pos, &virt_val, count); + } + + /* Non-virtualzed and writable bits go to hardware */ + if (write & ~virt) { + struct pci_dev *pdev = vdev->pdev; + __le32 phys_val = 0; + int ret; + + ret = vfio_user_config_read(pdev, pos, &phys_val, count); + if (ret) + return ret; + + phys_val &= ~(write & ~virt); + phys_val |= (val & (write & ~virt)); + + ret = vfio_user_config_write(pdev, pos, phys_val, count); + if (ret) + return ret; + } + + return count; +} + +/* Allow direct read from hardware, except for capability next pointer */ +static int vfio_direct_config_read(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 *val) +{ + int ret; + + ret = vfio_user_config_read(vdev->pdev, pos, val, count); + if (ret) + return pcibios_err_to_errno(ret); + + if (pos >= PCI_CFG_SPACE_SIZE) { /* Extended cap header mangling */ + if (offset < 4) + memcpy(val, vdev->vconfig + pos, count); + } else if (pos >= PCI_STD_HEADER_SIZEOF) { /* Std cap mangling */ + if (offset == PCI_CAP_LIST_ID && count > 1) + memcpy(val, vdev->vconfig + pos, + min(PCI_CAP_FLAGS, count)); + else if (offset == PCI_CAP_LIST_NEXT) + memcpy(val, vdev->vconfig + pos, 1); + } + + return count; +} + +static int vfio_direct_config_write(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 val) +{ + int ret; + + ret = vfio_user_config_write(vdev->pdev, pos, val, count); + if (ret) + return ret; + + return count; +} + +/* Default all regions to read-only, no-virtualization */ +static struct perm_bits cap_perms[PCI_CAP_ID_MAX + 1] = { + [0 ... PCI_CAP_ID_MAX] = { .readfn = vfio_direct_config_read } +}; +static struct perm_bits ecap_perms[PCI_EXT_CAP_ID_MAX + 1] = { + [0 ... PCI_EXT_CAP_ID_MAX] = { .readfn = vfio_direct_config_read } +}; + +static void free_perm_bits(struct perm_bits *perm) +{ + kfree(perm->virt); + kfree(perm->write); + perm->virt = NULL; + perm->write = NULL; +} + +static int alloc_perm_bits(struct perm_bits *perm, int size) +{ + /* + * Round up all permission bits to the next dword, this lets us + * ignore whether a read/write exceeds the defined capability + * structure. We can do this because: + * - Standard config space is already dword aligned + * - Capabilities are all dword alinged (bits 0:1 of next reserved) + * - Express capabilities defined as dword aligned + */ + size = round_up(size, 4); + + /* + * Zero state is + * - All Readable, None Writeable, None Virtualized + */ + perm->virt = kzalloc(size, GFP_KERNEL); + perm->write = kzalloc(size, GFP_KERNEL); + if (!perm->virt || !perm->write) { + free_perm_bits(perm); + return -ENOMEM; + } + + perm->readfn = vfio_default_config_read; + perm->writefn = vfio_default_config_write; + + return 0; +} + +/* + * Helper functions for filling in permission tables + */ +static inline void p_setb(struct perm_bits *p, int off, u8 virt, u8 write) +{ + p->virt[off] = virt; + p->write[off] = write; +} + +/* Handle endian-ness - pci and tables are little-endian */ +static inline void p_setw(struct perm_bits *p, int off, u16 virt, u16 write) +{ + *(__le16 *)(&p->virt[off]) = cpu_to_le16(virt); + *(__le16 *)(&p->write[off]) = cpu_to_le16(write); +} + +/* Handle endian-ness - pci and tables are little-endian */ +static inline void p_setd(struct perm_bits *p, int off, u32 virt, u32 write) +{ + *(__le32 *)(&p->virt[off]) = cpu_to_le32(virt); + *(__le32 *)(&p->write[off]) = cpu_to_le32(write); +} + +/* + * Restore the *real* BARs after we detect a FLR or backdoor reset. + * (backdoor = some device specific technique that we didn't catch) + */ +static void vfio_bar_restore(struct vfio_pci_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + u32 *rbar = vdev->rbar; + int i; + + if (pdev->is_virtfn) + return; + + pr_info("%s: %s reset recovery - restoring bars\n", + __func__, dev_name(&pdev->dev)); + + for (i = PCI_BASE_ADDRESS_0; i <= PCI_BASE_ADDRESS_5; i += 4, rbar++) + pci_user_write_config_dword(pdev, i, *rbar); + + pci_user_write_config_dword(pdev, PCI_ROM_ADDRESS, *rbar); +} + +static __le32 vfio_generate_bar_flags(struct pci_dev *pdev, int bar) +{ + unsigned long flags = pci_resource_flags(pdev, bar); + u32 val; + + if (flags & IORESOURCE_IO) + return cpu_to_le32(PCI_BASE_ADDRESS_SPACE_IO); + + val = PCI_BASE_ADDRESS_SPACE_MEMORY; + + if (flags & IORESOURCE_PREFETCH) + val |= PCI_BASE_ADDRESS_MEM_PREFETCH; + + if (flags & IORESOURCE_MEM_64) + val |= PCI_BASE_ADDRESS_MEM_TYPE_64; + + return cpu_to_le32(val); +} + +/* + * Pretend we're hardware and tweak the values of the *virtual* PCI BARs + * to reflect the hardware capabilities. This implements BAR sizing. + */ +static void vfio_bar_fixup(struct vfio_pci_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + int i; + __le32 *bar; + u64 mask; + + bar = (__le32 *)&vdev->vconfig[PCI_BASE_ADDRESS_0]; + + for (i = PCI_STD_RESOURCES; i <= PCI_STD_RESOURCE_END; i++, bar++) { + if (!pci_resource_start(pdev, i)) { + *bar = 0; /* Unmapped by host = unimplemented to user */ + continue; + } + + mask = ~(pci_resource_len(pdev, i) - 1); + + *bar &= cpu_to_le32((u32)mask); + *bar |= vfio_generate_bar_flags(pdev, i); + + if (*bar & cpu_to_le32(PCI_BASE_ADDRESS_MEM_TYPE_64)) { + bar++; + *bar &= cpu_to_le32((u32)(mask >> 32)); + i++; + } + } + + bar = (__le32 *)&vdev->vconfig[PCI_ROM_ADDRESS]; + + /* + * NB. we expose the actual BAR size here, regardless of whether + * we can read it. When we report the REGION_INFO for the ROM + * we report what PCI tells us is the actual ROM size. + */ + if (pci_resource_start(pdev, PCI_ROM_RESOURCE)) { + mask = ~(pci_resource_len(pdev, PCI_ROM_RESOURCE) - 1); + mask |= PCI_ROM_ADDRESS_ENABLE; + *bar &= cpu_to_le32((u32)mask); + } else + *bar = 0; + + vdev->bardirty = false; +} + +static int vfio_basic_config_read(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 *val) +{ + if (is_bar(offset)) /* pos == offset for basic config */ + vfio_bar_fixup(vdev); + + count = vfio_default_config_read(vdev, pos, count, perm, offset, val); + + /* Mask in virtual memory enable for SR-IOV devices */ + if (offset == PCI_COMMAND && vdev->pdev->is_virtfn) { + u16 cmd = le16_to_cpu(*(__le16 *)&vdev->vconfig[PCI_COMMAND]); + u32 tmp_val = le32_to_cpu(*val); + + tmp_val |= cmd & PCI_COMMAND_MEMORY; + *val = cpu_to_le32(tmp_val); + } + + return count; +} + +static int vfio_basic_config_write(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 val) +{ + struct pci_dev *pdev = vdev->pdev; + __le16 *virt_cmd; + u16 new_cmd = 0; + int ret; + + virt_cmd = (__le16 *)&vdev->vconfig[PCI_COMMAND]; + + if (offset == PCI_COMMAND) { + bool phys_mem, virt_mem, new_mem, phys_io, virt_io, new_io; + u16 phys_cmd; + + ret = pci_user_read_config_word(pdev, PCI_COMMAND, &phys_cmd); + if (ret) + return ret; + + new_cmd = le32_to_cpu(val); + + phys_mem = !!(phys_cmd & PCI_COMMAND_MEMORY); + virt_mem = !!(le16_to_cpu(*virt_cmd) & PCI_COMMAND_MEMORY); + new_mem = !!(new_cmd & PCI_COMMAND_MEMORY); + + phys_io = !!(phys_cmd & PCI_COMMAND_IO); + virt_io = !!(le16_to_cpu(*virt_cmd) & PCI_COMMAND_IO); + new_io = !!(new_cmd & PCI_COMMAND_IO); + + /* + * If the user is writing mem/io enable (new_mem/io) and we + * think it's already enabled (virt_mem/io), but the hardware + * shows it disabled (phys_mem/io, then the device has + * undergone some kind of backdoor reset and needs to be + * restored before we allow it to enable the bars. + * SR-IOV devices will trigger this, but we catch them later + */ + if ((new_mem && virt_mem && !phys_mem) || + (new_io && virt_io && !phys_io)) + vfio_bar_restore(vdev); + } + + count = vfio_default_config_write(vdev, pos, count, perm, offset, val); + if (count < 0) + return count; + + /* + * Save current memory/io enable bits in vconfig to allow for + * the test above next time. + */ + if (offset == PCI_COMMAND) { + u16 mask = PCI_COMMAND_MEMORY | PCI_COMMAND_IO; + + *virt_cmd &= cpu_to_le16(~mask); + *virt_cmd |= cpu_to_le16(new_cmd & mask); + } + + /* Emulate INTx disable */ + if (offset >= PCI_COMMAND && offset <= PCI_COMMAND + 1) { + bool virt_intx_disable; + + virt_intx_disable = !!(le16_to_cpu(*virt_cmd) & + PCI_COMMAND_INTX_DISABLE); + + if (virt_intx_disable && !vdev->virq_disabled) { + vdev->virq_disabled = true; + vfio_pci_intx_mask(vdev); + } else if (!virt_intx_disable && vdev->virq_disabled) { + vdev->virq_disabled = false; + vfio_pci_intx_unmask(vdev); + } + } + + if (is_bar(offset)) + vdev->bardirty = true; + + return count; +} + +/* Permissions for the Basic PCI Header */ +static int __init init_pci_cap_basic_perm(struct perm_bits *perm) +{ + if (alloc_perm_bits(perm, PCI_STD_HEADER_SIZEOF)) + return -ENOMEM; + + perm->readfn = vfio_basic_config_read; + perm->writefn = vfio_basic_config_write; + + /* Virtualized for SR-IOV functions, which just have FFFF */ + p_setw(perm, PCI_VENDOR_ID, (u16)ALL_VIRT, NO_WRITE); + p_setw(perm, PCI_DEVICE_ID, (u16)ALL_VIRT, NO_WRITE); + + /* + * Virtualize INTx disable, we use it internally for interrupt + * control and can emulate it for non-PCI 2.3 devices. + */ + p_setw(perm, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE, (u16)ALL_WRITE); + + /* Virtualize capability list, we might want to skip/disable */ + p_setw(perm, PCI_STATUS, PCI_STATUS_CAP_LIST, NO_WRITE); + + /* No harm to write */ + p_setb(perm, PCI_CACHE_LINE_SIZE, NO_VIRT, (u8)ALL_WRITE); + p_setb(perm, PCI_LATENCY_TIMER, NO_VIRT, (u8)ALL_WRITE); + p_setb(perm, PCI_BIST, NO_VIRT, (u8)ALL_WRITE); + + /* Virtualize all bars, can't touch the real ones */ + p_setd(perm, PCI_BASE_ADDRESS_0, ALL_VIRT, ALL_WRITE); + p_setd(perm, PCI_BASE_ADDRESS_1, ALL_VIRT, ALL_WRITE); + p_setd(perm, PCI_BASE_ADDRESS_2, ALL_VIRT, ALL_WRITE); + p_setd(perm, PCI_BASE_ADDRESS_3, ALL_VIRT, ALL_WRITE); + p_setd(perm, PCI_BASE_ADDRESS_4, ALL_VIRT, ALL_WRITE); + p_setd(perm, PCI_BASE_ADDRESS_5, ALL_VIRT, ALL_WRITE); + p_setd(perm, PCI_ROM_ADDRESS, ALL_VIRT, ALL_WRITE); + + /* Allow us to adjust capability chain */ + p_setb(perm, PCI_CAPABILITY_LIST, (u8)ALL_VIRT, NO_WRITE); + + /* Sometimes used by sw, just virtualize */ + p_setb(perm, PCI_INTERRUPT_LINE, (u8)ALL_VIRT, (u8)ALL_WRITE); + return 0; +} + +/* Permissions for the Power Management capability */ +static int __init init_pci_cap_pm_perm(struct perm_bits *perm) +{ + if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_PM])) + return -ENOMEM; + + /* + * We always virtualize the next field so we can remove + * capabilities from the chain if we want to. + */ + p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); + + /* + * Power management is defined *per function*, + * so we let the user write this + */ + p_setd(perm, PCI_PM_CTRL, NO_VIRT, ALL_WRITE); + return 0; +} + +/* Permissions for PCI-X capability */ +static int __init init_pci_cap_pcix_perm(struct perm_bits *perm) +{ + /* Alloc 24, but only 8 are used in v0 */ + if (alloc_perm_bits(perm, PCI_CAP_PCIX_SIZEOF_V2)) + return -ENOMEM; + + p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); + + p_setw(perm, PCI_X_CMD, NO_VIRT, (u16)ALL_WRITE); + p_setd(perm, PCI_X_ECC_CSR, NO_VIRT, ALL_WRITE); + return 0; +} + +/* Permissions for PCI Express capability */ +static int __init init_pci_cap_exp_perm(struct perm_bits *perm) +{ + /* Alloc larger of two possible sizes */ + if (alloc_perm_bits(perm, PCI_CAP_EXP_ENDPOINT_SIZEOF_V2)) + return -ENOMEM; + + p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); + + /* + * Allow writes to device control fields (includes FLR!) + * but not to devctl_phantom which could confuse IOMMU + * or to the ARI bit in devctl2 which is set at probe time + */ + p_setw(perm, PCI_EXP_DEVCTL, NO_VIRT, ~PCI_EXP_DEVCTL_PHANTOM); + p_setw(perm, PCI_EXP_DEVCTL2, NO_VIRT, ~PCI_EXP_DEVCTL2_ARI); + return 0; +} + +/* Permissions for Advanced Function capability */ +static int __init init_pci_cap_af_perm(struct perm_bits *perm) +{ + if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_AF])) + return -ENOMEM; + + p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); + p_setb(perm, PCI_AF_CTRL, NO_VIRT, PCI_AF_CTRL_FLR); + return 0; +} + +/* Permissions for Advanced Error Reporting extended capability */ +static int __init init_pci_ext_cap_err_perm(struct perm_bits *perm) +{ + u32 mask; + + if (alloc_perm_bits(perm, pci_ext_cap_length[PCI_EXT_CAP_ID_ERR])) + return -ENOMEM; + + /* + * Virtualize the first dword of all express capabilities + * because it includes the next pointer. This lets us later + * remove capabilities from the chain if we need to. + */ + p_setd(perm, 0, ALL_VIRT, NO_WRITE); + + /* Writable bits mask */ + mask = PCI_ERR_UNC_TRAIN | /* Training */ + PCI_ERR_UNC_DLP | /* Data Link Protocol */ + PCI_ERR_UNC_SURPDN | /* Surprise Down */ + PCI_ERR_UNC_POISON_TLP | /* Poisoned TLP */ + PCI_ERR_UNC_FCP | /* Flow Control Protocol */ + PCI_ERR_UNC_COMP_TIME | /* Completion Timeout */ + PCI_ERR_UNC_COMP_ABORT | /* Completer Abort */ + PCI_ERR_UNC_UNX_COMP | /* Unexpected Completion */ + PCI_ERR_UNC_RX_OVER | /* Receiver Overflow */ + PCI_ERR_UNC_MALF_TLP | /* Malformed TLP */ + PCI_ERR_UNC_ECRC | /* ECRC Error Status */ + PCI_ERR_UNC_UNSUP | /* Unsupported Request */ + PCI_ERR_UNC_ACSV | /* ACS Violation */ + PCI_ERR_UNC_INTN | /* internal error */ + PCI_ERR_UNC_MCBTLP | /* MC blocked TLP */ + PCI_ERR_UNC_ATOMEG | /* Atomic egress blocked */ + PCI_ERR_UNC_TLPPRE; /* TLP prefix blocked */ + p_setd(perm, PCI_ERR_UNCOR_STATUS, NO_VIRT, mask); + p_setd(perm, PCI_ERR_UNCOR_MASK, NO_VIRT, mask); + p_setd(perm, PCI_ERR_UNCOR_SEVER, NO_VIRT, mask); + + mask = PCI_ERR_COR_RCVR | /* Receiver Error Status */ + PCI_ERR_COR_BAD_TLP | /* Bad TLP Status */ + PCI_ERR_COR_BAD_DLLP | /* Bad DLLP Status */ + PCI_ERR_COR_REP_ROLL | /* REPLAY_NUM Rollover */ + PCI_ERR_COR_REP_TIMER | /* Replay Timer Timeout */ + PCI_ERR_COR_ADV_NFAT | /* Advisory Non-Fatal */ + PCI_ERR_COR_INTERNAL | /* Corrected Internal */ + PCI_ERR_COR_LOG_OVER; /* Header Log Overflow */ + p_setd(perm, PCI_ERR_COR_STATUS, NO_VIRT, mask); + p_setd(perm, PCI_ERR_COR_MASK, NO_VIRT, mask); + + mask = PCI_ERR_CAP_ECRC_GENE | /* ECRC Generation Enable */ + PCI_ERR_CAP_ECRC_CHKE; /* ECRC Check Enable */ + p_setd(perm, PCI_ERR_CAP, NO_VIRT, mask); + return 0; +} + +/* Permissions for Power Budgeting extended capability */ +static int __init init_pci_ext_cap_pwr_perm(struct perm_bits *perm) +{ + if (alloc_perm_bits(perm, pci_ext_cap_length[PCI_EXT_CAP_ID_PWR])) + return -ENOMEM; + + p_setd(perm, 0, ALL_VIRT, NO_WRITE); + + /* Writing the data selector is OK, the info is still read-only */ + p_setb(perm, PCI_PWR_DATA, NO_VIRT, (u8)ALL_WRITE); + return 0; +} + +/* + * Initialize the shared permission tables + */ +void vfio_pci_uninit_perm_bits(void) +{ + free_perm_bits(&cap_perms[PCI_CAP_ID_BASIC]); + + free_perm_bits(&cap_perms[PCI_CAP_ID_PM]); + free_perm_bits(&cap_perms[PCI_CAP_ID_PCIX]); + free_perm_bits(&cap_perms[PCI_CAP_ID_EXP]); + free_perm_bits(&cap_perms[PCI_CAP_ID_AF]); + + free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_ERR]); + free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_PWR]); +} + +int __init vfio_pci_init_perm_bits(void) +{ + int ret; + + /* Basic config space */ + ret = init_pci_cap_basic_perm(&cap_perms[PCI_CAP_ID_BASIC]); + + /* Capabilities */ + ret |= init_pci_cap_pm_perm(&cap_perms[PCI_CAP_ID_PM]); + cap_perms[PCI_CAP_ID_VPD].writefn = vfio_direct_config_write; + ret |= init_pci_cap_pcix_perm(&cap_perms[PCI_CAP_ID_PCIX]); + cap_perms[PCI_CAP_ID_VNDR].writefn = vfio_direct_config_write; + ret |= init_pci_cap_exp_perm(&cap_perms[PCI_CAP_ID_EXP]); + ret |= init_pci_cap_af_perm(&cap_perms[PCI_CAP_ID_AF]); + + /* Extended capabilities */ + ret |= init_pci_ext_cap_err_perm(&ecap_perms[PCI_EXT_CAP_ID_ERR]); + ret |= init_pci_ext_cap_pwr_perm(&ecap_perms[PCI_EXT_CAP_ID_PWR]); + ecap_perms[PCI_EXT_CAP_ID_VNDR].writefn = vfio_direct_config_write; + + if (ret) + vfio_pci_uninit_perm_bits(); + + return ret; +} + +static int vfio_find_cap_start(struct vfio_pci_device *vdev, int pos) +{ + u8 cap; + int base = (pos >= PCI_CFG_SPACE_SIZE) ? PCI_CFG_SPACE_SIZE : + PCI_STD_HEADER_SIZEOF; + base /= 4; + pos /= 4; + + cap = vdev->pci_config_map[pos]; + + if (cap == PCI_CAP_ID_BASIC) + return 0; + + /* XXX Can we have to abutting capabilities of the same type? */ + while (pos - 1 >= base && vdev->pci_config_map[pos - 1] == cap) + pos--; + + return pos * 4; +} + +static int vfio_msi_config_read(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 *val) +{ + /* Update max available queue size from msi_qmax */ + if (offset <= PCI_MSI_FLAGS && offset + count >= PCI_MSI_FLAGS) { + __le16 *flags; + int start; + + start = vfio_find_cap_start(vdev, pos); + + flags = (__le16 *)&vdev->vconfig[start]; + + *flags &= cpu_to_le16(~PCI_MSI_FLAGS_QMASK); + *flags |= cpu_to_le16(vdev->msi_qmax << 1); + } + + return vfio_default_config_read(vdev, pos, count, perm, offset, val); +} + +static int vfio_msi_config_write(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 val) +{ + count = vfio_default_config_write(vdev, pos, count, perm, offset, val); + if (count < 0) + return count; + + /* Fixup and write configured queue size and enable to hardware */ + if (offset <= PCI_MSI_FLAGS && offset + count >= PCI_MSI_FLAGS) { + __le16 *pflags; + u16 flags; + int start, ret; + + start = vfio_find_cap_start(vdev, pos); + + pflags = (__le16 *)&vdev->vconfig[start + PCI_MSI_FLAGS]; + + flags = le16_to_cpu(*pflags); + + /* MSI is enabled via ioctl */ + if (!is_msi(vdev)) + flags &= ~PCI_MSI_FLAGS_ENABLE; + + /* Check queue size */ + if ((flags & PCI_MSI_FLAGS_QSIZE) >> 4 > vdev->msi_qmax) { + flags &= ~PCI_MSI_FLAGS_QSIZE; + flags |= vdev->msi_qmax << 4; + } + + /* Write back to virt and to hardware */ + *pflags = cpu_to_le16(flags); + ret = pci_user_write_config_word(vdev->pdev, + start + PCI_MSI_FLAGS, + flags); + if (ret) + return pcibios_err_to_errno(ret); + } + + return count; +} + +/* + * MSI determination is per-device, so this routine gets used beyond + * initialization time. Don't add __init + */ +static int init_pci_cap_msi_perm(struct perm_bits *perm, int len, u16 flags) +{ + if (alloc_perm_bits(perm, len)) + return -ENOMEM; + + perm->readfn = vfio_msi_config_read; + perm->writefn = vfio_msi_config_write; + + p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); + + /* + * The upper byte of the control register is reserved, + * just setup the lower byte. + */ + p_setb(perm, PCI_MSI_FLAGS, (u8)ALL_VIRT, (u8)ALL_WRITE); + p_setd(perm, PCI_MSI_ADDRESS_LO, ALL_VIRT, ALL_WRITE); + if (flags & PCI_MSI_FLAGS_64BIT) { + p_setd(perm, PCI_MSI_ADDRESS_HI, ALL_VIRT, ALL_WRITE); + p_setw(perm, PCI_MSI_DATA_64, (u16)ALL_VIRT, (u16)ALL_WRITE); + if (flags & PCI_MSI_FLAGS_MASKBIT) { + p_setd(perm, PCI_MSI_MASK_64, NO_VIRT, ALL_WRITE); + p_setd(perm, PCI_MSI_PENDING_64, NO_VIRT, ALL_WRITE); + } + } else { + p_setw(perm, PCI_MSI_DATA_32, (u16)ALL_VIRT, (u16)ALL_WRITE); + if (flags & PCI_MSI_FLAGS_MASKBIT) { + p_setd(perm, PCI_MSI_MASK_32, NO_VIRT, ALL_WRITE); + p_setd(perm, PCI_MSI_PENDING_32, NO_VIRT, ALL_WRITE); + } + } + return 0; +} + +/* Determine MSI CAP field length; initialize msi_perms on 1st call per vdev */ +static int vfio_msi_cap_len(struct vfio_pci_device *vdev, u8 pos) +{ + struct pci_dev *pdev = vdev->pdev; + int len, ret; + u16 flags; + + ret = pci_read_config_word(pdev, pos + PCI_MSI_FLAGS, &flags); + if (ret) + return pcibios_err_to_errno(ret); + + len = 10; /* Minimum size */ + if (flags & PCI_MSI_FLAGS_64BIT) + len += 4; + if (flags & PCI_MSI_FLAGS_MASKBIT) + len += 10; + + if (vdev->msi_perm) + return len; + + vdev->msi_perm = kmalloc(sizeof(struct perm_bits), GFP_KERNEL); + if (!vdev->msi_perm) + return -ENOMEM; + + ret = init_pci_cap_msi_perm(vdev->msi_perm, len, flags); + if (ret) + return ret; + + return len; +} + +/* Determine extended capability length for VC (2 & 9) and MFVC */ +static int vfio_vc_cap_len(struct vfio_pci_device *vdev, u16 pos) +{ + struct pci_dev *pdev = vdev->pdev; + u32 tmp; + int ret, evcc, phases, vc_arb; + int len = PCI_CAP_VC_BASE_SIZEOF; + + ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG1, &tmp); + if (ret) + return pcibios_err_to_errno(ret); + + evcc = tmp & PCI_VC_REG1_EVCC; /* extended vc count */ + ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG2, &tmp); + if (ret) + return pcibios_err_to_errno(ret); + + if (tmp & PCI_VC_REG2_128_PHASE) + phases = 128; + else if (tmp & PCI_VC_REG2_64_PHASE) + phases = 64; + else if (tmp & PCI_VC_REG2_32_PHASE) + phases = 32; + else + phases = 0; + + vc_arb = phases * 4; + + /* + * Port arbitration tables are root & switch only; + * function arbitration tables are function 0 only. + * In either case, we'll never let user write them so + * we don't care how big they are + */ + len += (1 + evcc) * PCI_CAP_VC_PER_VC_SIZEOF; + if (vc_arb) { + len = round_up(len, 16); + len += vc_arb / 8; + } + return len; +} + +static int vfio_cap_len(struct vfio_pci_device *vdev, u8 cap, u8 pos) +{ + struct pci_dev *pdev = vdev->pdev; + u16 word; + u8 byte; + int ret; + + switch (cap) { + case PCI_CAP_ID_MSI: + return vfio_msi_cap_len(vdev, pos); + case PCI_CAP_ID_PCIX: + ret = pci_read_config_word(pdev, pos + PCI_X_CMD, &word); + if (ret) + return pcibios_err_to_errno(ret); + + if (PCI_X_CMD_VERSION(word)) { + vdev->extended_caps = true; + return PCI_CAP_PCIX_SIZEOF_V2; + } else + return PCI_CAP_PCIX_SIZEOF_V0; + case PCI_CAP_ID_VNDR: + /* length follows next field */ + ret = pci_read_config_byte(pdev, pos + PCI_CAP_FLAGS, &byte); + if (ret) + return pcibios_err_to_errno(ret); + + return byte; + case PCI_CAP_ID_EXP: + /* length based on version */ + ret = pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &word); + if (ret) + return pcibios_err_to_errno(ret); + + if ((word & PCI_EXP_FLAGS_VERS) == 1) + return PCI_CAP_EXP_ENDPOINT_SIZEOF_V1; + else { + vdev->extended_caps = true; + return PCI_CAP_EXP_ENDPOINT_SIZEOF_V2; + } + case PCI_CAP_ID_HT: + ret = pci_read_config_byte(pdev, pos + 3, &byte); + if (ret) + return pcibios_err_to_errno(ret); + + return (byte & HT_3BIT_CAP_MASK) ? + HT_CAP_SIZEOF_SHORT : HT_CAP_SIZEOF_LONG; + case PCI_CAP_ID_SATA: + ret = pci_read_config_byte(pdev, pos + PCI_SATA_REGS, &byte); + if (ret) + return pcibios_err_to_errno(ret); + + byte &= PCI_SATA_REGS_MASK; + if (byte == PCI_SATA_REGS_INLINE) + return PCI_SATA_SIZEOF_LONG; + else + return PCI_SATA_SIZEOF_SHORT; + default: + pr_warn("%s: %s unknown length for pci cap 0x%x@0x%x\n", + dev_name(&pdev->dev), __func__, cap, pos); + } + + return 0; +} + +static int vfio_ext_cap_len(struct vfio_pci_device *vdev, u16 ecap, u16 epos) +{ + struct pci_dev *pdev = vdev->pdev; + u8 byte; + u32 dword; + int ret; + + switch (ecap) { + case PCI_EXT_CAP_ID_VNDR: + ret = pci_read_config_dword(pdev, epos + PCI_VSEC_HDR, &dword); + if (ret) + return pcibios_err_to_errno(ret); + + return dword >> PCI_VSEC_HDR_LEN_SHIFT; + case PCI_EXT_CAP_ID_VC: + case PCI_EXT_CAP_ID_VC9: + case PCI_EXT_CAP_ID_MFVC: + return vfio_vc_cap_len(vdev, epos); + case PCI_EXT_CAP_ID_ACS: + ret = pci_read_config_byte(pdev, epos + PCI_ACS_CAP, &byte); + if (ret) + return pcibios_err_to_errno(ret); + + if (byte & PCI_ACS_EC) { + int bits; + + ret = pci_read_config_byte(pdev, + epos + PCI_ACS_EGRESS_BITS, + &byte); + if (ret) + return pcibios_err_to_errno(ret); + + bits = byte ? round_up(byte, 32) : 256; + return 8 + (bits / 8); + } + return 8; + + case PCI_EXT_CAP_ID_REBAR: + ret = pci_read_config_byte(pdev, epos + PCI_REBAR_CTRL, &byte); + if (ret) + return pcibios_err_to_errno(ret); + + byte &= PCI_REBAR_CTRL_NBAR_MASK; + byte >>= PCI_REBAR_CTRL_NBAR_SHIFT; + + return 4 + (byte * 8); + case PCI_EXT_CAP_ID_DPA: + ret = pci_read_config_byte(pdev, epos + PCI_DPA_CAP, &byte); + if (ret) + return pcibios_err_to_errno(ret); + + byte &= PCI_DPA_CAP_SUBSTATE_MASK; + byte = round_up(byte + 1, 4); + return PCI_DPA_BASE_SIZEOF + byte; + case PCI_EXT_CAP_ID_TPH: + ret = pci_read_config_dword(pdev, epos + PCI_TPH_CAP, &dword); + if (ret) + return pcibios_err_to_errno(ret); + + if ((dword & PCI_TPH_CAP_LOC_MASK) == PCI_TPH_LOC_CAP) { + int sts; + + sts = byte & PCI_TPH_CAP_ST_MASK; + sts >>= PCI_TPH_CAP_ST_SHIFT; + return PCI_TPH_BASE_SIZEOF + round_up(sts * 2, 4); + } + return PCI_TPH_BASE_SIZEOF; + default: + pr_warn("%s: %s unknown length for pci ecap 0x%x@0x%x\n", + dev_name(&pdev->dev), __func__, ecap, epos); + } + + return 0; +} + +static int vfio_fill_vconfig_bytes(struct vfio_pci_device *vdev, + int offset, int size) +{ + struct pci_dev *pdev = vdev->pdev; + int ret = 0; + + /* + * We try to read physical config space in the largest chunks + * we can, assuming that all of the fields support dword access. + * pci_save_state() makes this same assumption and seems to do ok. + */ + while (size) { + int filled; + + if (size >= 4 && !(offset % 4)) { + __le32 *dwordp = (__le32 *)&vdev->vconfig[offset]; + u32 dword; + + ret = pci_read_config_dword(pdev, offset, &dword); + if (ret) + return ret; + *dwordp = cpu_to_le32(dword); + filled = 4; + } else if (size >= 2 && !(offset % 2)) { + __le16 *wordp = (__le16 *)&vdev->vconfig[offset]; + u16 word; + + ret = pci_read_config_word(pdev, offset, &word); + if (ret) + return ret; + *wordp = cpu_to_le16(word); + filled = 2; + } else { + u8 *byte = &vdev->vconfig[offset]; + ret = pci_read_config_byte(pdev, offset, byte); + if (ret) + return ret; + filled = 1; + } + + offset += filled; + size -= filled; + } + + return ret; +} + +static int vfio_cap_init(struct vfio_pci_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + u8 *map = vdev->pci_config_map; + u16 status; + u8 pos, *prev, cap; + int loops, ret, caps = 0; + + /* Any capabilities? */ + ret = pci_read_config_word(pdev, PCI_STATUS, &status); + if (ret) + return ret; + + if (!(status & PCI_STATUS_CAP_LIST)) + return 0; /* Done */ + + ret = pci_read_config_byte(pdev, PCI_CAPABILITY_LIST, &pos); + if (ret) + return ret; + + /* Mark the previous position in case we want to skip a capability */ + prev = &vdev->vconfig[PCI_CAPABILITY_LIST]; + + /* We can bound our loop, capabilities are dword aligned */ + loops = (PCI_CFG_SPACE_SIZE - PCI_STD_HEADER_SIZEOF) / PCI_CAP_SIZEOF; + while (pos && loops--) { + u8 next; + int i, len = 0; + + ret = pci_read_config_byte(pdev, pos, &cap); + if (ret) + return ret; + + ret = pci_read_config_byte(pdev, + pos + PCI_CAP_LIST_NEXT, &next); + if (ret) + return ret; + + if (cap <= PCI_CAP_ID_MAX) { + len = pci_cap_length[cap]; + if (len == 0xFF) { /* Variable length */ + len = vfio_cap_len(vdev, cap, pos); + if (len < 0) + return len; + } + } + + if (!len) { + pr_info("%s: %s hiding cap 0x%x\n", + __func__, dev_name(&pdev->dev), cap); + *prev = next; + pos = next; + continue; + } + + /* Sanity check, do we overlap other capabilities? */ + for (i = 0; i < len; i += 4) { + if (likely(map[(pos + i) / 4] == PCI_CAP_ID_INVALID)) + continue; + + pr_warn("%s: %s pci config conflict @0x%x, was cap 0x%x now cap 0x%x\n", + __func__, dev_name(&pdev->dev), + pos + i, map[pos + i], cap); + } + + memset(map + (pos / 4), cap, len / 4); + ret = vfio_fill_vconfig_bytes(vdev, pos, len); + if (ret) + return ret; + + prev = &vdev->vconfig[pos + PCI_CAP_LIST_NEXT]; + pos = next; + caps++; + } + + /* If we didn't fill any capabilities, clear the status flag */ + if (!caps) { + __le16 *vstatus = (__le16 *)&vdev->vconfig[PCI_STATUS]; + *vstatus &= ~cpu_to_le16(PCI_STATUS_CAP_LIST); + } + + return 0; +} + +static int vfio_ecap_init(struct vfio_pci_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + u8 *map = vdev->pci_config_map; + u16 epos; + __le32 *prev = NULL; + int loops, ret, ecaps = 0; + + if (!vdev->extended_caps) + return 0; + + epos = PCI_CFG_SPACE_SIZE; + + loops = (pdev->cfg_size - PCI_CFG_SPACE_SIZE) / PCI_CAP_SIZEOF; + + while (loops-- && epos >= PCI_CFG_SPACE_SIZE) { + u32 header; + u16 ecap; + int i, len = 0; + bool hidden = false; + + ret = pci_read_config_dword(pdev, epos, &header); + if (ret) + return ret; + + ecap = PCI_EXT_CAP_ID(header); + + if (ecap <= PCI_EXT_CAP_ID_MAX) { + len = pci_ext_cap_length[ecap]; + if (len == 0xFF) { + len = vfio_ext_cap_len(vdev, ecap, epos); + if (len < 0) + return ret; + } + } + + if (!len) { + pr_info("%s: %s hiding ecap 0x%x@0x%x\n", + __func__, dev_name(&pdev->dev), ecap, epos); + + /* If not the first in the chain, we can skip over it */ + if (prev) { + u32 val = epos = PCI_EXT_CAP_NEXT(header); + *prev &= cpu_to_le32(~(0xffcU << 20)); + *prev |= cpu_to_le32(val << 20); + continue; + } + + /* + * Otherwise, fill in a placeholder, the direct + * readfn will virtualize this automatically + */ + len = PCI_CAP_SIZEOF; + hidden = true; + } + + for (i = 0; i < len; i += 4) { + if (likely(map[(epos + i) / 4] == PCI_CAP_ID_INVALID)) + continue; + + pr_warn("%s: %s pci config conflict @0x%x, was ecap 0x%x now ecap 0x%x\n", + __func__, dev_name(&pdev->dev), + epos + i, map[epos + i], ecap); + } + + /* + * Even though ecap is 2 bytes, we're currently a long way + * from exceeding 1 byte capabilities. If we ever make it + * up to 0xFF we'll need to up this to a two-byte, byte map. + */ + BUILD_BUG_ON(PCI_EXT_CAP_ID_MAX >= PCI_CAP_ID_INVALID); + + memset(map + (epos / 4), ecap, len / 4); + ret = vfio_fill_vconfig_bytes(vdev, epos, len); + if (ret) + return ret; + + /* + * If we're just using this capability to anchor the list, + * hide the real ID. Only count real ecaps. XXX PCI spec + * indicates to use cap id = 0, version = 0, next = 0 if + * ecaps are absent, hope users check all the way to next. + */ + if (hidden) + *(__le32 *)&vdev->vconfig[epos] &= + cpu_to_le32((0xffcU << 20)); + else + ecaps++; + + prev = (__le32 *)&vdev->vconfig[epos]; + epos = PCI_EXT_CAP_NEXT(header); + } + + if (!ecaps) + *(u32 *)&vdev->vconfig[PCI_CFG_SPACE_SIZE] = 0; + + return 0; +} + +/* + * For each device we allocate a pci_config_map that indicates the + * capability occupying each dword and thus the struct perm_bits we + * use for read and write. We also allocate a virtualized config + * space which tracks reads and writes to bits that we emulate for + * the user. Initial values filled from device. + * + * Using shared stuct perm_bits between all vfio-pci devices saves + * us from allocating cfg_size buffers for virt and write for every + * device. We could remove vconfig and allocate individual buffers + * for each area requring emulated bits, but the array of pointers + * would be comparable in size (at least for standard config space). + */ +int vfio_config_init(struct vfio_pci_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + u8 *map, *vconfig; + int ret; + + /* + * Config space, caps and ecaps are all dword aligned, so we can + * use one byte per dword to record the type. + */ + map = kmalloc(pdev->cfg_size / 4, GFP_KERNEL); + if (!map) + return -ENOMEM; + + vconfig = kmalloc(pdev->cfg_size, GFP_KERNEL); + if (!vconfig) { + kfree(map); + return -ENOMEM; + } + + vdev->pci_config_map = map; + vdev->vconfig = vconfig; + + memset(map, PCI_CAP_ID_BASIC, PCI_STD_HEADER_SIZEOF / 4); + memset(map + (PCI_STD_HEADER_SIZEOF / 4), PCI_CAP_ID_INVALID, + (pdev->cfg_size - PCI_STD_HEADER_SIZEOF) / 4); + + ret = vfio_fill_vconfig_bytes(vdev, 0, PCI_STD_HEADER_SIZEOF); + if (ret) + goto out; + + vdev->bardirty = true; + + /* + * XXX can we just pci_load_saved_state/pci_restore_state? + * may need to rebuild vconfig after that + */ + + /* For restore after reset */ + vdev->rbar[0] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_0]); + vdev->rbar[1] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_1]); + vdev->rbar[2] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_2]); + vdev->rbar[3] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_3]); + vdev->rbar[4] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_4]); + vdev->rbar[5] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_5]); + vdev->rbar[6] = le32_to_cpu(*(__le32 *)&vconfig[PCI_ROM_ADDRESS]); + + if (pdev->is_virtfn) { + *(__le16 *)&vconfig[PCI_VENDOR_ID] = cpu_to_le16(pdev->vendor); + *(__le16 *)&vconfig[PCI_DEVICE_ID] = cpu_to_le16(pdev->device); + } + + ret = vfio_cap_init(vdev); + if (ret) + goto out; + + ret = vfio_ecap_init(vdev); + if (ret) + goto out; + + return 0; + +out: + kfree(map); + vdev->pci_config_map = NULL; + kfree(vconfig); + vdev->vconfig = NULL; + return pcibios_err_to_errno(ret); +} + +void vfio_config_free(struct vfio_pci_device *vdev) +{ + kfree(vdev->vconfig); + vdev->vconfig = NULL; + kfree(vdev->pci_config_map); + vdev->pci_config_map = NULL; + kfree(vdev->msi_perm); + vdev->msi_perm = NULL; +} + +static ssize_t vfio_config_do_rw(struct vfio_pci_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite) +{ + struct pci_dev *pdev = vdev->pdev; + struct perm_bits *perm; + __le32 val = 0; + int cap_start = 0, offset; + u8 cap_id; + ssize_t ret = count; + + if (*ppos < 0 || *ppos + count > pdev->cfg_size) + return -EFAULT; + + /* + * gcc can't seem to figure out we're a static function, only called + * with count of 1/2/4 and hits copy_from_user_overflow without this. + */ + if (count > sizeof(val)) + return -EINVAL; + + cap_id = vdev->pci_config_map[*ppos / 4]; + + if (cap_id == PCI_CAP_ID_INVALID) { + if (iswrite) + return ret; /* drop */ + + /* + * Per PCI spec 3.0, section 6.1, reads from reserved and + * unimplemented registers return 0 + */ + if (copy_to_user(buf, &val, count)) + return -EFAULT; + + return ret; + } + + /* + * All capabilities are minimum 4 bytes and aligned on dword + * boundaries. Since we don't support unaligned accesses, we're + * only ever accessing a single capability. + */ + if (*ppos >= PCI_CFG_SPACE_SIZE) { + WARN_ON(cap_id > PCI_EXT_CAP_ID_MAX); + + perm = &ecap_perms[cap_id]; + cap_start = vfio_find_cap_start(vdev, *ppos); + + } else { + WARN_ON(cap_id > PCI_CAP_ID_MAX); + + perm = &cap_perms[cap_id]; + + if (cap_id == PCI_CAP_ID_MSI) + perm = vdev->msi_perm; + + if (cap_id > PCI_CAP_ID_BASIC) + cap_start = vfio_find_cap_start(vdev, *ppos); + } + + WARN_ON(!cap_start && cap_id != PCI_CAP_ID_BASIC); + WARN_ON(cap_start > *ppos); + + offset = *ppos - cap_start; + + if (iswrite) { + if (!perm->writefn) + return ret; + + if (copy_from_user(&val, buf, count)) + return -EFAULT; + + ret = perm->writefn(vdev, *ppos, count, perm, offset, val); + } else { + if (perm->readfn) { + ret = perm->readfn(vdev, *ppos, count, + perm, offset, &val); + if (ret < 0) + return ret; + } + + if (copy_to_user(buf, &val, count)) + return -EFAULT; + } + + return ret; +} + +ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev, + char __user *buf, size_t count, + loff_t *ppos, bool iswrite) +{ + size_t done = 0; + int ret = 0; + loff_t pos = *ppos; + + pos &= VFIO_PCI_OFFSET_MASK; + + /* + * We want to both keep the access size the caller users as well as + * support reading large chunks of config space in a single call. + * PCI doesn't support unaligned accesses, so we can safely break + * those apart. + */ + while (count) { + if (count >= 4 && !(pos % 4)) + ret = vfio_config_do_rw(vdev, buf, 4, &pos, iswrite); + else if (count >= 2 && !(pos % 2)) + ret = vfio_config_do_rw(vdev, buf, 2, &pos, iswrite); + else + ret = vfio_config_do_rw(vdev, buf, 1, &pos, iswrite); + + if (ret < 0) + return ret; + + count -= ret; + done += ret; + buf += ret; + pos += ret; + } + + *ppos += done; + + return done; +} diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c new file mode 100644 index 000000000000..211a4920b88a --- /dev/null +++ b/drivers/vfio/pci/vfio_pci_intrs.c @@ -0,0 +1,740 @@ +/* + * VFIO PCI interrupt handling + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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. + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vfio_pci_private.h" + +/* + * IRQfd - generic + */ +struct virqfd { + struct vfio_pci_device *vdev; + struct eventfd_ctx *eventfd; + int (*handler)(struct vfio_pci_device *, void *); + void (*thread)(struct vfio_pci_device *, void *); + void *data; + struct work_struct inject; + wait_queue_t wait; + poll_table pt; + struct work_struct shutdown; + struct virqfd **pvirqfd; +}; + +static struct workqueue_struct *vfio_irqfd_cleanup_wq; + +int __init vfio_pci_virqfd_init(void) +{ + vfio_irqfd_cleanup_wq = + create_singlethread_workqueue("vfio-irqfd-cleanup"); + if (!vfio_irqfd_cleanup_wq) + return -ENOMEM; + + return 0; +} + +void vfio_pci_virqfd_exit(void) +{ + destroy_workqueue(vfio_irqfd_cleanup_wq); +} + +static void virqfd_deactivate(struct virqfd *virqfd) +{ + queue_work(vfio_irqfd_cleanup_wq, &virqfd->shutdown); +} + +static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key) +{ + struct virqfd *virqfd = container_of(wait, struct virqfd, wait); + unsigned long flags = (unsigned long)key; + + if (flags & POLLIN) { + /* An event has been signaled, call function */ + if ((!virqfd->handler || + virqfd->handler(virqfd->vdev, virqfd->data)) && + virqfd->thread) + schedule_work(&virqfd->inject); + } + + if (flags & POLLHUP) + /* The eventfd is closing, detach from VFIO */ + virqfd_deactivate(virqfd); + + return 0; +} + +static void virqfd_ptable_queue_proc(struct file *file, + wait_queue_head_t *wqh, poll_table *pt) +{ + struct virqfd *virqfd = container_of(pt, struct virqfd, pt); + add_wait_queue(wqh, &virqfd->wait); +} + +static void virqfd_shutdown(struct work_struct *work) +{ + struct virqfd *virqfd = container_of(work, struct virqfd, shutdown); + struct virqfd **pvirqfd = virqfd->pvirqfd; + u64 cnt; + + eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt); + flush_work(&virqfd->inject); + eventfd_ctx_put(virqfd->eventfd); + + kfree(virqfd); + *pvirqfd = NULL; +} + +static void virqfd_inject(struct work_struct *work) +{ + struct virqfd *virqfd = container_of(work, struct virqfd, inject); + if (virqfd->thread) + virqfd->thread(virqfd->vdev, virqfd->data); +} + +static int virqfd_enable(struct vfio_pci_device *vdev, + int (*handler)(struct vfio_pci_device *, void *), + void (*thread)(struct vfio_pci_device *, void *), + void *data, struct virqfd **pvirqfd, int fd) +{ + struct file *file = NULL; + struct eventfd_ctx *ctx = NULL; + struct virqfd *virqfd; + int ret = 0; + unsigned int events; + + if (*pvirqfd) + return -EBUSY; + + virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL); + if (!virqfd) + return -ENOMEM; + + virqfd->pvirqfd = pvirqfd; + *pvirqfd = virqfd; + virqfd->vdev = vdev; + virqfd->handler = handler; + virqfd->thread = thread; + virqfd->data = data; + + INIT_WORK(&virqfd->shutdown, virqfd_shutdown); + INIT_WORK(&virqfd->inject, virqfd_inject); + + file = eventfd_fget(fd); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto fail; + } + + ctx = eventfd_ctx_fileget(file); + if (IS_ERR(ctx)) { + ret = PTR_ERR(ctx); + goto fail; + } + + virqfd->eventfd = ctx; + + /* + * Install our own custom wake-up handling so we are notified via + * a callback whenever someone signals the underlying eventfd. + */ + init_waitqueue_func_entry(&virqfd->wait, virqfd_wakeup); + init_poll_funcptr(&virqfd->pt, virqfd_ptable_queue_proc); + + events = file->f_op->poll(file, &virqfd->pt); + + /* + * Check if there was an event already pending on the eventfd + * before we registered and trigger it as if we didn't miss it. + */ + if (events & POLLIN) { + if ((!handler || handler(vdev, data)) && thread) + schedule_work(&virqfd->inject); + } + + /* + * Do not drop the file until the irqfd is fully initialized, + * otherwise we might race against the POLLHUP. + */ + fput(file); + + return 0; + +fail: + if (ctx && !IS_ERR(ctx)) + eventfd_ctx_put(ctx); + + if (file && !IS_ERR(file)) + fput(file); + + kfree(virqfd); + *pvirqfd = NULL; + + return ret; +} + +static void virqfd_disable(struct virqfd *virqfd) +{ + if (!virqfd) + return; + + virqfd_deactivate(virqfd); + + /* Block until we know all outstanding shutdown jobs have completed. */ + flush_workqueue(vfio_irqfd_cleanup_wq); +} + +/* + * INTx + */ +static void vfio_send_intx_eventfd(struct vfio_pci_device *vdev, void *unused) +{ + if (likely(is_intx(vdev) && !vdev->virq_disabled)) + eventfd_signal(vdev->ctx[0].trigger, 1); +} + +void vfio_pci_intx_mask(struct vfio_pci_device *vdev) +{ + struct pci_dev *pdev = vdev->pdev; + unsigned long flags; + + spin_lock_irqsave(&vdev->irqlock, flags); + + /* + * Masking can come from interrupt, ioctl, or config space + * via INTx disable. The latter means this can get called + * even when not using intx delivery. In this case, just + * try to have the physical bit follow the virtual bit. + */ + if (unlikely(!is_intx(vdev))) { + if (vdev->pci_2_3) + pci_intx(pdev, 0); + } else if (!vdev->ctx[0].masked) { + /* + * Can't use check_and_mask here because we always want to + * mask, not just when something is pending. + */ + if (vdev->pci_2_3) + pci_intx(pdev, 0); + else + disable_irq_nosync(pdev->irq); + + vdev->ctx[0].masked = true; + } + + spin_unlock_irqrestore(&vdev->irqlock, flags); +} + +/* + * If this is triggered by an eventfd, we can't call eventfd_signal + * or else we'll deadlock on the eventfd wait queue. Return >0 when + * a signal is necessary, which can then be handled via a work queue + * or directly depending on the caller. + */ +int vfio_pci_intx_unmask_handler(struct vfio_pci_device *vdev, void *unused) +{ + struct pci_dev *pdev = vdev->pdev; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&vdev->irqlock, flags); + + /* + * Unmasking comes from ioctl or config, so again, have the + * physical bit follow the virtual even when not using INTx. + */ + if (unlikely(!is_intx(vdev))) { + if (vdev->pci_2_3) + pci_intx(pdev, 1); + } else if (vdev->ctx[0].masked && !vdev->virq_disabled) { + /* + * A pending interrupt here would immediately trigger, + * but we can avoid that overhead by just re-sending + * the interrupt to the user. + */ + if (vdev->pci_2_3) { + if (!pci_check_and_unmask_intx(pdev)) + ret = 1; + } else + enable_irq(pdev->irq); + + vdev->ctx[0].masked = (ret > 0); + } + + spin_unlock_irqrestore(&vdev->irqlock, flags); + + return ret; +} + +void vfio_pci_intx_unmask(struct vfio_pci_device *vdev) +{ + if (vfio_pci_intx_unmask_handler(vdev, NULL) > 0) + vfio_send_intx_eventfd(vdev, NULL); +} + +static irqreturn_t vfio_intx_handler(int irq, void *dev_id) +{ + struct vfio_pci_device *vdev = dev_id; + unsigned long flags; + int ret = IRQ_NONE; + + spin_lock_irqsave(&vdev->irqlock, flags); + + if (!vdev->pci_2_3) { + disable_irq_nosync(vdev->pdev->irq); + vdev->ctx[0].masked = true; + ret = IRQ_HANDLED; + } else if (!vdev->ctx[0].masked && /* may be shared */ + pci_check_and_mask_intx(vdev->pdev)) { + vdev->ctx[0].masked = true; + ret = IRQ_HANDLED; + } + + spin_unlock_irqrestore(&vdev->irqlock, flags); + + if (ret == IRQ_HANDLED) + vfio_send_intx_eventfd(vdev, NULL); + + return ret; +} + +static int vfio_intx_enable(struct vfio_pci_device *vdev) +{ + if (!is_irq_none(vdev)) + return -EINVAL; + + if (!vdev->pdev->irq) + return -ENODEV; + + vdev->ctx = kzalloc(sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL); + if (!vdev->ctx) + return -ENOMEM; + + vdev->num_ctx = 1; + vdev->irq_type = VFIO_PCI_INTX_IRQ_INDEX; + + return 0; +} + +static int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd) +{ + struct pci_dev *pdev = vdev->pdev; + unsigned long irqflags = IRQF_SHARED; + struct eventfd_ctx *trigger; + unsigned long flags; + int ret; + + if (vdev->ctx[0].trigger) { + free_irq(pdev->irq, vdev); + kfree(vdev->ctx[0].name); + eventfd_ctx_put(vdev->ctx[0].trigger); + vdev->ctx[0].trigger = NULL; + } + + if (fd < 0) /* Disable only */ + return 0; + + vdev->ctx[0].name = kasprintf(GFP_KERNEL, "vfio-intx(%s)", + pci_name(pdev)); + if (!vdev->ctx[0].name) + return -ENOMEM; + + trigger = eventfd_ctx_fdget(fd); + if (IS_ERR(trigger)) { + kfree(vdev->ctx[0].name); + return PTR_ERR(trigger); + } + + if (!vdev->pci_2_3) + irqflags = 0; + + ret = request_irq(pdev->irq, vfio_intx_handler, + irqflags, vdev->ctx[0].name, vdev); + if (ret) { + kfree(vdev->ctx[0].name); + eventfd_ctx_put(trigger); + return ret; + } + + vdev->ctx[0].trigger = trigger; + + /* + * INTx disable will stick across the new irq setup, + * disable_irq won't. + */ + spin_lock_irqsave(&vdev->irqlock, flags); + if (!vdev->pci_2_3 && (vdev->ctx[0].masked || vdev->virq_disabled)) + disable_irq_nosync(pdev->irq); + spin_unlock_irqrestore(&vdev->irqlock, flags); + + return 0; +} + +static void vfio_intx_disable(struct vfio_pci_device *vdev) +{ + vfio_intx_set_signal(vdev, -1); + virqfd_disable(vdev->ctx[0].unmask); + virqfd_disable(vdev->ctx[0].mask); + vdev->irq_type = VFIO_PCI_NUM_IRQS; + vdev->num_ctx = 0; + kfree(vdev->ctx); +} + +/* + * MSI/MSI-X + */ +static irqreturn_t vfio_msihandler(int irq, void *arg) +{ + struct eventfd_ctx *trigger = arg; + + eventfd_signal(trigger, 1); + return IRQ_HANDLED; +} + +static int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix) +{ + struct pci_dev *pdev = vdev->pdev; + int ret; + + if (!is_irq_none(vdev)) + return -EINVAL; + + vdev->ctx = kzalloc(nvec * sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL); + if (!vdev->ctx) + return -ENOMEM; + + if (msix) { + int i; + + vdev->msix = kzalloc(nvec * sizeof(struct msix_entry), + GFP_KERNEL); + if (!vdev->msix) { + kfree(vdev->ctx); + return -ENOMEM; + } + + for (i = 0; i < nvec; i++) + vdev->msix[i].entry = i; + + ret = pci_enable_msix(pdev, vdev->msix, nvec); + if (ret) { + kfree(vdev->msix); + kfree(vdev->ctx); + return ret; + } + } else { + ret = pci_enable_msi_block(pdev, nvec); + if (ret) { + kfree(vdev->ctx); + return ret; + } + } + + vdev->num_ctx = nvec; + vdev->irq_type = msix ? VFIO_PCI_MSIX_IRQ_INDEX : + VFIO_PCI_MSI_IRQ_INDEX; + + if (!msix) { + /* + * Compute the virtual hardware field for max msi vectors - + * it is the log base 2 of the number of vectors. + */ + vdev->msi_qmax = fls(nvec * 2 - 1) - 1; + } + + return 0; +} + +static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev, + int vector, int fd, bool msix) +{ + struct pci_dev *pdev = vdev->pdev; + int irq = msix ? vdev->msix[vector].vector : pdev->irq + vector; + char *name = msix ? "vfio-msix" : "vfio-msi"; + struct eventfd_ctx *trigger; + int ret; + + if (vector >= vdev->num_ctx) + return -EINVAL; + + if (vdev->ctx[vector].trigger) { + free_irq(irq, vdev->ctx[vector].trigger); + kfree(vdev->ctx[vector].name); + eventfd_ctx_put(vdev->ctx[vector].trigger); + vdev->ctx[vector].trigger = NULL; + } + + if (fd < 0) + return 0; + + vdev->ctx[vector].name = kasprintf(GFP_KERNEL, "%s[%d](%s)", + name, vector, pci_name(pdev)); + if (!vdev->ctx[vector].name) + return -ENOMEM; + + trigger = eventfd_ctx_fdget(fd); + if (IS_ERR(trigger)) { + kfree(vdev->ctx[vector].name); + return PTR_ERR(trigger); + } + + ret = request_irq(irq, vfio_msihandler, 0, + vdev->ctx[vector].name, trigger); + if (ret) { + kfree(vdev->ctx[vector].name); + eventfd_ctx_put(trigger); + return ret; + } + + vdev->ctx[vector].trigger = trigger; + + return 0; +} + +static int vfio_msi_set_block(struct vfio_pci_device *vdev, unsigned start, + unsigned count, int32_t *fds, bool msix) +{ + int i, j, ret = 0; + + if (start + count > vdev->num_ctx) + return -EINVAL; + + for (i = 0, j = start; i < count && !ret; i++, j++) { + int fd = fds ? fds[i] : -1; + ret = vfio_msi_set_vector_signal(vdev, j, fd, msix); + } + + if (ret) { + for (--j; j >= start; j--) + vfio_msi_set_vector_signal(vdev, j, -1, msix); + } + + return ret; +} + +static void vfio_msi_disable(struct vfio_pci_device *vdev, bool msix) +{ + struct pci_dev *pdev = vdev->pdev; + int i; + + vfio_msi_set_block(vdev, 0, vdev->num_ctx, NULL, msix); + + for (i = 0; i < vdev->num_ctx; i++) { + virqfd_disable(vdev->ctx[i].unmask); + virqfd_disable(vdev->ctx[i].mask); + } + + if (msix) { + pci_disable_msix(vdev->pdev); + kfree(vdev->msix); + } else + pci_disable_msi(pdev); + + vdev->irq_type = VFIO_PCI_NUM_IRQS; + vdev->num_ctx = 0; + kfree(vdev->ctx); +} + +/* + * IOCTL support + */ +static int vfio_pci_set_intx_unmask(struct vfio_pci_device *vdev, + unsigned index, unsigned start, + unsigned count, uint32_t flags, void *data) +{ + if (!is_intx(vdev) || start != 0 || count != 1) + return -EINVAL; + + if (flags & VFIO_IRQ_SET_DATA_NONE) { + vfio_pci_intx_unmask(vdev); + } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { + uint8_t unmask = *(uint8_t *)data; + if (unmask) + vfio_pci_intx_unmask(vdev); + } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + int32_t fd = *(int32_t *)data; + if (fd >= 0) + return virqfd_enable(vdev, vfio_pci_intx_unmask_handler, + vfio_send_intx_eventfd, NULL, + &vdev->ctx[0].unmask, fd); + + virqfd_disable(vdev->ctx[0].unmask); + } + + return 0; +} + +static int vfio_pci_set_intx_mask(struct vfio_pci_device *vdev, + unsigned index, unsigned start, + unsigned count, uint32_t flags, void *data) +{ + if (!is_intx(vdev) || start != 0 || count != 1) + return -EINVAL; + + if (flags & VFIO_IRQ_SET_DATA_NONE) { + vfio_pci_intx_mask(vdev); + } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { + uint8_t mask = *(uint8_t *)data; + if (mask) + vfio_pci_intx_mask(vdev); + } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + return -ENOTTY; /* XXX implement me */ + } + + return 0; +} + +static int vfio_pci_set_intx_trigger(struct vfio_pci_device *vdev, + unsigned index, unsigned start, + unsigned count, uint32_t flags, void *data) +{ + if (is_intx(vdev) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) { + vfio_intx_disable(vdev); + return 0; + } + + if (!(is_intx(vdev) || is_irq_none(vdev)) || start != 0 || count != 1) + return -EINVAL; + + if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + int32_t fd = *(int32_t *)data; + int ret; + + if (is_intx(vdev)) + return vfio_intx_set_signal(vdev, fd); + + ret = vfio_intx_enable(vdev); + if (ret) + return ret; + + ret = vfio_intx_set_signal(vdev, fd); + if (ret) + vfio_intx_disable(vdev); + + return ret; + } + + if (!is_intx(vdev)) + return -EINVAL; + + if (flags & VFIO_IRQ_SET_DATA_NONE) { + vfio_send_intx_eventfd(vdev, NULL); + } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { + uint8_t trigger = *(uint8_t *)data; + if (trigger) + vfio_send_intx_eventfd(vdev, NULL); + } + return 0; +} + +static int vfio_pci_set_msi_trigger(struct vfio_pci_device *vdev, + unsigned index, unsigned start, + unsigned count, uint32_t flags, void *data) +{ + int i; + bool msix = (index == VFIO_PCI_MSIX_IRQ_INDEX) ? true : false; + + if (irq_is(vdev, index) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) { + vfio_msi_disable(vdev, msix); + return 0; + } + + if (!(irq_is(vdev, index) || is_irq_none(vdev))) + return -EINVAL; + + if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + int32_t *fds = data; + int ret; + + if (vdev->irq_type == index) + return vfio_msi_set_block(vdev, start, count, + fds, msix); + + ret = vfio_msi_enable(vdev, start + count, msix); + if (ret) + return ret; + + ret = vfio_msi_set_block(vdev, start, count, fds, msix); + if (ret) + vfio_msi_disable(vdev, msix); + + return ret; + } + + if (!irq_is(vdev, index) || start + count > vdev->num_ctx) + return -EINVAL; + + for (i = start; i < start + count; i++) { + if (!vdev->ctx[i].trigger) + continue; + if (flags & VFIO_IRQ_SET_DATA_NONE) { + eventfd_signal(vdev->ctx[i].trigger, 1); + } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { + uint8_t *bools = data; + if (bools[i - start]) + eventfd_signal(vdev->ctx[i].trigger, 1); + } + } + return 0; +} + +int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags, + unsigned index, unsigned start, unsigned count, + void *data) +{ + int (*func)(struct vfio_pci_device *vdev, unsigned index, + unsigned start, unsigned count, uint32_t flags, + void *data) = NULL; + + switch (index) { + case VFIO_PCI_INTX_IRQ_INDEX: + switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { + case VFIO_IRQ_SET_ACTION_MASK: + func = vfio_pci_set_intx_mask; + break; + case VFIO_IRQ_SET_ACTION_UNMASK: + func = vfio_pci_set_intx_unmask; + break; + case VFIO_IRQ_SET_ACTION_TRIGGER: + func = vfio_pci_set_intx_trigger; + break; + } + break; + case VFIO_PCI_MSI_IRQ_INDEX: + case VFIO_PCI_MSIX_IRQ_INDEX: + switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { + case VFIO_IRQ_SET_ACTION_MASK: + case VFIO_IRQ_SET_ACTION_UNMASK: + /* XXX Need masking support exported */ + break; + case VFIO_IRQ_SET_ACTION_TRIGGER: + func = vfio_pci_set_msi_trigger; + break; + } + break; + } + + if (!func) + return -ENOTTY; + + return func(vdev, index, start, count, flags, data); +} diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h new file mode 100644 index 000000000000..611827cba8cd --- /dev/null +++ b/drivers/vfio/pci/vfio_pci_private.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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. + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + */ + +#include +#include + +#ifndef VFIO_PCI_PRIVATE_H +#define VFIO_PCI_PRIVATE_H + +#define VFIO_PCI_OFFSET_SHIFT 40 + +#define VFIO_PCI_OFFSET_TO_INDEX(off) (off >> VFIO_PCI_OFFSET_SHIFT) +#define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT) +#define VFIO_PCI_OFFSET_MASK (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1) + +struct vfio_pci_irq_ctx { + struct eventfd_ctx *trigger; + struct virqfd *unmask; + struct virqfd *mask; + char *name; + bool masked; +}; + +struct vfio_pci_device { + struct pci_dev *pdev; + void __iomem *barmap[PCI_STD_RESOURCE_END + 1]; + u8 *pci_config_map; + u8 *vconfig; + struct perm_bits *msi_perm; + spinlock_t irqlock; + struct mutex igate; + struct msix_entry *msix; + struct vfio_pci_irq_ctx *ctx; + int num_ctx; + int irq_type; + u8 msi_qmax; + u8 msix_bar; + u16 msix_size; + u32 msix_offset; + u32 rbar[7]; + bool pci_2_3; + bool virq_disabled; + bool reset_works; + bool extended_caps; + bool bardirty; + struct pci_saved_state *pci_saved_state; + atomic_t refcnt; +}; + +#define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX) +#define is_msi(vdev) (vdev->irq_type == VFIO_PCI_MSI_IRQ_INDEX) +#define is_msix(vdev) (vdev->irq_type == VFIO_PCI_MSIX_IRQ_INDEX) +#define is_irq_none(vdev) (!(is_intx(vdev) || is_msi(vdev) || is_msix(vdev))) +#define irq_is(vdev, type) (vdev->irq_type == type) + +extern void vfio_pci_intx_mask(struct vfio_pci_device *vdev); +extern void vfio_pci_intx_unmask(struct vfio_pci_device *vdev); + +extern int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, + uint32_t flags, unsigned index, + unsigned start, unsigned count, void *data); + +extern ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev, + char __user *buf, size_t count, + loff_t *ppos, bool iswrite); +extern ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, + char __user *buf, size_t count, + loff_t *ppos, bool iswrite); +extern ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, + char __user *buf, size_t count, + loff_t *ppos, bool iswrite); + +extern int vfio_pci_init_perm_bits(void); +extern void vfio_pci_uninit_perm_bits(void); + +extern int vfio_pci_virqfd_init(void); +extern void vfio_pci_virqfd_exit(void); + +extern int vfio_config_init(struct vfio_pci_device *vdev); +extern void vfio_config_free(struct vfio_pci_device *vdev); +#endif /* VFIO_PCI_PRIVATE_H */ diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c new file mode 100644 index 000000000000..4362d9e7baa3 --- /dev/null +++ b/drivers/vfio/pci/vfio_pci_rdwr.c @@ -0,0 +1,269 @@ +/* + * VFIO PCI I/O Port & MMIO access + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * 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. + * + * Derived from original vfio: + * Copyright 2010 Cisco Systems, Inc. All rights reserved. + * Author: Tom Lyon, pugs@cisco.com + */ + +#include +#include +#include +#include + +#include "vfio_pci_private.h" + +/* I/O Port BAR access */ +ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite) +{ + struct pci_dev *pdev = vdev->pdev; + loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; + int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); + void __iomem *io; + size_t done = 0; + + if (!pci_resource_start(pdev, bar)) + return -EINVAL; + + if (pos + count > pci_resource_len(pdev, bar)) + return -EINVAL; + + if (!vdev->barmap[bar]) { + int ret; + + ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); + if (ret) + return ret; + + vdev->barmap[bar] = pci_iomap(pdev, bar, 0); + + if (!vdev->barmap[bar]) { + pci_release_selected_regions(pdev, 1 << bar); + return -EINVAL; + } + } + + io = vdev->barmap[bar]; + + while (count) { + int filled; + + if (count >= 3 && !(pos % 4)) { + __le32 val; + + if (iswrite) { + if (copy_from_user(&val, buf, 4)) + return -EFAULT; + + iowrite32(le32_to_cpu(val), io + pos); + } else { + val = cpu_to_le32(ioread32(io + pos)); + + if (copy_to_user(buf, &val, 4)) + return -EFAULT; + } + + filled = 4; + + } else if ((pos % 2) == 0 && count >= 2) { + __le16 val; + + if (iswrite) { + if (copy_from_user(&val, buf, 2)) + return -EFAULT; + + iowrite16(le16_to_cpu(val), io + pos); + } else { + val = cpu_to_le16(ioread16(io + pos)); + + if (copy_to_user(buf, &val, 2)) + return -EFAULT; + } + + filled = 2; + } else { + u8 val; + + if (iswrite) { + if (copy_from_user(&val, buf, 1)) + return -EFAULT; + + iowrite8(val, io + pos); + } else { + val = ioread8(io + pos); + + if (copy_to_user(buf, &val, 1)) + return -EFAULT; + } + + filled = 1; + } + + count -= filled; + done += filled; + buf += filled; + pos += filled; + } + + *ppos += done; + + return done; +} + +/* + * MMIO BAR access + * We handle two excluded ranges here as well, if the user tries to read + * the ROM beyond what PCI tells us is available or the MSI-X table region, + * we return 0xFF and writes are dropped. + */ +ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite) +{ + struct pci_dev *pdev = vdev->pdev; + loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; + int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); + void __iomem *io; + resource_size_t end; + size_t done = 0; + size_t x_start = 0, x_end = 0; /* excluded range */ + + if (!pci_resource_start(pdev, bar)) + return -EINVAL; + + end = pci_resource_len(pdev, bar); + + if (pos > end) + return -EINVAL; + + if (pos == end) + return 0; + + if (pos + count > end) + count = end - pos; + + if (bar == PCI_ROM_RESOURCE) { + io = pci_map_rom(pdev, &x_start); + x_end = end; + } else { + if (!vdev->barmap[bar]) { + int ret; + + ret = pci_request_selected_regions(pdev, 1 << bar, + "vfio"); + if (ret) + return ret; + + vdev->barmap[bar] = pci_iomap(pdev, bar, 0); + + if (!vdev->barmap[bar]) { + pci_release_selected_regions(pdev, 1 << bar); + return -EINVAL; + } + } + + io = vdev->barmap[bar]; + + if (bar == vdev->msix_bar) { + x_start = vdev->msix_offset; + x_end = vdev->msix_offset + vdev->msix_size; + } + } + + if (!io) + return -EINVAL; + + while (count) { + size_t fillable, filled; + + if (pos < x_start) + fillable = x_start - pos; + else if (pos >= x_end) + fillable = end - pos; + else + fillable = 0; + + if (fillable >= 4 && !(pos % 4) && (count >= 4)) { + __le32 val; + + if (iswrite) { + if (copy_from_user(&val, buf, 4)) + goto out; + + iowrite32(le32_to_cpu(val), io + pos); + } else { + val = cpu_to_le32(ioread32(io + pos)); + + if (copy_to_user(buf, &val, 4)) + goto out; + } + + filled = 4; + } else if (fillable >= 2 && !(pos % 2) && (count >= 2)) { + __le16 val; + + if (iswrite) { + if (copy_from_user(&val, buf, 2)) + goto out; + + iowrite16(le16_to_cpu(val), io + pos); + } else { + val = cpu_to_le16(ioread16(io + pos)); + + if (copy_to_user(buf, &val, 2)) + goto out; + } + + filled = 2; + } else if (fillable) { + u8 val; + + if (iswrite) { + if (copy_from_user(&val, buf, 1)) + goto out; + + iowrite8(val, io + pos); + } else { + val = ioread8(io + pos); + + if (copy_to_user(buf, &val, 1)) + goto out; + } + + filled = 1; + } else { + /* Drop writes, fill reads with FF */ + if (!iswrite) { + char val = 0xFF; + size_t i; + + for (i = 0; i < x_end - pos; i++) { + if (put_user(val, buf + i)) + goto out; + } + } + + filled = x_end - pos; + } + + count -= filled; + done += filled; + buf += filled; + pos += filled; + } + + *ppos += done; + +out: + if (bar == PCI_ROM_RESOURCE) + pci_unmap_rom(pdev, io); + + return count ? -EFAULT : done; +} diff --git a/include/linux/vfio.h b/include/linux/vfio.h index acb046fd5b70..0a4f180a11d8 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -223,6 +223,7 @@ struct vfio_device_info { __u32 argsz; __u32 flags; #define VFIO_DEVICE_FLAGS_RESET (1 << 0) /* Device supports reset */ +#define VFIO_DEVICE_FLAGS_PCI (1 << 1) /* vfio-pci device */ __u32 num_regions; /* Max region index + 1 */ __u32 num_irqs; /* Max IRQ index + 1 */ }; @@ -364,6 +365,31 @@ struct vfio_irq_set { */ #define VFIO_DEVICE_RESET _IO(VFIO_TYPE, VFIO_BASE + 11) +/* + * The VFIO-PCI bus driver makes use of the following fixed region and + * IRQ index mapping. Unimplemented regions return a size of zero. + * Unimplemented IRQ types return a count of zero. + */ + +enum { + VFIO_PCI_BAR0_REGION_INDEX, + VFIO_PCI_BAR1_REGION_INDEX, + VFIO_PCI_BAR2_REGION_INDEX, + VFIO_PCI_BAR3_REGION_INDEX, + VFIO_PCI_BAR4_REGION_INDEX, + VFIO_PCI_BAR5_REGION_INDEX, + VFIO_PCI_ROM_REGION_INDEX, + VFIO_PCI_CONFIG_REGION_INDEX, + VFIO_PCI_NUM_REGIONS +}; + +enum { + VFIO_PCI_INTX_IRQ_INDEX, + VFIO_PCI_MSI_IRQ_INDEX, + VFIO_PCI_MSIX_IRQ_INDEX, + VFIO_PCI_NUM_IRQS +}; + /* -------- API for Type1 VFIO IOMMU -------- */ /** -- cgit v1.2.3 From e6dab5ffab59e910ec0e3355f4a6f29f7a7be474 Mon Sep 17 00:00:00 2001 From: Andrew Vagin Date: Wed, 11 Jul 2012 18:14:58 +0400 Subject: perf/trace: Add ability to set a target task for events A few events are interesting not only for a current task. For example, sched_stat_* events are interesting for a task which wakes up. For this reason, it will be good if such events will be delivered to a target task too. Now a target task can be set by using __perf_task(). The original idea and a draft patch belongs to Peter Zijlstra. I need these events for profiling sleep times. sched_switch is used for getting callchains and sched_stat_* is used for getting time periods. These events are combined in user space, then it can be analyzed by perf tools. Inspired-by: Peter Zijlstra Cc: Steven Rostedt Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Arun Sharma Signed-off-by: Andrew Vagin Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1342016098-213063-1-git-send-email-avagin@openvz.org Signed-off-by: Ingo Molnar --- include/linux/ftrace_event.h | 5 +++-- include/linux/perf_event.h | 3 ++- include/trace/events/sched.h | 4 ++++ include/trace/ftrace.h | 6 +++++- kernel/events/callchain.c | 9 ++++++++- kernel/events/core.c | 30 ++++++++++++++++++++++++++++-- kernel/events/internal.h | 3 ++- kernel/trace/trace_event_perf.c | 2 +- kernel/trace/trace_kprobe.c | 6 ++++-- kernel/trace/trace_syscalls.c | 4 ++-- kernel/trace/trace_uprobe.c | 2 +- 11 files changed, 60 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index af961d6f7ab1..642928cf57b4 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -306,9 +306,10 @@ extern void *perf_trace_buf_prepare(int size, unsigned short type, static inline void perf_trace_buf_submit(void *raw_data, int size, int rctx, u64 addr, - u64 count, struct pt_regs *regs, void *head) + u64 count, struct pt_regs *regs, void *head, + struct task_struct *task) { - perf_tp_event(addr, count, raw_data, size, regs, head, rctx); + perf_tp_event(addr, count, raw_data, size, regs, head, rctx, task); } #endif diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 76c5c8b724a7..7602ccb3f40e 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1272,7 +1272,8 @@ static inline bool perf_paranoid_kernel(void) extern void perf_event_init(void); extern void perf_tp_event(u64 addr, u64 count, void *record, int entry_size, struct pt_regs *regs, - struct hlist_head *head, int rctx); + struct hlist_head *head, int rctx, + struct task_struct *task); extern void perf_bp_event(struct perf_event *event, void *data); #ifndef perf_misc_flags diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index ea7a2035456d..5a8671e8a67f 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -73,6 +73,9 @@ DECLARE_EVENT_CLASS(sched_wakeup_template, __entry->prio = p->prio; __entry->success = success; __entry->target_cpu = task_cpu(p); + ) + TP_perf_assign( + __perf_task(p); ), TP_printk("comm=%s pid=%d prio=%d success=%d target_cpu=%03d", @@ -325,6 +328,7 @@ DECLARE_EVENT_CLASS(sched_stat_template, ) TP_perf_assign( __perf_count(delay); + __perf_task(tsk); ), TP_printk("comm=%s pid=%d delay=%Lu [ns]", diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index c6bc2faaf261..a763888a36f9 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -712,6 +712,9 @@ __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call #undef __perf_count #define __perf_count(c) __count = (c) +#undef __perf_task +#define __perf_task(t) __task = (t) + #undef TP_perf_assign #define TP_perf_assign(args...) args @@ -725,6 +728,7 @@ perf_trace_##call(void *__data, proto) \ struct ftrace_raw_##call *entry; \ struct pt_regs __regs; \ u64 __addr = 0, __count = 1; \ + struct task_struct *__task = NULL; \ struct hlist_head *head; \ int __entry_size; \ int __data_size; \ @@ -752,7 +756,7 @@ perf_trace_##call(void *__data, proto) \ \ head = this_cpu_ptr(event_call->perf_events); \ perf_trace_buf_submit(entry, __entry_size, rctx, __addr, \ - __count, &__regs, head); \ + __count, &__regs, head, __task); \ } /* diff --git a/kernel/events/callchain.c b/kernel/events/callchain.c index 6581a040f399..98d4597f43d6 100644 --- a/kernel/events/callchain.c +++ b/kernel/events/callchain.c @@ -153,7 +153,8 @@ put_callchain_entry(int rctx) put_recursion_context(__get_cpu_var(callchain_recursion), rctx); } -struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) +struct perf_callchain_entry * +perf_callchain(struct perf_event *event, struct pt_regs *regs) { int rctx; struct perf_callchain_entry *entry; @@ -178,6 +179,12 @@ struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) } if (regs) { + /* + * Disallow cross-task user callchains. + */ + if (event->ctx->task && event->ctx->task != current) + goto exit_put; + perf_callchain_store(entry, PERF_CONTEXT_USER); perf_callchain_user(entry, regs); } diff --git a/kernel/events/core.c b/kernel/events/core.c index f1cf0edeb39a..b7935fcec7d9 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4039,7 +4039,7 @@ void perf_prepare_sample(struct perf_event_header *header, if (sample_type & PERF_SAMPLE_CALLCHAIN) { int size = 1; - data->callchain = perf_callchain(regs); + data->callchain = perf_callchain(event, regs); if (data->callchain) size += data->callchain->nr; @@ -5209,7 +5209,8 @@ static int perf_tp_event_match(struct perf_event *event, } void perf_tp_event(u64 addr, u64 count, void *record, int entry_size, - struct pt_regs *regs, struct hlist_head *head, int rctx) + struct pt_regs *regs, struct hlist_head *head, int rctx, + struct task_struct *task) { struct perf_sample_data data; struct perf_event *event; @@ -5228,6 +5229,31 @@ void perf_tp_event(u64 addr, u64 count, void *record, int entry_size, perf_swevent_event(event, count, &data, regs); } + /* + * If we got specified a target task, also iterate its context and + * deliver this event there too. + */ + if (task && task != current) { + struct perf_event_context *ctx; + struct trace_entry *entry = record; + + rcu_read_lock(); + ctx = rcu_dereference(task->perf_event_ctxp[perf_sw_context]); + if (!ctx) + goto unlock; + + list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { + if (event->attr.type != PERF_TYPE_TRACEPOINT) + continue; + if (event->attr.config != entry->type) + continue; + if (perf_tp_event_match(event, &data, regs)) + perf_swevent_event(event, count, &data, regs); + } +unlock: + rcu_read_unlock(); + } + perf_swevent_put_recursion_context(rctx); } EXPORT_SYMBOL_GPL(perf_tp_event); diff --git a/kernel/events/internal.h b/kernel/events/internal.h index b0b107f90afc..a096c19f2c2a 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h @@ -101,7 +101,8 @@ __output_copy(struct perf_output_handle *handle, } /* Callchain handling */ -extern struct perf_callchain_entry *perf_callchain(struct pt_regs *regs); +extern struct perf_callchain_entry * +perf_callchain(struct perf_event *event, struct pt_regs *regs); extern int get_callchain_buffers(void); extern void put_callchain_buffers(void); diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index fee3752ae8f6..8a6d2ee2086c 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c @@ -281,7 +281,7 @@ perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip) head = this_cpu_ptr(event_function.perf_events); perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, 0, - 1, ®s, head); + 1, ®s, head, NULL); #undef ENTRY_SIZE } diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index b31d3d5699fe..1a2117043bb1 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -1002,7 +1002,8 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp, store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); head = this_cpu_ptr(call->perf_events); - perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head); + perf_trace_buf_submit(entry, size, rctx, + entry->ip, 1, regs, head, NULL); } /* Kretprobe profile handler */ @@ -1033,7 +1034,8 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri, store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); head = this_cpu_ptr(call->perf_events); - perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1, regs, head); + perf_trace_buf_submit(entry, size, rctx, + entry->ret_ip, 1, regs, head, NULL); } #endif /* CONFIG_PERF_EVENTS */ diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 96fc73369099..60e4d7875672 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -532,7 +532,7 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) (unsigned long *)&rec->args); head = this_cpu_ptr(sys_data->enter_event->perf_events); - perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head); + perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL); } int perf_sysenter_enable(struct ftrace_event_call *call) @@ -608,7 +608,7 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) rec->ret = syscall_get_return_value(current, regs); head = this_cpu_ptr(sys_data->exit_event->perf_events); - perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head); + perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL); } int perf_sysexit_enable(struct ftrace_event_call *call) diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index 2b36ac68549e..03003cd7dd96 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -670,7 +670,7 @@ static void uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs) call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset); head = this_cpu_ptr(call->perf_events); - perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head); + perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head, NULL); out: preempt_enable(); -- cgit v1.2.3 From a7ea3bbf5d58f4df2265d312f91d5769eabc8144 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Fri, 27 Jul 2012 14:48:09 -0400 Subject: time/jiffies: Allow CLOCK_TICK_RATE to be undefined CLOCK_TICK_RATE is a legacy constant that defines the timer device's granularity. On hardware with particularly coarse granularity, this constant is used to reduce accumulated time error when using jiffies as a clocksource, by calculating the hardware's actual tick length rather then just assuming it is 1sec/HZ. However, for the most part this is unnecessary, as most modern systems don't use jiffies for their clocksource, and their tick device is sufficiently fine grained to avoid major error. Thus, this patch allows an architecture to not define CLOCK_TICK_RATE, in which case ACTHZ defaults to (HZ << 8). Signed-off-by: Catalin Marinas Acked-by: Arnd Bergmann Cc: Richard Cochran Cc: Prarit Bhargava Cc: Andrew Morton [ Commit log & intention tweaks ] Signed-off-by: John Stultz Link: http://lkml.kernel.org/r/1343414893-45779-2-git-send-email-john.stultz@linaro.org Signed-off-by: Ingo Molnar --- include/linux/jiffies.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h index 265e2c3cbd1c..7d24466fb80b 100644 --- a/include/linux/jiffies.h +++ b/include/linux/jiffies.h @@ -39,9 +39,6 @@ # error Invalid value of HZ. #endif -/* LATCH is used in the interval timer and ftape setup. */ -#define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */ - /* Suppose we want to divide two numbers NOM and DEN: NOM/DEN, then we can * improve accuracy by shifting LSH bits, hence calculating: * (NOM << LSH) / DEN @@ -54,8 +51,15 @@ #define SH_DIV(NOM,DEN,LSH) ( (((NOM) / (DEN)) << (LSH)) \ + ((((NOM) % (DEN)) << (LSH)) + (DEN) / 2) / (DEN)) +#ifdef CLOCK_TICK_RATE +/* LATCH is used in the interval timer and ftape setup. */ +# define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */ + /* HZ is the requested value. ACTHZ is actual HZ ("<< 8" is for accuracy) */ -#define ACTHZ (SH_DIV (CLOCK_TICK_RATE, LATCH, 8)) +# define ACTHZ (SH_DIV(CLOCK_TICK_RATE, LATCH, 8)) +#else +# define ACTHZ (HZ << 8) +#endif /* TICK_NSEC is the time between ticks in nsec assuming real ACTHZ */ #define TICK_NSEC (SH_DIV (1000000UL * 1000, ACTHZ, 8)) -- cgit v1.2.3 From 02ab20ae38337b99b5c29c81090f594b8fd61283 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 27 Jul 2012 14:48:10 -0400 Subject: time/jiffies: Rename ACTHZ to SHIFTED_HZ Ingo noted that ACTHZ is a confusing name, and requested it be renamed, so this patch renames ACTHZ to SHIFTED_HZ to better describe it. Signed-off-by: John Stultz Cc: Prarit Bhargava Link: http://lkml.kernel.org/r/1343414893-45779-3-git-send-email-john.stultz@linaro.org Signed-off-by: Ingo Molnar --- include/linux/jiffies.h | 21 +++++++++++++-------- include/linux/timex.h | 2 +- kernel/time/jiffies.c | 2 +- kernel/time/ntp.c | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h index 7d24466fb80b..82680541576d 100644 --- a/include/linux/jiffies.h +++ b/include/linux/jiffies.h @@ -55,21 +55,26 @@ /* LATCH is used in the interval timer and ftape setup. */ # define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */ -/* HZ is the requested value. ACTHZ is actual HZ ("<< 8" is for accuracy) */ -# define ACTHZ (SH_DIV(CLOCK_TICK_RATE, LATCH, 8)) +/* + * HZ is the requested value. However the CLOCK_TICK_RATE may not allow + * for exactly HZ. So SHIFTED_HZ is high res HZ ("<< 8" is for accuracy) + */ +# define SHIFTED_HZ (SH_DIV(CLOCK_TICK_RATE, LATCH, 8)) #else -# define ACTHZ (HZ << 8) +# define SHIFTED_HZ (HZ << 8) #endif -/* TICK_NSEC is the time between ticks in nsec assuming real ACTHZ */ -#define TICK_NSEC (SH_DIV (1000000UL * 1000, ACTHZ, 8)) +/* TICK_NSEC is the time between ticks in nsec assuming SHIFTED_HZ */ +#define TICK_NSEC (SH_DIV(1000000UL * 1000, SHIFTED_HZ, 8)) /* TICK_USEC is the time between ticks in usec assuming fake USER_HZ */ #define TICK_USEC ((1000000UL + USER_HZ/2) / USER_HZ) -/* TICK_USEC_TO_NSEC is the time between ticks in nsec assuming real ACTHZ and */ -/* a value TUSEC for TICK_USEC (can be set bij adjtimex) */ -#define TICK_USEC_TO_NSEC(TUSEC) (SH_DIV (TUSEC * USER_HZ * 1000, ACTHZ, 8)) +/* + * TICK_USEC_TO_NSEC is the time between ticks in nsec assuming SHIFTED_HZ and + * a value TUSEC for TICK_USEC (can be set bij adjtimex) + */ +#define TICK_USEC_TO_NSEC(TUSEC) (SH_DIV(TUSEC * USER_HZ * 1000, SHIFTED_HZ, 8)) /* some arch's have a small-data section that can be accessed register-relative * but that can only take up to, say, 4-byte variables. jiffies being part of diff --git a/include/linux/timex.h b/include/linux/timex.h index 99bc88b1fc02..7c5ceb20e03a 100644 --- a/include/linux/timex.h +++ b/include/linux/timex.h @@ -232,7 +232,7 @@ struct timex { * estimated error = NTP dispersion. */ extern unsigned long tick_usec; /* USER_HZ period (usec) */ -extern unsigned long tick_nsec; /* ACTHZ period (nsec) */ +extern unsigned long tick_nsec; /* SHIFTED_HZ period (nsec) */ extern void ntp_init(void); extern void ntp_clear(void); diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c index a470154e0408..46da0537c10b 100644 --- a/kernel/time/jiffies.c +++ b/kernel/time/jiffies.c @@ -37,7 +37,7 @@ * requested HZ value. It is also not recommended * for "tick-less" systems. */ -#define NSEC_PER_JIFFY ((u32)((((u64)NSEC_PER_SEC)<<8)/ACTHZ)) +#define NSEC_PER_JIFFY ((u32)((((u64)NSEC_PER_SEC)<<8)/SHIFTED_HZ)) /* Since jiffies uses a simple NSEC_PER_JIFFY multiplier * conversion, the .shift value could be zero. However diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index b7fbadc5c973..24174b4d669b 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -28,7 +28,7 @@ DEFINE_SPINLOCK(ntp_lock); /* USER_HZ period (usecs): */ unsigned long tick_usec = TICK_USEC; -/* ACTHZ period (nsecs): */ +/* SHIFTED_HZ period (nsecs): */ unsigned long tick_nsec; static u64 tick_length; -- cgit v1.2.3 From 54764bb647b2e847c512acf8d443df965da35000 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 31 Jul 2012 01:08:23 +0000 Subject: ipv4: Restore old dst_free() behavior. commit 404e0a8b6a55 (net: ipv4: fix RCU races on dst refcounts) tried to solve a race but added a problem at device/fib dismantle time : We really want to call dst_free() as soon as possible, even if sockets still have dst in their cache. dst_release() calls in free_fib_info_rcu() are not welcomed. Root of the problem was that now we also cache output routes (in nh_rth_output), we must use call_rcu() instead of call_rcu_bh() in rt_free(), because output route lookups are done in process context. Based on feedback and initial patch from David Miller (adding another call_rcu_bh() call in fib, but it appears it was not the right fix) I left the inet_sk_rx_dst_set() helper and added __rcu attributes to nh_rth_output and nh_rth_input to better document what is going on in this code. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/dst.h | 7 ++++++- include/net/inet_sock.h | 10 +++------- include/net/ip_fib.h | 4 ++-- net/core/dst.c | 26 +++++--------------------- net/decnet/dn_route.c | 6 ------ net/ipv4/fib_semantics.c | 21 +++++++++++++++++---- net/ipv4/route.c | 26 +++++++++++++++++--------- 7 files changed, 50 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 31a9fd39edb6..baf597890064 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -61,7 +61,6 @@ struct dst_entry { #define DST_NOPEER 0x0040 #define DST_FAKE_RTABLE 0x0080 #define DST_XFRM_TUNNEL 0x0100 -#define DST_RCU_FREE 0x0200 unsigned short pending_confirm; @@ -383,6 +382,12 @@ static inline void dst_free(struct dst_entry *dst) __dst_free(dst); } +static inline void dst_rcu_free(struct rcu_head *head) +{ + struct dst_entry *dst = container_of(head, struct dst_entry, rcu_head); + dst_free(dst); +} + static inline void dst_confirm(struct dst_entry *dst) { dst->pending_confirm = 1; diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index e3fd34c83ac9..83b567fe1941 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -253,13 +253,9 @@ static inline void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb { struct dst_entry *dst = skb_dst(skb); - if (atomic_inc_not_zero(&dst->__refcnt)) { - if (!(dst->flags & DST_RCU_FREE)) - dst->flags |= DST_RCU_FREE; - - sk->sk_rx_dst = dst; - inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; - } + dst_hold(dst); + sk->sk_rx_dst = dst; + inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; } #endif /* _INET_SOCK_H */ diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index e69c3a47153d..e521a03515b1 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -81,8 +81,8 @@ struct fib_nh { __be32 nh_gw; __be32 nh_saddr; int nh_saddr_genid; - struct rtable *nh_rth_output; - struct rtable *nh_rth_input; + struct rtable __rcu *nh_rth_output; + struct rtable __rcu *nh_rth_input; struct fnhe_hash_bucket *nh_exceptions; }; diff --git a/net/core/dst.c b/net/core/dst.c index d9e33ebe170f..069d51d29414 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -258,15 +258,6 @@ again: } EXPORT_SYMBOL(dst_destroy); -static void dst_rcu_destroy(struct rcu_head *head) -{ - struct dst_entry *dst = container_of(head, struct dst_entry, rcu_head); - - dst = dst_destroy(dst); - if (dst) - __dst_free(dst); -} - void dst_release(struct dst_entry *dst) { if (dst) { @@ -274,14 +265,10 @@ void dst_release(struct dst_entry *dst) newrefcnt = atomic_dec_return(&dst->__refcnt); WARN_ON(newrefcnt < 0); - if (unlikely(dst->flags & (DST_NOCACHE | DST_RCU_FREE)) && !newrefcnt) { - if (dst->flags & DST_RCU_FREE) { - call_rcu_bh(&dst->rcu_head, dst_rcu_destroy); - } else { - dst = dst_destroy(dst); - if (dst) - __dst_free(dst); - } + if (unlikely(dst->flags & DST_NOCACHE) && !newrefcnt) { + dst = dst_destroy(dst); + if (dst) + __dst_free(dst); } } } @@ -333,14 +320,11 @@ EXPORT_SYMBOL(__dst_destroy_metrics_generic); */ void skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst) { - bool hold; - WARN_ON(!rcu_read_lock_held() && !rcu_read_lock_bh_held()); /* If dst not in cache, we must take a reference, because * dst_release() will destroy dst as soon as its refcount becomes zero */ - hold = (dst->flags & (DST_NOCACHE | DST_RCU_FREE)) == DST_NOCACHE; - if (unlikely(hold)) { + if (unlikely(dst->flags & DST_NOCACHE)) { dst_hold(dst); skb_dst_set(skb, dst); } else { diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 26719779ad8e..85a3604c87c8 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -184,12 +184,6 @@ static __inline__ unsigned int dn_hash(__le16 src, __le16 dst) return dn_rt_hash_mask & (unsigned int)tmp; } -static inline void dst_rcu_free(struct rcu_head *head) -{ - struct dst_entry *dst = container_of(head, struct dst_entry, rcu_head); - dst_free(dst); -} - static inline void dnrt_free(struct dn_route *rt) { call_rcu_bh(&rt->dst.rcu_head, dst_rcu_free); diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index e55171f184f9..625cf185c489 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -161,6 +161,21 @@ static void free_nh_exceptions(struct fib_nh *nh) kfree(hash); } +static void rt_nexthop_free(struct rtable __rcu **rtp) +{ + struct rtable *rt = rcu_dereference_protected(*rtp, 1); + + if (!rt) + return; + + /* Not even needed : RCU_INIT_POINTER(*rtp, NULL); + * because we waited an RCU grace period before calling + * free_fib_info_rcu() + */ + + dst_free(&rt->dst); +} + /* Release a nexthop info record */ static void free_fib_info_rcu(struct rcu_head *head) { @@ -171,10 +186,8 @@ static void free_fib_info_rcu(struct rcu_head *head) dev_put(nexthop_nh->nh_dev); if (nexthop_nh->nh_exceptions) free_nh_exceptions(nexthop_nh); - if (nexthop_nh->nh_rth_output) - dst_release(&nexthop_nh->nh_rth_output->dst); - if (nexthop_nh->nh_rth_input) - dst_release(&nexthop_nh->nh_rth_input->dst); + rt_nexthop_free(&nexthop_nh->nh_rth_output); + rt_nexthop_free(&nexthop_nh->nh_rth_input); } endfor_nexthops(fi); release_net(fi->fib_net); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index d6eabcfe8a90..2bd107477469 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1199,23 +1199,31 @@ restart: fnhe->fnhe_stamp = jiffies; } +static inline void rt_free(struct rtable *rt) +{ + call_rcu(&rt->dst.rcu_head, dst_rcu_free); +} + static void rt_cache_route(struct fib_nh *nh, struct rtable *rt) { - struct rtable *orig, *prev, **p = &nh->nh_rth_output; + struct rtable *orig, *prev, **p = (struct rtable **)&nh->nh_rth_output; if (rt_is_input_route(rt)) - p = &nh->nh_rth_input; + p = (struct rtable **)&nh->nh_rth_input; orig = *p; - rt->dst.flags |= DST_RCU_FREE; - dst_hold(&rt->dst); prev = cmpxchg(p, orig, rt); if (prev == orig) { if (orig) - dst_release(&orig->dst); + rt_free(orig); } else { - dst_release(&rt->dst); + /* Routes we intend to cache in the FIB nexthop have + * the DST_NOCACHE bit clear. However, if we are + * unsuccessful at storing this route into the cache + * we really need to set it. + */ + rt->dst.flags |= DST_NOCACHE; } } @@ -1412,7 +1420,7 @@ static int __mkroute_input(struct sk_buff *skb, do_cache = false; if (res->fi) { if (!itag) { - rth = FIB_RES_NH(*res).nh_rth_input; + rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_input); if (rt_cache_valid(rth)) { skb_dst_set_noref(skb, &rth->dst); goto out; @@ -1574,7 +1582,7 @@ local_input: do_cache = false; if (res.fi) { if (!itag) { - rth = FIB_RES_NH(res).nh_rth_input; + rth = rcu_dereference(FIB_RES_NH(res).nh_rth_input); if (rt_cache_valid(rth)) { skb_dst_set_noref(skb, &rth->dst); err = 0; @@ -1742,7 +1750,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res, if (fi) { fnhe = find_exception(&FIB_RES_NH(*res), fl4->daddr); if (!fnhe) { - rth = FIB_RES_NH(*res).nh_rth_output; + rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_output); if (rt_cache_valid(rth)) { dst_hold(&rth->dst); return rth; -- cgit v1.2.3 From d26b3a7c4b3b26319f18bb645de93eba8f4bdcd5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 31 Jul 2012 05:45:30 +0000 Subject: ipv4: percpu nh_rth_output cache Input path is mostly run under RCU and doesnt touch dst refcnt But output path on forwarding or UDP workloads hits badly dst refcount, and we have lot of false sharing, for example in ipv4_mtu() when reading rt->rt_pmtu Using a percpu cache for nh_rth_output gives a nice performance increase at a small cost. 24 udpflood test on my 24 cpu machine (dummy0 output device) (each process sends 1.000.000 udp frames, 24 processes are started) before : 5.24 s after : 2.06 s For reference, time on linux-3.5 : 6.60 s Signed-off-by: Eric Dumazet Tested-by: Alexander Duyck Signed-off-by: David S. Miller --- include/net/ip_fib.h | 3 ++- net/ipv4/fib_semantics.c | 20 +++++++++++++++++++- net/ipv4/route.c | 18 +++++++++++++----- 3 files changed, 34 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index e521a03515b1..e331746029b4 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -21,6 +21,7 @@ #include #include #include +#include struct fib_config { u8 fc_dst_len; @@ -81,7 +82,7 @@ struct fib_nh { __be32 nh_gw; __be32 nh_saddr; int nh_saddr_genid; - struct rtable __rcu *nh_rth_output; + struct rtable __rcu * __percpu *nh_pcpu_rth_output; struct rtable __rcu *nh_rth_input; struct fnhe_hash_bucket *nh_exceptions; }; diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 625cf185c489..fe2ca02a1979 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -176,6 +176,23 @@ static void rt_nexthop_free(struct rtable __rcu **rtp) dst_free(&rt->dst); } +static void rt_nexthop_free_cpus(struct rtable __rcu * __percpu *rtp) +{ + int cpu; + + if (!rtp) + return; + + for_each_possible_cpu(cpu) { + struct rtable *rt; + + rt = rcu_dereference_protected(*per_cpu_ptr(rtp, cpu), 1); + if (rt) + dst_free(&rt->dst); + } + free_percpu(rtp); +} + /* Release a nexthop info record */ static void free_fib_info_rcu(struct rcu_head *head) { @@ -186,7 +203,7 @@ static void free_fib_info_rcu(struct rcu_head *head) dev_put(nexthop_nh->nh_dev); if (nexthop_nh->nh_exceptions) free_nh_exceptions(nexthop_nh); - rt_nexthop_free(&nexthop_nh->nh_rth_output); + rt_nexthop_free_cpus(nexthop_nh->nh_pcpu_rth_output); rt_nexthop_free(&nexthop_nh->nh_rth_input); } endfor_nexthops(fi); @@ -817,6 +834,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg) fi->fib_nhs = nhs; change_nexthops(fi) { nexthop_nh->nh_parent = fi; + nexthop_nh->nh_pcpu_rth_output = alloc_percpu(struct rtable __rcu *); } endfor_nexthops(fi) if (cfg->fc_mx) { diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 2bd107477469..4f6276ce0af3 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1206,11 +1206,15 @@ static inline void rt_free(struct rtable *rt) static void rt_cache_route(struct fib_nh *nh, struct rtable *rt) { - struct rtable *orig, *prev, **p = (struct rtable **)&nh->nh_rth_output; + struct rtable *orig, *prev, **p; - if (rt_is_input_route(rt)) + if (rt_is_input_route(rt)) { p = (struct rtable **)&nh->nh_rth_input; - + } else { + if (!nh->nh_pcpu_rth_output) + goto nocache; + p = (struct rtable **)__this_cpu_ptr(nh->nh_pcpu_rth_output); + } orig = *p; prev = cmpxchg(p, orig, rt); @@ -1223,6 +1227,7 @@ static void rt_cache_route(struct fib_nh *nh, struct rtable *rt) * unsuccessful at storing this route into the cache * we really need to set it. */ +nocache: rt->dst.flags |= DST_NOCACHE; } } @@ -1749,8 +1754,11 @@ static struct rtable *__mkroute_output(const struct fib_result *res, fnhe = NULL; if (fi) { fnhe = find_exception(&FIB_RES_NH(*res), fl4->daddr); - if (!fnhe) { - rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_output); + if (!fnhe && FIB_RES_NH(*res).nh_pcpu_rth_output) { + struct rtable __rcu **prth; + + prth = __this_cpu_ptr(FIB_RES_NH(*res).nh_pcpu_rth_output); + rth = rcu_dereference(*prth); if (rt_cache_valid(rth)) { dst_hold(&rth->dst); return rth; -- cgit v1.2.3 From c5038a8327b980a5b279fa193163c468011de009 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 31 Jul 2012 15:02:02 -0700 Subject: ipv4: Cache routes in nexthop exception entries. Signed-off-by: David S. Miller --- include/net/ip_fib.h | 1 + net/ipv4/fib_semantics.c | 39 +++++++++--------- net/ipv4/route.c | 103 ++++++++++++++++++++++++++--------------------- 3 files changed, 79 insertions(+), 64 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index e331746029b4..926142ed8d7a 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -55,6 +55,7 @@ struct fib_nh_exception { u32 fnhe_pmtu; __be32 fnhe_gw; unsigned long fnhe_expires; + struct rtable __rcu *fnhe_rth; unsigned long fnhe_stamp; }; diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index fe2ca02a1979..da80dc14cc76 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -140,6 +140,21 @@ const struct fib_prop fib_props[RTN_MAX + 1] = { }, }; +static void rt_fibinfo_free(struct rtable __rcu **rtp) +{ + struct rtable *rt = rcu_dereference_protected(*rtp, 1); + + if (!rt) + return; + + /* Not even needed : RCU_INIT_POINTER(*rtp, NULL); + * because we waited an RCU grace period before calling + * free_fib_info_rcu() + */ + + dst_free(&rt->dst); +} + static void free_nh_exceptions(struct fib_nh *nh) { struct fnhe_hash_bucket *hash = nh->nh_exceptions; @@ -153,6 +168,9 @@ static void free_nh_exceptions(struct fib_nh *nh) struct fib_nh_exception *next; next = rcu_dereference_protected(fnhe->fnhe_next, 1); + + rt_fibinfo_free(&fnhe->fnhe_rth); + kfree(fnhe); fnhe = next; @@ -161,22 +179,7 @@ static void free_nh_exceptions(struct fib_nh *nh) kfree(hash); } -static void rt_nexthop_free(struct rtable __rcu **rtp) -{ - struct rtable *rt = rcu_dereference_protected(*rtp, 1); - - if (!rt) - return; - - /* Not even needed : RCU_INIT_POINTER(*rtp, NULL); - * because we waited an RCU grace period before calling - * free_fib_info_rcu() - */ - - dst_free(&rt->dst); -} - -static void rt_nexthop_free_cpus(struct rtable __rcu * __percpu *rtp) +static void rt_fibinfo_free_cpus(struct rtable __rcu * __percpu *rtp) { int cpu; @@ -203,8 +206,8 @@ static void free_fib_info_rcu(struct rcu_head *head) dev_put(nexthop_nh->nh_dev); if (nexthop_nh->nh_exceptions) free_nh_exceptions(nexthop_nh); - rt_nexthop_free_cpus(nexthop_nh->nh_pcpu_rth_output); - rt_nexthop_free(&nexthop_nh->nh_rth_input); + rt_fibinfo_free_cpus(nexthop_nh->nh_pcpu_rth_output); + rt_fibinfo_free(&nexthop_nh->nh_rth_input); } endfor_nexthops(fi); release_net(fi->fib_net); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 4f6276ce0af3..b102eeb16e34 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -587,11 +587,17 @@ static void ip_rt_build_flow_key(struct flowi4 *fl4, const struct sock *sk, build_sk_flow_key(fl4, sk); } -static DEFINE_SEQLOCK(fnhe_seqlock); +static inline void rt_free(struct rtable *rt) +{ + call_rcu(&rt->dst.rcu_head, dst_rcu_free); +} + +static DEFINE_SPINLOCK(fnhe_lock); static struct fib_nh_exception *fnhe_oldest(struct fnhe_hash_bucket *hash) { struct fib_nh_exception *fnhe, *oldest; + struct rtable *orig; oldest = rcu_dereference(hash->chain); for (fnhe = rcu_dereference(oldest->fnhe_next); fnhe; @@ -599,6 +605,11 @@ static struct fib_nh_exception *fnhe_oldest(struct fnhe_hash_bucket *hash) if (time_before(fnhe->fnhe_stamp, oldest->fnhe_stamp)) oldest = fnhe; } + orig = rcu_dereference(oldest->fnhe_rth); + if (orig) { + RCU_INIT_POINTER(oldest->fnhe_rth, NULL); + rt_free(orig); + } return oldest; } @@ -620,7 +631,7 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, int depth; u32 hval = fnhe_hashfun(daddr); - write_seqlock_bh(&fnhe_seqlock); + spin_lock_bh(&fnhe_lock); hash = nh->nh_exceptions; if (!hash) { @@ -667,7 +678,7 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, fnhe->fnhe_stamp = jiffies; out_unlock: - write_sequnlock_bh(&fnhe_seqlock); + spin_unlock_bh(&fnhe_lock); return; } @@ -1167,41 +1178,40 @@ static struct fib_nh_exception *find_exception(struct fib_nh *nh, __be32 daddr) static void rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe, __be32 daddr) { - __be32 fnhe_daddr, gw; - unsigned long expires; - unsigned int seq; - u32 pmtu; - -restart: - seq = read_seqbegin(&fnhe_seqlock); - fnhe_daddr = fnhe->fnhe_daddr; - gw = fnhe->fnhe_gw; - pmtu = fnhe->fnhe_pmtu; - expires = fnhe->fnhe_expires; - if (read_seqretry(&fnhe_seqlock, seq)) - goto restart; - - if (daddr != fnhe_daddr) - return; + spin_lock_bh(&fnhe_lock); - if (pmtu) { - unsigned long diff = expires - jiffies; + if (daddr == fnhe->fnhe_daddr) { + struct rtable *orig; - if (time_before(jiffies, expires)) { - rt->rt_pmtu = pmtu; - dst_set_expires(&rt->dst, diff); + if (fnhe->fnhe_pmtu) { + unsigned long expires = fnhe->fnhe_expires; + unsigned long diff = expires - jiffies; + + if (time_before(jiffies, expires)) { + rt->rt_pmtu = fnhe->fnhe_pmtu; + dst_set_expires(&rt->dst, diff); + } + } + if (fnhe->fnhe_gw) { + rt->rt_flags |= RTCF_REDIRECTED; + rt->rt_gateway = fnhe->fnhe_gw; } - } - if (gw) { - rt->rt_flags |= RTCF_REDIRECTED; - rt->rt_gateway = gw; - } - fnhe->fnhe_stamp = jiffies; -} -static inline void rt_free(struct rtable *rt) -{ - call_rcu(&rt->dst.rcu_head, dst_rcu_free); + orig = rcu_dereference(fnhe->fnhe_rth); + rcu_assign_pointer(fnhe->fnhe_rth, rt); + if (orig) + rt_free(orig); + + fnhe->fnhe_stamp = jiffies; + } else { + /* Routes we intend to cache in nexthop exception have + * the DST_NOCACHE bit clear. However, if we are + * unsuccessful at storing this route into the cache + * we really need to set it. + */ + rt->dst.flags |= DST_NOCACHE; + } + spin_unlock_bh(&fnhe_lock); } static void rt_cache_route(struct fib_nh *nh, struct rtable *rt) @@ -1249,13 +1259,13 @@ static void rt_set_nexthop(struct rtable *rt, __be32 daddr, if (nh->nh_gw && nh->nh_scope == RT_SCOPE_LINK) rt->rt_gateway = nh->nh_gw; - if (unlikely(fnhe)) - rt_bind_exception(rt, fnhe, daddr); dst_init_metrics(&rt->dst, fi->fib_metrics, true); #ifdef CONFIG_IP_ROUTE_CLASSID rt->dst.tclassid = nh->nh_tclassid; #endif - if (!(rt->dst.flags & DST_NOCACHE)) + if (unlikely(fnhe)) + rt_bind_exception(rt, fnhe, daddr); + else if (!(rt->dst.flags & DST_NOCACHE)) rt_cache_route(nh, rt); } @@ -1753,22 +1763,23 @@ static struct rtable *__mkroute_output(const struct fib_result *res, fnhe = NULL; if (fi) { - fnhe = find_exception(&FIB_RES_NH(*res), fl4->daddr); - if (!fnhe && FIB_RES_NH(*res).nh_pcpu_rth_output) { - struct rtable __rcu **prth; + struct rtable __rcu **prth; + fnhe = find_exception(&FIB_RES_NH(*res), fl4->daddr); + if (fnhe) + prth = &fnhe->fnhe_rth; + else prth = __this_cpu_ptr(FIB_RES_NH(*res).nh_pcpu_rth_output); - rth = rcu_dereference(*prth); - if (rt_cache_valid(rth)) { - dst_hold(&rth->dst); - return rth; - } + rth = rcu_dereference(*prth); + if (rt_cache_valid(rth)) { + dst_hold(&rth->dst); + return rth; } } rth = rt_dst_alloc(dev_out, IN_DEV_CONF_GET(in_dev, NOPOLICY), IN_DEV_CONF_GET(in_dev, NOXFRM), - fi && !fnhe); + fi); if (!rth) return ERR_PTR(-ENOBUFS); -- cgit v1.2.3 From caacf05e5ad1abf0a2864863da4e33024bc68ec6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 31 Jul 2012 15:06:50 -0700 Subject: ipv4: Properly purge netdev references on uncached routes. When a device is unregistered, we have to purge all of the references to it that may exist in the entire system. If a route is uncached, we currently have no way of accomplishing this. So create a global list that is scanned when a network device goes down. This mirrors the logic in net/core/dst.c's dst_ifdown(). Signed-off-by: David S. Miller --- include/net/route.h | 3 +++ net/ipv4/fib_frontend.c | 1 + net/ipv4/route.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++--- net/ipv4/xfrm4_policy.c | 1 + 4 files changed, 69 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/route.h b/include/net/route.h index 8c52bc6f1c90..776a27f1ab78 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -57,6 +57,8 @@ struct rtable { /* Miscellaneous cached information */ u32 rt_pmtu; + + struct list_head rt_uncached; }; static inline bool rt_is_input_route(const struct rtable *rt) @@ -107,6 +109,7 @@ extern struct ip_rt_acct __percpu *ip_rt_acct; struct in_device; extern int ip_rt_init(void); extern void rt_cache_flush(struct net *net, int how); +extern void rt_flush_dev(struct net_device *dev); extern struct rtable *__ip_route_output_key(struct net *, struct flowi4 *flp); extern struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp, struct sock *sk); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 8732cc7920ed..c43ae3fba792 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1046,6 +1046,7 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo if (event == NETDEV_UNREGISTER) { fib_disable_ip(dev, 2, -1); + rt_flush_dev(dev); return NOTIFY_DONE; } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index b102eeb16e34..c035251beb07 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -147,6 +147,7 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb, u32 mtu); static void ip_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb); +static void ipv4_dst_destroy(struct dst_entry *dst); static void ipv4_dst_ifdown(struct dst_entry *dst, struct net_device *dev, int how) @@ -170,6 +171,7 @@ static struct dst_ops ipv4_dst_ops = { .default_advmss = ipv4_default_advmss, .mtu = ipv4_mtu, .cow_metrics = ipv4_cow_metrics, + .destroy = ipv4_dst_destroy, .ifdown = ipv4_dst_ifdown, .negative_advice = ipv4_negative_advice, .link_failure = ipv4_link_failure, @@ -1175,9 +1177,11 @@ static struct fib_nh_exception *find_exception(struct fib_nh *nh, __be32 daddr) return NULL; } -static void rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe, +static bool rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe, __be32 daddr) { + bool ret = false; + spin_lock_bh(&fnhe_lock); if (daddr == fnhe->fnhe_daddr) { @@ -1203,6 +1207,7 @@ static void rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe, rt_free(orig); fnhe->fnhe_stamp = jiffies; + ret = true; } else { /* Routes we intend to cache in nexthop exception have * the DST_NOCACHE bit clear. However, if we are @@ -1212,11 +1217,14 @@ static void rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe, rt->dst.flags |= DST_NOCACHE; } spin_unlock_bh(&fnhe_lock); + + return ret; } -static void rt_cache_route(struct fib_nh *nh, struct rtable *rt) +static bool rt_cache_route(struct fib_nh *nh, struct rtable *rt) { struct rtable *orig, *prev, **p; + bool ret = true; if (rt_is_input_route(rt)) { p = (struct rtable **)&nh->nh_rth_input; @@ -1239,6 +1247,48 @@ static void rt_cache_route(struct fib_nh *nh, struct rtable *rt) */ nocache: rt->dst.flags |= DST_NOCACHE; + ret = false; + } + + return ret; +} + +static DEFINE_SPINLOCK(rt_uncached_lock); +static LIST_HEAD(rt_uncached_list); + +static void rt_add_uncached_list(struct rtable *rt) +{ + spin_lock_bh(&rt_uncached_lock); + list_add_tail(&rt->rt_uncached, &rt_uncached_list); + spin_unlock_bh(&rt_uncached_lock); +} + +static void ipv4_dst_destroy(struct dst_entry *dst) +{ + struct rtable *rt = (struct rtable *) dst; + + if (dst->flags & DST_NOCACHE) { + spin_lock_bh(&rt_uncached_lock); + list_del(&rt->rt_uncached); + spin_unlock_bh(&rt_uncached_lock); + } +} + +void rt_flush_dev(struct net_device *dev) +{ + if (!list_empty(&rt_uncached_list)) { + struct net *net = dev_net(dev); + struct rtable *rt; + + spin_lock_bh(&rt_uncached_lock); + list_for_each_entry(rt, &rt_uncached_list, rt_uncached) { + if (rt->dst.dev != dev) + continue; + rt->dst.dev = net->loopback_dev; + dev_hold(rt->dst.dev); + dev_put(dev); + } + spin_unlock_bh(&rt_uncached_lock); } } @@ -1254,6 +1304,8 @@ static void rt_set_nexthop(struct rtable *rt, __be32 daddr, struct fib_nh_exception *fnhe, struct fib_info *fi, u16 type, u32 itag) { + bool cached = false; + if (fi) { struct fib_nh *nh = &FIB_RES_NH(*res); @@ -1264,10 +1316,12 @@ static void rt_set_nexthop(struct rtable *rt, __be32 daddr, rt->dst.tclassid = nh->nh_tclassid; #endif if (unlikely(fnhe)) - rt_bind_exception(rt, fnhe, daddr); + cached = rt_bind_exception(rt, fnhe, daddr); else if (!(rt->dst.flags & DST_NOCACHE)) - rt_cache_route(nh, rt); + cached = rt_cache_route(nh, rt); } + if (unlikely(!cached)) + rt_add_uncached_list(rt); #ifdef CONFIG_IP_ROUTE_CLASSID #ifdef CONFIG_IP_MULTIPLE_TABLES @@ -1334,6 +1388,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, rth->rt_iif = 0; rth->rt_pmtu = 0; rth->rt_gateway = 0; + INIT_LIST_HEAD(&rth->rt_uncached); if (our) { rth->dst.input= ip_local_deliver; rth->rt_flags |= RTCF_LOCAL; @@ -1459,6 +1514,7 @@ static int __mkroute_input(struct sk_buff *skb, rth->rt_iif = 0; rth->rt_pmtu = 0; rth->rt_gateway = 0; + INIT_LIST_HEAD(&rth->rt_uncached); rth->dst.input = ip_forward; rth->dst.output = ip_output; @@ -1625,6 +1681,7 @@ local_input: rth->rt_iif = 0; rth->rt_pmtu = 0; rth->rt_gateway = 0; + INIT_LIST_HEAD(&rth->rt_uncached); if (res.type == RTN_UNREACHABLE) { rth->dst.input= ip_error; rth->dst.error= -err; @@ -1792,6 +1849,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res, rth->rt_iif = orig_oif ? : 0; rth->rt_pmtu = 0; rth->rt_gateway = 0; + INIT_LIST_HEAD(&rth->rt_uncached); RT_CACHE_STAT_INC(out_slow_tot); @@ -2071,6 +2129,8 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or rt->rt_type = ort->rt_type; rt->rt_gateway = ort->rt_gateway; + INIT_LIST_HEAD(&rt->rt_uncached); + dst_free(new); } diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index c6281847f16a..681ea2f413e2 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -92,6 +92,7 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, xdst->u.rt.rt_type = rt->rt_type; xdst->u.rt.rt_gateway = rt->rt_gateway; xdst->u.rt.rt_pmtu = rt->rt_pmtu; + INIT_LIST_HEAD(&xdst->u.rt.rt_uncached); return 0; } -- cgit v1.2.3 From 44de9d0cad41f2c51ef26916842be046b582dcc9 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Tue, 31 Jul 2012 16:41:49 -0700 Subject: mm: account the total_vm in the vm_stat_account() vm_stat_account() accounts the shared_vm, stack_vm and reserved_vm now. But we can also account for total_vm in the vm_stat_account() which makes the code tidy. Even for mprotect_fixup(), we can get the right result in the end. Signed-off-by: Huang Shijie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/kernel/perfmon.c | 1 - include/linux/mm.h | 1 + kernel/fork.c | 4 +--- mm/mmap.c | 5 ++--- mm/mremap.c | 2 -- 5 files changed, 4 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index d7f558c1e711..3fa4bc536953 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -2353,7 +2353,6 @@ pfm_smpl_buffer_alloc(struct task_struct *task, struct file *filp, pfm_context_t */ insert_vm_struct(mm, vma); - mm->total_vm += size >> PAGE_SHIFT; vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file, vma_pages(vma)); up_write(&task->mm->mmap_sem); diff --git a/include/linux/mm.h b/include/linux/mm.h index f9f279cf5b1b..3955bedeeed1 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1528,6 +1528,7 @@ void vm_stat_account(struct mm_struct *, unsigned long, struct file *, long); static inline void vm_stat_account(struct mm_struct *mm, unsigned long flags, struct file *file, long pages) { + mm->total_vm += pages; } #endif /* CONFIG_PROC_FS */ diff --git a/kernel/fork.c b/kernel/fork.c index 8efac1fe56bc..aaa8813c45d1 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -381,10 +381,8 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) struct file *file; if (mpnt->vm_flags & VM_DONTCOPY) { - long pages = vma_pages(mpnt); - mm->total_vm -= pages; vm_stat_account(mm, mpnt->vm_flags, mpnt->vm_file, - -pages); + -vma_pages(mpnt)); continue; } charge = 0; diff --git a/mm/mmap.c b/mm/mmap.c index 3edfcdfa42d9..1ee2fd8bc486 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -943,6 +943,8 @@ void vm_stat_account(struct mm_struct *mm, unsigned long flags, const unsigned long stack_flags = VM_STACK_FLAGS & (VM_GROWSUP|VM_GROWSDOWN); + mm->total_vm += pages; + if (file) { mm->shared_vm += pages; if ((flags & (VM_EXEC|VM_WRITE)) == VM_EXEC) @@ -1347,7 +1349,6 @@ munmap_back: out: perf_event_mmap(vma); - mm->total_vm += len >> PAGE_SHIFT; vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT); if (vm_flags & VM_LOCKED) { if (!mlock_vma_pages_range(vma, addr, addr + len)) @@ -1707,7 +1708,6 @@ static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, uns return -ENOMEM; /* Ok, everything looks good - let it rip */ - mm->total_vm += grow; if (vma->vm_flags & VM_LOCKED) mm->locked_vm += grow; vm_stat_account(mm, vma->vm_flags, vma->vm_file, grow); @@ -1889,7 +1889,6 @@ static void remove_vma_list(struct mm_struct *mm, struct vm_area_struct *vma) if (vma->vm_flags & VM_ACCOUNT) nr_accounted += nrpages; - mm->total_vm -= nrpages; vm_stat_account(mm, vma->vm_flags, vma->vm_file, -nrpages); vma = remove_vma(vma); } while (vma); diff --git a/mm/mremap.c b/mm/mremap.c index 21fed202ddad..cc06d0e48d05 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -260,7 +260,6 @@ static unsigned long move_vma(struct vm_area_struct *vma, * If this were a serious issue, we'd add a flag to do_munmap(). */ hiwater_vm = mm->hiwater_vm; - mm->total_vm += new_len >> PAGE_SHIFT; vm_stat_account(mm, vma->vm_flags, vma->vm_file, new_len>>PAGE_SHIFT); if (do_munmap(mm, old_addr, old_len) < 0) { @@ -497,7 +496,6 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len, goto out; } - mm->total_vm += pages; vm_stat_account(mm, vma->vm_flags, vma->vm_file, pages); if (vma->vm_flags & VM_LOCKED) { mm->locked_vm += pages; -- cgit v1.2.3 From 3965c9ae47d64aadf6f13b6fcd37767b83c0689a Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Tue, 31 Jul 2012 16:41:52 -0700 Subject: mm: prepare for removal of obsolete /proc/sys/vm/nr_pdflush_threads Since per-BDI flusher threads were introduced in 2.6, the pdflush mechanism is not used any more. But the old interface exported through /proc/sys/vm/nr_pdflush_threads still exists and is obviously useless. For back-compatibility, printk warning information and return 2 to notify the users that the interface is removed. Signed-off-by: Wanpeng Li Cc: Wu Fengguang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- .../ABI/obsolete/proc-sys-vm-nr_pdflush_threads | 5 +++++ Documentation/feature-removal-schedule.txt | 8 ++++++++ Documentation/sysctl/vm.txt | 11 ----------- fs/fs-writeback.c | 5 ----- include/linux/backing-dev.h | 3 +++ include/linux/writeback.h | 5 ----- kernel/sysctl.c | 8 +++----- kernel/sysctl_binary.c | 2 +- mm/backing-dev.c | 20 ++++++++++++++++++++ 9 files changed, 40 insertions(+), 27 deletions(-) create mode 100644 Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads (limited to 'include') diff --git a/Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads b/Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads new file mode 100644 index 000000000000..b0b0eeb20fe3 --- /dev/null +++ b/Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads @@ -0,0 +1,5 @@ +What: /proc/sys/vm/nr_pdflush_threads +Date: June 2012 +Contact: Wanpeng Li +Description: Since pdflush is replaced by per-BDI flusher, the interface of old pdflush + exported in /proc/sys/vm/ should be removed. diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index e9237fb71950..88f2fa48bb63 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -13,6 +13,14 @@ Who: Jim Cromie , Jason Baron --------------------------- +What: /proc/sys/vm/nr_pdflush_threads +When: 2012 +Why: Since pdflush is deprecated, the interface exported in /proc/sys/vm/ + should be removed. +Who: Wanpeng Li + +--------------------------- + What: CONFIG_APM_CPU_IDLE, and its ability to call APM BIOS in idle When: 2012 Why: This optional sub-feature of APM is of dubious reliability, diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index 84eb25cd69aa..06d662b1c5d5 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -42,7 +42,6 @@ Currently, these files are in /proc/sys/vm: - mmap_min_addr - nr_hugepages - nr_overcommit_hugepages -- nr_pdflush_threads - nr_trim_pages (only if CONFIG_MMU=n) - numa_zonelist_order - oom_dump_tasks @@ -426,16 +425,6 @@ See Documentation/vm/hugetlbpage.txt ============================================================== -nr_pdflush_threads - -The current number of pdflush threads. This value is read-only. -The value changes according to the number of dirty pages in the system. - -When necessary, additional pdflush threads are created, one per second, up to -nr_pdflush_threads_max. - -============================================================== - nr_trim_pages This is available only on NOMMU kernels. diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 50d0b78130a1..be3efc4f64f4 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -52,11 +52,6 @@ struct wb_writeback_work { struct completion *done; /* set if the caller waits */ }; -/* - * We don't actually have pdflush, but this one is exported though /proc... - */ -int nr_pdflush_threads; - /** * writeback_in_progress - determine whether there is writeback in progress * @bdi: the device's backing_dev_info structure. diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 489de625cd25..c97c6b9cd38e 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -17,6 +17,7 @@ #include #include #include +#include struct page; struct device; @@ -304,6 +305,8 @@ void clear_bdi_congested(struct backing_dev_info *bdi, int sync); void set_bdi_congested(struct backing_dev_info *bdi, int sync); long congestion_wait(int sync, long timeout); long wait_iff_congested(struct zone *zone, int sync, long timeout); +int pdflush_proc_obsolete(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); static inline bool bdi_cap_writeback_dirty(struct backing_dev_info *bdi) { diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 6d0a0fcd80e7..c66fe3332d83 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -189,9 +189,4 @@ void tag_pages_for_writeback(struct address_space *mapping, void account_page_redirty(struct page *page); -/* pdflush.c */ -extern int nr_pdflush_threads; /* Global so it can be exported to sysctl - read-only. */ - - #endif /* WRITEBACK_H */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 97186b99b0e4..6502d35a25ba 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1101,11 +1101,9 @@ static struct ctl_table vm_table[] = { .extra1 = &zero, }, { - .procname = "nr_pdflush_threads", - .data = &nr_pdflush_threads, - .maxlen = sizeof nr_pdflush_threads, - .mode = 0444 /* read-only*/, - .proc_handler = proc_dointvec, + .procname = "nr_pdflush_threads", + .mode = 0444 /* read-only */, + .proc_handler = pdflush_proc_obsolete, }, { .procname = "swappiness", diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c index a650694883a1..65bdcf198d4e 100644 --- a/kernel/sysctl_binary.c +++ b/kernel/sysctl_binary.c @@ -147,7 +147,7 @@ static const struct bin_table bin_vm_table[] = { { CTL_INT, VM_DIRTY_RATIO, "dirty_ratio" }, /* VM_DIRTY_WB_CS "dirty_writeback_centisecs" no longer used */ /* VM_DIRTY_EXPIRE_CS "dirty_expire_centisecs" no longer used */ - { CTL_INT, VM_NR_PDFLUSH_THREADS, "nr_pdflush_threads" }, + /* VM_NR_PDFLUSH_THREADS "nr_pdflush_threads" no longer used */ { CTL_INT, VM_OVERCOMMIT_RATIO, "overcommit_ratio" }, /* VM_PAGEBUF unused */ /* VM_HUGETLB_PAGES "nr_hugepages" no longer used */ diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 3387aea11209..6b4718e2ee34 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -886,3 +886,23 @@ out: return ret; } EXPORT_SYMBOL(wait_iff_congested); + +int pdflush_proc_obsolete(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + char kbuf[] = "0\n"; + + if (*ppos) { + *lenp = 0; + return 0; + } + + if (copy_to_user(buffer, kbuf, sizeof(kbuf))) + return -EFAULT; + printk_once(KERN_WARNING "%s exported in /proc is scheduled for removal\n", + table->procname); + + *lenp = 2; + *ppos += *lenp; + return 2; +} -- cgit v1.2.3 From 972dc4de13f667a7df27ee32573b2e6fc6cc8434 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:00 -0700 Subject: hugetlb: add an inline helper for finding hstate index Add an inline helper and use it in the code. Signed-off-by: Aneesh Kumar K.V Acked-by: David Rientjes Acked-by: Michal Hocko Reviewed-by: KAMEZAWA Hiroyuki Cc: Hillf Danton Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 6 ++++++ mm/hugetlb.c | 20 +++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index d5d6bbe2259e..217f52859fa7 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -302,6 +302,11 @@ static inline unsigned hstate_index_to_shift(unsigned index) return hstates[index].order + PAGE_SHIFT; } +static inline int hstate_index(struct hstate *h) +{ + return h - hstates; +} + #else struct hstate {}; #define alloc_huge_page_node(h, nid) NULL @@ -320,6 +325,7 @@ static inline unsigned int pages_per_huge_page(struct hstate *h) return 1; } #define hstate_index_to_shift(index) 0 +#define hstate_index(h) 0 #endif #endif /* _LINUX_HUGETLB_H */ diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 34a7e2375478..b1e0ed1ea912 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1646,7 +1646,7 @@ static int hugetlb_sysfs_add_hstate(struct hstate *h, struct kobject *parent, struct attribute_group *hstate_attr_group) { int retval; - int hi = h - hstates; + int hi = hstate_index(h); hstate_kobjs[hi] = kobject_create_and_add(h->name, parent); if (!hstate_kobjs[hi]) @@ -1741,11 +1741,13 @@ void hugetlb_unregister_node(struct node *node) if (!nhs->hugepages_kobj) return; /* no hstate attributes */ - for_each_hstate(h) - if (nhs->hstate_kobjs[h - hstates]) { - kobject_put(nhs->hstate_kobjs[h - hstates]); - nhs->hstate_kobjs[h - hstates] = NULL; + for_each_hstate(h) { + int idx = hstate_index(h); + if (nhs->hstate_kobjs[idx]) { + kobject_put(nhs->hstate_kobjs[idx]); + nhs->hstate_kobjs[idx] = NULL; } + } kobject_put(nhs->hugepages_kobj); nhs->hugepages_kobj = NULL; @@ -1848,7 +1850,7 @@ static void __exit hugetlb_exit(void) hugetlb_unregister_all_nodes(); for_each_hstate(h) { - kobject_put(hstate_kobjs[h - hstates]); + kobject_put(hstate_kobjs[hstate_index(h)]); } kobject_put(hugepages_kobj); @@ -1869,7 +1871,7 @@ static int __init hugetlb_init(void) if (!size_to_hstate(default_hstate_size)) hugetlb_add_hstate(HUGETLB_PAGE_ORDER); } - default_hstate_idx = size_to_hstate(default_hstate_size) - hstates; + default_hstate_idx = hstate_index(size_to_hstate(default_hstate_size)); if (default_hstate_max_huge_pages) default_hstate.max_huge_pages = default_hstate_max_huge_pages; @@ -2687,7 +2689,7 @@ retry: */ if (unlikely(PageHWPoison(page))) { ret = VM_FAULT_HWPOISON | - VM_FAULT_SET_HINDEX(h - hstates); + VM_FAULT_SET_HINDEX(hstate_index(h)); goto backout_unlocked; } } @@ -2760,7 +2762,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, return 0; } else if (unlikely(is_hugetlb_entry_hwpoisoned(entry))) return VM_FAULT_HWPOISON_LARGE | - VM_FAULT_SET_HINDEX(h - hstates); + VM_FAULT_SET_HINDEX(hstate_index(h)); } ptep = huge_pte_alloc(mm, address, huge_page_size(h)); -- cgit v1.2.3 From 24669e58477e2752c1fbca9c1c988e9dd0d79d15 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:03 -0700 Subject: hugetlb: use mmu_gather instead of a temporary linked list for accumulating pages Use a mmu_gather instead of a temporary linked list for accumulating pages when we unmap a hugepage range Signed-off-by: Aneesh Kumar K.V Reviewed-by: KAMEZAWA Hiroyuki Cc: David Rientjes Cc: Hillf Danton Cc: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/hugetlbfs/inode.c | 4 ++-- include/linux/hugetlb.h | 22 +++++++++++++----- mm/hugetlb.c | 59 +++++++++++++++++++++++++++++-------------------- mm/memory.c | 7 ++++-- 4 files changed, 59 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index e13e9bdb0bf5..8349a899912e 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -416,8 +416,8 @@ hugetlb_vmtruncate_list(struct prio_tree_root *root, pgoff_t pgoff) else v_offset = 0; - __unmap_hugepage_range(vma, - vma->vm_start + v_offset, vma->vm_end, NULL); + unmap_hugepage_range(vma, vma->vm_start + v_offset, + vma->vm_end, NULL); } } diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 217f52859fa7..0f23c1840c9b 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -7,6 +7,7 @@ struct ctl_table; struct user_struct; +struct mmu_gather; #ifdef CONFIG_HUGETLB_PAGE @@ -40,9 +41,10 @@ int follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *, struct page **, struct vm_area_struct **, unsigned long *, int *, int, unsigned int flags); void unmap_hugepage_range(struct vm_area_struct *, - unsigned long, unsigned long, struct page *); -void __unmap_hugepage_range(struct vm_area_struct *, - unsigned long, unsigned long, struct page *); + unsigned long, unsigned long, struct page *); +void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, + unsigned long start, unsigned long end, + struct page *ref_page); int hugetlb_prefault(struct address_space *, struct vm_area_struct *); void hugetlb_report_meminfo(struct seq_file *); int hugetlb_report_node_meminfo(int, char *); @@ -98,7 +100,6 @@ static inline unsigned long hugetlb_total_pages(void) #define follow_huge_addr(mm, addr, write) ERR_PTR(-EINVAL) #define copy_hugetlb_page_range(src, dst, vma) ({ BUG(); 0; }) #define hugetlb_prefault(mapping, vma) ({ BUG(); 0; }) -#define unmap_hugepage_range(vma, start, end, page) BUG() static inline void hugetlb_report_meminfo(struct seq_file *m) { } @@ -112,13 +113,24 @@ static inline void hugetlb_report_meminfo(struct seq_file *m) #define hugetlb_free_pgd_range(tlb, addr, end, floor, ceiling) ({BUG(); 0; }) #define hugetlb_fault(mm, vma, addr, flags) ({ BUG(); 0; }) #define huge_pte_offset(mm, address) 0 -#define dequeue_hwpoisoned_huge_page(page) 0 +static inline int dequeue_hwpoisoned_huge_page(struct page *page) +{ + return 0; +} + static inline void copy_huge_page(struct page *dst, struct page *src) { } #define hugetlb_change_protection(vma, address, end, newprot) +static inline void __unmap_hugepage_range(struct mmu_gather *tlb, + struct vm_area_struct *vma, unsigned long start, + unsigned long end, struct page *ref_page) +{ + BUG(); +} + #endif /* !CONFIG_HUGETLB_PAGE */ #define HUGETLB_ANON_FILE "anon_hugepage" diff --git a/mm/hugetlb.c b/mm/hugetlb.c index b1e0ed1ea912..e54b695336f9 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -24,8 +24,9 @@ #include #include -#include +#include +#include #include #include #include "internal.h" @@ -2310,30 +2311,26 @@ static int is_hugetlb_entry_hwpoisoned(pte_t pte) return 0; } -void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, - unsigned long end, struct page *ref_page) +void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, + unsigned long start, unsigned long end, + struct page *ref_page) { + int force_flush = 0; struct mm_struct *mm = vma->vm_mm; unsigned long address; pte_t *ptep; pte_t pte; struct page *page; - struct page *tmp; struct hstate *h = hstate_vma(vma); unsigned long sz = huge_page_size(h); - /* - * A page gathering list, protected by per file i_mmap_mutex. The - * lock is used to avoid list corruption from multiple unmapping - * of the same page since we are using page->lru. - */ - LIST_HEAD(page_list); - WARN_ON(!is_vm_hugetlb_page(vma)); BUG_ON(start & ~huge_page_mask(h)); BUG_ON(end & ~huge_page_mask(h)); + tlb_start_vma(tlb, vma); mmu_notifier_invalidate_range_start(mm, start, end); +again: spin_lock(&mm->page_table_lock); for (address = start; address < end; address += sz) { ptep = huge_pte_offset(mm, address); @@ -2372,30 +2369,45 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, } pte = huge_ptep_get_and_clear(mm, address, ptep); + tlb_remove_tlb_entry(tlb, ptep, address); if (pte_dirty(pte)) set_page_dirty(page); - list_add(&page->lru, &page_list); + page_remove_rmap(page); + force_flush = !__tlb_remove_page(tlb, page); + if (force_flush) + break; /* Bail out after unmapping reference page if supplied */ if (ref_page) break; } - flush_tlb_range(vma, start, end); spin_unlock(&mm->page_table_lock); - mmu_notifier_invalidate_range_end(mm, start, end); - list_for_each_entry_safe(page, tmp, &page_list, lru) { - page_remove_rmap(page); - list_del(&page->lru); - put_page(page); + /* + * mmu_gather ran out of room to batch pages, we break out of + * the PTE lock to avoid doing the potential expensive TLB invalidate + * and page-free while holding it. + */ + if (force_flush) { + force_flush = 0; + tlb_flush_mmu(tlb); + if (address < end && !ref_page) + goto again; } + mmu_notifier_invalidate_range_end(mm, start, end); + tlb_end_vma(tlb, vma); } void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, unsigned long end, struct page *ref_page) { - mutex_lock(&vma->vm_file->f_mapping->i_mmap_mutex); - __unmap_hugepage_range(vma, start, end, ref_page); - mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex); + struct mm_struct *mm; + struct mmu_gather tlb; + + mm = vma->vm_mm; + + tlb_gather_mmu(&tlb, mm, 0); + __unmap_hugepage_range(&tlb, vma, start, end, ref_page); + tlb_finish_mmu(&tlb, start, end); } /* @@ -2440,9 +2452,8 @@ static int unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma, * from the time of fork. This would look like data corruption */ if (!is_vma_resv_set(iter_vma, HPAGE_RESV_OWNER)) - __unmap_hugepage_range(iter_vma, - address, address + huge_page_size(h), - page); + unmap_hugepage_range(iter_vma, address, + address + huge_page_size(h), page); } mutex_unlock(&mapping->i_mmap_mutex); diff --git a/mm/memory.c b/mm/memory.c index 91f69459d3e8..59e5bebc2e35 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1343,8 +1343,11 @@ static void unmap_single_vma(struct mmu_gather *tlb, * Since no pte has actually been setup, it is * safe to do nothing in this case. */ - if (vma->vm_file) - unmap_hugepage_range(vma, start, end, NULL); + if (vma->vm_file) { + mutex_lock(&vma->vm_file->f_mapping->i_mmap_mutex); + __unmap_hugepage_range(tlb, vma, start, end, NULL); + mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex); + } } else unmap_page_range(tlb, vma, start, end, details); } -- cgit v1.2.3 From 189ebff2894a9d0f4e250dd1e154d282ef0a6779 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:06 -0700 Subject: hugetlb: simplify migrate_huge_page() Since we migrate only one hugepage, don't use linked list for passing the page around. Directly pass the page that need to be migrated as argument. This also removes the usage of page->lru in the migrate path. Signed-off-by: Aneesh Kumar K.V Reviewed-by: KAMEZAWA Hiroyuki Cc: David Rientjes Cc: Hillf Danton Reviewed-by: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/migrate.h | 4 +-- mm/memory-failure.c | 15 +++--------- mm/migrate.c | 65 ++++++++++++++++--------------------------------- 3 files changed, 27 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/include/linux/migrate.h b/include/linux/migrate.h index 855c337b20c3..ce7e6671968b 100644 --- a/include/linux/migrate.h +++ b/include/linux/migrate.h @@ -15,7 +15,7 @@ extern int migrate_page(struct address_space *, extern int migrate_pages(struct list_head *l, new_page_t x, unsigned long private, bool offlining, enum migrate_mode mode); -extern int migrate_huge_pages(struct list_head *l, new_page_t x, +extern int migrate_huge_page(struct page *, new_page_t x, unsigned long private, bool offlining, enum migrate_mode mode); @@ -36,7 +36,7 @@ static inline void putback_lru_pages(struct list_head *l) {} static inline int migrate_pages(struct list_head *l, new_page_t x, unsigned long private, bool offlining, enum migrate_mode mode) { return -ENOSYS; } -static inline int migrate_huge_pages(struct list_head *l, new_page_t x, +static inline int migrate_huge_page(struct page *page, new_page_t x, unsigned long private, bool offlining, enum migrate_mode mode) { return -ENOSYS; } diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 6de0d613bbe6..b04ff2d6f73d 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -1416,7 +1416,6 @@ static int soft_offline_huge_page(struct page *page, int flags) int ret; unsigned long pfn = page_to_pfn(page); struct page *hpage = compound_head(page); - LIST_HEAD(pagelist); ret = get_any_page(page, pfn, flags); if (ret < 0) @@ -1431,24 +1430,18 @@ static int soft_offline_huge_page(struct page *page, int flags) } /* Keep page count to indicate a given hugepage is isolated. */ - - list_add(&hpage->lru, &pagelist); - ret = migrate_huge_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL, false, + ret = migrate_huge_page(hpage, new_page, MPOL_MF_MOVE_ALL, false, MIGRATE_SYNC); + put_page(hpage); if (ret) { - struct page *page1, *page2; - list_for_each_entry_safe(page1, page2, &pagelist, lru) - put_page(page1); - pr_info("soft offline: %#lx: migration failed %d, type %lx\n", pfn, ret, page->flags); - if (ret > 0) - ret = -EIO; return ret; } done: if (!PageHWPoison(hpage)) - atomic_long_add(1 << compound_trans_order(hpage), &mce_bad_pages); + atomic_long_add(1 << compound_trans_order(hpage), + &mce_bad_pages); set_page_hwpoison_huge_page(hpage); dequeue_hwpoisoned_huge_page(hpage); /* keep elevated page count for bad page */ diff --git a/mm/migrate.c b/mm/migrate.c index be26d5cbe56b..fdce3a29fc4c 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -932,15 +932,8 @@ static int unmap_and_move_huge_page(new_page_t get_new_page, if (anon_vma) put_anon_vma(anon_vma); unlock_page(hpage); - out: - if (rc != -EAGAIN) { - list_del(&hpage->lru); - put_page(hpage); - } - put_page(new_hpage); - if (result) { if (rc) *result = rc; @@ -1016,48 +1009,32 @@ out: return nr_failed + retry; } -int migrate_huge_pages(struct list_head *from, - new_page_t get_new_page, unsigned long private, bool offlining, - enum migrate_mode mode) +int migrate_huge_page(struct page *hpage, new_page_t get_new_page, + unsigned long private, bool offlining, + enum migrate_mode mode) { - int retry = 1; - int nr_failed = 0; - int pass = 0; - struct page *page; - struct page *page2; - int rc; - - for (pass = 0; pass < 10 && retry; pass++) { - retry = 0; - - list_for_each_entry_safe(page, page2, from, lru) { + int pass, rc; + + for (pass = 0; pass < 10; pass++) { + rc = unmap_and_move_huge_page(get_new_page, + private, hpage, pass > 2, offlining, + mode); + switch (rc) { + case -ENOMEM: + goto out; + case -EAGAIN: + /* try again */ cond_resched(); - - rc = unmap_and_move_huge_page(get_new_page, - private, page, pass > 2, offlining, - mode); - - switch(rc) { - case -ENOMEM: - goto out; - case -EAGAIN: - retry++; - break; - case 0: - break; - default: - /* Permanent failure */ - nr_failed++; - break; - } + break; + case 0: + goto out; + default: + rc = -EIO; + goto out; } } - rc = 0; out: - if (rc) - return rc; - - return nr_failed + retry; + return rc; } #ifdef CONFIG_NUMA -- cgit v1.2.3 From 0edaecfab218d747d30de4575e911907371e2cd2 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:07 -0700 Subject: hugetlb: add a list for tracking in-use HugeTLB pages hugepage_activelist will be used to track currently used HugeTLB pages. We need to find the in-use HugeTLB pages to support HugeTLB cgroup removal. On cgroup removal we update the page's HugeTLB cgroup to point to parent cgroup. Signed-off-by: Aneesh Kumar K.V Reviewed-by: KAMEZAWA Hiroyuki Cc: David Rientjes Cc: Hillf Danton Reviewed-by: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 1 + mm/hugetlb.c | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 0f23c1840c9b..ed550d819044 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -211,6 +211,7 @@ struct hstate { unsigned long resv_huge_pages; unsigned long surplus_huge_pages; unsigned long nr_overcommit_huge_pages; + struct list_head hugepage_activelist; struct list_head hugepage_freelists[MAX_NUMNODES]; unsigned int nr_huge_pages_node[MAX_NUMNODES]; unsigned int free_huge_pages_node[MAX_NUMNODES]; diff --git a/mm/hugetlb.c b/mm/hugetlb.c index e54b695336f9..b5b6e156ca76 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -510,7 +510,7 @@ void copy_huge_page(struct page *dst, struct page *src) static void enqueue_huge_page(struct hstate *h, struct page *page) { int nid = page_to_nid(page); - list_add(&page->lru, &h->hugepage_freelists[nid]); + list_move(&page->lru, &h->hugepage_freelists[nid]); h->free_huge_pages++; h->free_huge_pages_node[nid]++; } @@ -522,7 +522,7 @@ static struct page *dequeue_huge_page_node(struct hstate *h, int nid) if (list_empty(&h->hugepage_freelists[nid])) return NULL; page = list_entry(h->hugepage_freelists[nid].next, struct page, lru); - list_del(&page->lru); + list_move(&page->lru, &h->hugepage_activelist); set_page_refcounted(page); h->free_huge_pages--; h->free_huge_pages_node[nid]--; @@ -626,10 +626,11 @@ static void free_huge_page(struct page *page) page->mapping = NULL; BUG_ON(page_count(page)); BUG_ON(page_mapcount(page)); - INIT_LIST_HEAD(&page->lru); spin_lock(&hugetlb_lock); if (h->surplus_huge_pages_node[nid] && huge_page_order(h) < MAX_ORDER) { + /* remove the page from active list */ + list_del(&page->lru); update_and_free_page(h, page); h->surplus_huge_pages--; h->surplus_huge_pages_node[nid]--; @@ -642,6 +643,7 @@ static void free_huge_page(struct page *page) static void prep_new_huge_page(struct hstate *h, struct page *page, int nid) { + INIT_LIST_HEAD(&page->lru); set_compound_page_dtor(page, free_huge_page); spin_lock(&hugetlb_lock); h->nr_huge_pages++; @@ -890,6 +892,7 @@ static struct page *alloc_buddy_huge_page(struct hstate *h, int nid) spin_lock(&hugetlb_lock); if (page) { + INIT_LIST_HEAD(&page->lru); r_nid = page_to_nid(page); set_compound_page_dtor(page, free_huge_page); /* @@ -994,7 +997,6 @@ retry: list_for_each_entry_safe(page, tmp, &surplus_list, lru) { if ((--needed) < 0) break; - list_del(&page->lru); /* * This page is now managed by the hugetlb allocator and has * no users -- drop the buddy allocator's reference. @@ -1009,7 +1011,6 @@ free: /* Free unnecessary surplus pages to the buddy allocator */ if (!list_empty(&surplus_list)) { list_for_each_entry_safe(page, tmp, &surplus_list, lru) { - list_del(&page->lru); put_page(page); } } @@ -1909,6 +1910,7 @@ void __init hugetlb_add_hstate(unsigned order) h->free_huge_pages = 0; for (i = 0; i < MAX_NUMNODES; ++i) INIT_LIST_HEAD(&h->hugepage_freelists[i]); + INIT_LIST_HEAD(&h->hugepage_activelist); h->next_nid_to_alloc = first_node(node_states[N_HIGH_MEMORY]); h->next_nid_to_free = first_node(node_states[N_HIGH_MEMORY]); snprintf(h->name, HSTATE_NAME_LEN, "hugepages-%lukB", -- cgit v1.2.3 From c3f38a38715e9b4e7b1dda6840a1375f6894744d Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:10 -0700 Subject: hugetlb: make some static variables global We will use them later in hugetlb_cgroup.c Signed-off-by: Aneesh Kumar K.V Cc: David Rientjes Acked-by: KAMEZAWA Hiroyuki Cc: Hillf Danton Reviewed-by: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 5 +++++ mm/hugetlb.c | 7 ++----- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index ed550d819044..3d677dd41898 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -21,6 +21,11 @@ struct hugepage_subpool { long max_hpages, used_hpages; }; +extern spinlock_t hugetlb_lock; +extern int hugetlb_max_hstate __read_mostly; +#define for_each_hstate(h) \ + for ((h) = hstates; (h) < &hstates[hugetlb_max_hstate]; (h)++) + struct hugepage_subpool *hugepage_new_subpool(long nr_blocks); void hugepage_put_subpool(struct hugepage_subpool *spool); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index b5b6e156ca76..d5971597736b 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -35,7 +35,7 @@ const unsigned long hugetlb_zero = 0, hugetlb_infinity = ~0UL; static gfp_t htlb_alloc_mask = GFP_HIGHUSER; unsigned long hugepages_treat_as_movable; -static int hugetlb_max_hstate; +int hugetlb_max_hstate __read_mostly; unsigned int default_hstate_idx; struct hstate hstates[HUGE_MAX_HSTATE]; @@ -46,13 +46,10 @@ static struct hstate * __initdata parsed_hstate; static unsigned long __initdata default_hstate_max_huge_pages; static unsigned long __initdata default_hstate_size; -#define for_each_hstate(h) \ - for ((h) = hstates; (h) < &hstates[hugetlb_max_hstate]; (h)++) - /* * Protects updates to hugepage_freelists, nr_huge_pages, and free_huge_pages */ -static DEFINE_SPINLOCK(hugetlb_lock); +DEFINE_SPINLOCK(hugetlb_lock); static inline void unlock_or_release_subpool(struct hugepage_subpool *spool) { -- cgit v1.2.3 From 2bc64a2046975410505bb119bba32705892b9255 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:12 -0700 Subject: mm/hugetlb: add new HugeTLB cgroup Implement a new controller that allows us to control HugeTLB allocations. The extension allows to limit the HugeTLB usage per control group and enforces the controller limit during page fault. Since HugeTLB doesn't support page reclaim, enforcing the limit at page fault time implies that, the application will get SIGBUS signal if it tries to access HugeTLB pages beyond its limit. This requires the application to know beforehand how much HugeTLB pages it would require for its use. The charge/uncharge calls will be added to HugeTLB code in later patch. Support for cgroup removal will be added in later patches. [akpm@linux-foundation.org: s/CONFIG_CGROUP_HUGETLB_RES_CTLR/CONFIG_MEMCG_HUGETLB/g] [akpm@linux-foundation.org: s/CONFIG_MEMCG_HUGETLB/CONFIG_CGROUP_HUGETLB/g] Reviewed-by: KAMEZAWA Hiroyuki Signed-off-by: Aneesh Kumar K.V Cc: David Rientjes Cc: Hillf Danton Reviewed-by: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/cgroup_subsys.h | 6 +++ include/linux/hugetlb_cgroup.h | 37 +++++++++++++ init/Kconfig | 15 ++++++ mm/Makefile | 1 + mm/hugetlb_cgroup.c | 120 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 179 insertions(+) create mode 100644 include/linux/hugetlb_cgroup.h create mode 100644 mm/hugetlb_cgroup.c (limited to 'include') diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h index 0bd390ce98b2..5b41ce079024 100644 --- a/include/linux/cgroup_subsys.h +++ b/include/linux/cgroup_subsys.h @@ -72,3 +72,9 @@ SUBSYS(net_prio) #endif /* */ + +#ifdef CONFIG_CGROUP_HUGETLB +SUBSYS(hugetlb) +#endif + +/* */ diff --git a/include/linux/hugetlb_cgroup.h b/include/linux/hugetlb_cgroup.h new file mode 100644 index 000000000000..f19889e56b47 --- /dev/null +++ b/include/linux/hugetlb_cgroup.h @@ -0,0 +1,37 @@ +/* + * Copyright IBM Corporation, 2012 + * Author Aneesh Kumar K.V + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef _LINUX_HUGETLB_CGROUP_H +#define _LINUX_HUGETLB_CGROUP_H + +#include + +struct hugetlb_cgroup; + +#ifdef CONFIG_CGROUP_HUGETLB +static inline bool hugetlb_cgroup_disabled(void) +{ + if (hugetlb_subsys.disabled) + return true; + return false; +} + +#else +static inline bool hugetlb_cgroup_disabled(void) +{ + return true; +} + +#endif /* CONFIG_MEM_RES_CTLR_HUGETLB */ +#endif diff --git a/init/Kconfig b/init/Kconfig index b3f55f15e107..72437760e90e 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -751,6 +751,21 @@ config CGROUP_MEM_RES_CTLR_KMEM the kmem extension can use it to guarantee that no group of processes will ever exhaust kernel resources alone. +config CGROUP_HUGETLB + bool "HugeTLB Resource Controller for Control Groups" + depends on RESOURCE_COUNTERS && HUGETLB_PAGE && EXPERIMENTAL + default n + help + Provides a cgroup Resource Controller for HugeTLB pages. + When you enable this, you can put a per cgroup limit on HugeTLB usage. + The limit is enforced during page fault. Since HugeTLB doesn't + support page reclaim, enforcing the limit at page fault time implies + that, the application will get SIGBUS signal if it tries to access + HugeTLB pages beyond its limit. This requires the application to know + beforehand how much HugeTLB pages it would require for its use. The + control group is tracked in the third page lru pointer. This means + that we cannot use the controller with huge page less than 3 pages. + config CGROUP_PERF bool "Enable perf_event per-cpu per-container group (cgroup) monitoring" depends on PERF_EVENTS && CGROUPS diff --git a/mm/Makefile b/mm/Makefile index 8e81fe263c94..fd6fc1c1966c 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_MIGRATION) += migrate.o obj-$(CONFIG_QUICKLIST) += quicklist.o obj-$(CONFIG_TRANSPARENT_HUGEPAGE) += huge_memory.o obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o page_cgroup.o +obj-$(CONFIG_CGROUP_HUGETLB) += hugetlb_cgroup.o obj-$(CONFIG_MEMORY_FAILURE) += memory-failure.o obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c new file mode 100644 index 000000000000..0d1a66e9039b --- /dev/null +++ b/mm/hugetlb_cgroup.c @@ -0,0 +1,120 @@ +/* + * + * Copyright IBM Corporation, 2012 + * Author Aneesh Kumar K.V + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#include +#include +#include +#include + +struct hugetlb_cgroup { + struct cgroup_subsys_state css; + /* + * the counter to account for hugepages from hugetlb. + */ + struct res_counter hugepage[HUGE_MAX_HSTATE]; +}; + +struct cgroup_subsys hugetlb_subsys __read_mostly; +static struct hugetlb_cgroup *root_h_cgroup __read_mostly; + +static inline +struct hugetlb_cgroup *hugetlb_cgroup_from_css(struct cgroup_subsys_state *s) +{ + return container_of(s, struct hugetlb_cgroup, css); +} + +static inline +struct hugetlb_cgroup *hugetlb_cgroup_from_cgroup(struct cgroup *cgroup) +{ + return hugetlb_cgroup_from_css(cgroup_subsys_state(cgroup, + hugetlb_subsys_id)); +} + +static inline +struct hugetlb_cgroup *hugetlb_cgroup_from_task(struct task_struct *task) +{ + return hugetlb_cgroup_from_css(task_subsys_state(task, + hugetlb_subsys_id)); +} + +static inline bool hugetlb_cgroup_is_root(struct hugetlb_cgroup *h_cg) +{ + return (h_cg == root_h_cgroup); +} + +static inline struct hugetlb_cgroup *parent_hugetlb_cgroup(struct cgroup *cg) +{ + if (!cg->parent) + return NULL; + return hugetlb_cgroup_from_cgroup(cg->parent); +} + +static inline bool hugetlb_cgroup_have_usage(struct cgroup *cg) +{ + int idx; + struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cg); + + for (idx = 0; idx < hugetlb_max_hstate; idx++) { + if ((res_counter_read_u64(&h_cg->hugepage[idx], RES_USAGE)) > 0) + return true; + } + return false; +} + +static struct cgroup_subsys_state *hugetlb_cgroup_create(struct cgroup *cgroup) +{ + int idx; + struct cgroup *parent_cgroup; + struct hugetlb_cgroup *h_cgroup, *parent_h_cgroup; + + h_cgroup = kzalloc(sizeof(*h_cgroup), GFP_KERNEL); + if (!h_cgroup) + return ERR_PTR(-ENOMEM); + + parent_cgroup = cgroup->parent; + if (parent_cgroup) { + parent_h_cgroup = hugetlb_cgroup_from_cgroup(parent_cgroup); + for (idx = 0; idx < HUGE_MAX_HSTATE; idx++) + res_counter_init(&h_cgroup->hugepage[idx], + &parent_h_cgroup->hugepage[idx]); + } else { + root_h_cgroup = h_cgroup; + for (idx = 0; idx < HUGE_MAX_HSTATE; idx++) + res_counter_init(&h_cgroup->hugepage[idx], NULL); + } + return &h_cgroup->css; +} + +static void hugetlb_cgroup_destroy(struct cgroup *cgroup) +{ + struct hugetlb_cgroup *h_cgroup; + + h_cgroup = hugetlb_cgroup_from_cgroup(cgroup); + kfree(h_cgroup); +} + +static int hugetlb_cgroup_pre_destroy(struct cgroup *cgroup) +{ + /* We will add the cgroup removal support in later patches */ + return -EBUSY; +} + +struct cgroup_subsys hugetlb_subsys = { + .name = "hugetlb", + .create = hugetlb_cgroup_create, + .pre_destroy = hugetlb_cgroup_pre_destroy, + .destroy = hugetlb_cgroup_destroy, + .subsys_id = hugetlb_subsys_id, +}; -- cgit v1.2.3 From 9dd540e23111d8884773ab942a736f3aba4040d4 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:15 -0700 Subject: hugetlb/cgroup: add the cgroup pointer to page lru Add the hugetlb cgroup pointer to 3rd page lru.next. This limit the usage to hugetlb cgroup to only hugepages with 3 or more normal pages. I guess that is an acceptable limitation. Signed-off-by: Aneesh Kumar K.V Cc: David Rientjes Acked-by: KAMEZAWA Hiroyuki Cc: Hillf Danton Reviewed-by: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb_cgroup.h | 37 +++++++++++++++++++++++++++++++++++++ mm/hugetlb.c | 4 ++++ 2 files changed, 41 insertions(+) (limited to 'include') diff --git a/include/linux/hugetlb_cgroup.h b/include/linux/hugetlb_cgroup.h index f19889e56b47..e5451a3b4ebc 100644 --- a/include/linux/hugetlb_cgroup.h +++ b/include/linux/hugetlb_cgroup.h @@ -18,8 +18,34 @@ #include struct hugetlb_cgroup; +/* + * Minimum page order trackable by hugetlb cgroup. + * At least 3 pages are necessary for all the tracking information. + */ +#define HUGETLB_CGROUP_MIN_ORDER 2 #ifdef CONFIG_CGROUP_HUGETLB + +static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page) +{ + VM_BUG_ON(!PageHuge(page)); + + if (compound_order(page) < HUGETLB_CGROUP_MIN_ORDER) + return NULL; + return (struct hugetlb_cgroup *)page[2].lru.next; +} + +static inline +int set_hugetlb_cgroup(struct page *page, struct hugetlb_cgroup *h_cg) +{ + VM_BUG_ON(!PageHuge(page)); + + if (compound_order(page) < HUGETLB_CGROUP_MIN_ORDER) + return -1; + page[2].lru.next = (void *)h_cg; + return 0; +} + static inline bool hugetlb_cgroup_disabled(void) { if (hugetlb_subsys.disabled) @@ -28,6 +54,17 @@ static inline bool hugetlb_cgroup_disabled(void) } #else +static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page) +{ + return NULL; +} + +static inline +int set_hugetlb_cgroup(struct page *page, struct hugetlb_cgroup *h_cg) +{ + return 0; +} + static inline bool hugetlb_cgroup_disabled(void) { return true; diff --git a/mm/hugetlb.c b/mm/hugetlb.c index d5971597736b..efe29b53daff 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -28,6 +28,7 @@ #include #include +#include #include #include "internal.h" @@ -591,6 +592,7 @@ static void update_and_free_page(struct hstate *h, struct page *page) 1 << PG_active | 1 << PG_reserved | 1 << PG_private | 1 << PG_writeback); } + VM_BUG_ON(hugetlb_cgroup_from_page(page)); set_compound_page_dtor(page, NULL); set_page_refcounted(page); arch_release_hugepage(page); @@ -643,6 +645,7 @@ static void prep_new_huge_page(struct hstate *h, struct page *page, int nid) INIT_LIST_HEAD(&page->lru); set_compound_page_dtor(page, free_huge_page); spin_lock(&hugetlb_lock); + set_hugetlb_cgroup(page, NULL); h->nr_huge_pages++; h->nr_huge_pages_node[nid]++; spin_unlock(&hugetlb_lock); @@ -892,6 +895,7 @@ static struct page *alloc_buddy_huge_page(struct hstate *h, int nid) INIT_LIST_HEAD(&page->lru); r_nid = page_to_nid(page); set_compound_page_dtor(page, free_huge_page); + set_hugetlb_cgroup(page, NULL); /* * We incremented the global counters already */ -- cgit v1.2.3 From 6d76dcf40405144a448040a350fd214ddc243d5e Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:18 -0700 Subject: hugetlb/cgroup: add charge/uncharge routines for hugetlb cgroup Add the charge and uncharge routines for hugetlb cgroup. We do cgroup charging in page alloc and uncharge in compound page destructor. Assigning page's hugetlb cgroup is protected by hugetlb_lock. [liwp@linux.vnet.ibm.com: add huge_page_order check to avoid incorrect uncharge] Signed-off-by: Aneesh Kumar K.V Cc: David Rientjes Acked-by: KAMEZAWA Hiroyuki Cc: Hillf Danton Cc: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Aneesh Kumar K.V Signed-off-by: Wanpeng Li Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb_cgroup.h | 38 ++++++++++++++++++++ mm/hugetlb.c | 16 ++++++++- mm/hugetlb_cgroup.c | 80 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/hugetlb_cgroup.h b/include/linux/hugetlb_cgroup.h index e5451a3b4ebc..7d3fde996be3 100644 --- a/include/linux/hugetlb_cgroup.h +++ b/include/linux/hugetlb_cgroup.h @@ -53,6 +53,16 @@ static inline bool hugetlb_cgroup_disabled(void) return false; } +extern int hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages, + struct hugetlb_cgroup **ptr); +extern void hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages, + struct hugetlb_cgroup *h_cg, + struct page *page); +extern void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, + struct page *page); +extern void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, + struct hugetlb_cgroup *h_cg); + #else static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page) { @@ -70,5 +80,33 @@ static inline bool hugetlb_cgroup_disabled(void) return true; } +static inline int +hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages, + struct hugetlb_cgroup **ptr) +{ + return 0; +} + +static inline void +hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages, + struct hugetlb_cgroup *h_cg, + struct page *page) +{ + return; +} + +static inline void +hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, struct page *page) +{ + return; +} + +static inline void +hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, + struct hugetlb_cgroup *h_cg) +{ + return; +} + #endif /* CONFIG_MEM_RES_CTLR_HUGETLB */ #endif diff --git a/mm/hugetlb.c b/mm/hugetlb.c index efe29b53daff..16a0f32c4820 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -627,6 +627,8 @@ static void free_huge_page(struct page *page) BUG_ON(page_mapcount(page)); spin_lock(&hugetlb_lock); + hugetlb_cgroup_uncharge_page(hstate_index(h), + pages_per_huge_page(h), page); if (h->surplus_huge_pages_node[nid] && huge_page_order(h) < MAX_ORDER) { /* remove the page from active list */ list_del(&page->lru); @@ -1115,7 +1117,10 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, struct hstate *h = hstate_vma(vma); struct page *page; long chg; + int ret, idx; + struct hugetlb_cgroup *h_cg; + idx = hstate_index(h); /* * Processes that did not create the mapping will have no * reserves and will not have accounted against subpool @@ -1131,6 +1136,11 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, if (hugepage_subpool_get_pages(spool, chg)) return ERR_PTR(-ENOSPC); + ret = hugetlb_cgroup_charge_cgroup(idx, pages_per_huge_page(h), &h_cg); + if (ret) { + hugepage_subpool_put_pages(spool, chg); + return ERR_PTR(-ENOSPC); + } spin_lock(&hugetlb_lock); page = dequeue_huge_page_vma(h, vma, addr, avoid_reserve); spin_unlock(&hugetlb_lock); @@ -1138,6 +1148,9 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, if (!page) { page = alloc_buddy_huge_page(h, NUMA_NO_NODE); if (!page) { + hugetlb_cgroup_uncharge_cgroup(idx, + pages_per_huge_page(h), + h_cg); hugepage_subpool_put_pages(spool, chg); return ERR_PTR(-ENOSPC); } @@ -1146,7 +1159,8 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma, set_page_private(page, (unsigned long)spool); vma_commit_reservation(h, vma, addr); - + /* update page cgroup details */ + hugetlb_cgroup_commit_charge(idx, pages_per_huge_page(h), h_cg, page); return page; } diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index 0d1a66e9039b..63e04cfa437d 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -111,6 +111,86 @@ static int hugetlb_cgroup_pre_destroy(struct cgroup *cgroup) return -EBUSY; } +int hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages, + struct hugetlb_cgroup **ptr) +{ + int ret = 0; + struct res_counter *fail_res; + struct hugetlb_cgroup *h_cg = NULL; + unsigned long csize = nr_pages * PAGE_SIZE; + + if (hugetlb_cgroup_disabled()) + goto done; + /* + * We don't charge any cgroup if the compound page have less + * than 3 pages. + */ + if (huge_page_order(&hstates[idx]) < HUGETLB_CGROUP_MIN_ORDER) + goto done; +again: + rcu_read_lock(); + h_cg = hugetlb_cgroup_from_task(current); + if (!css_tryget(&h_cg->css)) { + rcu_read_unlock(); + goto again; + } + rcu_read_unlock(); + + ret = res_counter_charge(&h_cg->hugepage[idx], csize, &fail_res); + css_put(&h_cg->css); +done: + *ptr = h_cg; + return ret; +} + +void hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages, + struct hugetlb_cgroup *h_cg, + struct page *page) +{ + if (hugetlb_cgroup_disabled() || !h_cg) + return; + + spin_lock(&hugetlb_lock); + set_hugetlb_cgroup(page, h_cg); + spin_unlock(&hugetlb_lock); + return; +} + +/* + * Should be called with hugetlb_lock held + */ +void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, + struct page *page) +{ + struct hugetlb_cgroup *h_cg; + unsigned long csize = nr_pages * PAGE_SIZE; + + if (hugetlb_cgroup_disabled()) + return; + VM_BUG_ON(!spin_is_locked(&hugetlb_lock)); + h_cg = hugetlb_cgroup_from_page(page); + if (unlikely(!h_cg)) + return; + set_hugetlb_cgroup(page, NULL); + res_counter_uncharge(&h_cg->hugepage[idx], csize); + return; +} + +void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, + struct hugetlb_cgroup *h_cg) +{ + unsigned long csize = nr_pages * PAGE_SIZE; + + if (hugetlb_cgroup_disabled() || !h_cg) + return; + + if (huge_page_order(&hstates[idx]) < HUGETLB_CGROUP_MIN_ORDER) + return; + + res_counter_uncharge(&h_cg->hugepage[idx], csize); + return; +} + struct cgroup_subsys hugetlb_subsys = { .name = "hugetlb", .create = hugetlb_cgroup_create, -- cgit v1.2.3 From abb8206cb07734d0b7bf033c715995d6371a94c3 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:24 -0700 Subject: hugetlb/cgroup: add hugetlb cgroup control files Add the control files for hugetlb controller [akpm@linux-foundation.org: s/CONFIG_CGROUP_HUGETLB_RES_CTLR/CONFIG_MEMCG_HUGETLB/g] [akpm@linux-foundation.org: s/CONFIG_MEMCG_HUGETLB/CONFIG_CGROUP_HUGETLB/] Signed-off-by: Aneesh Kumar K.V Cc: David Rientjes Acked-by: KAMEZAWA Hiroyuki Cc: Hillf Danton Reviewed-by: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 5 ++ include/linux/hugetlb_cgroup.h | 6 ++ mm/hugetlb.c | 8 +++ mm/hugetlb_cgroup.c | 129 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+) (limited to 'include') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 3d677dd41898..f9db20bfa9fc 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -4,6 +4,7 @@ #include #include #include +#include struct ctl_table; struct user_struct; @@ -221,6 +222,10 @@ struct hstate { unsigned int nr_huge_pages_node[MAX_NUMNODES]; unsigned int free_huge_pages_node[MAX_NUMNODES]; unsigned int surplus_huge_pages_node[MAX_NUMNODES]; +#ifdef CONFIG_CGROUP_HUGETLB + /* cgroup control files */ + struct cftype cgroup_files[5]; +#endif char name[HSTATE_NAME_LEN]; }; diff --git a/include/linux/hugetlb_cgroup.h b/include/linux/hugetlb_cgroup.h index 7d3fde996be3..73f1e600fc12 100644 --- a/include/linux/hugetlb_cgroup.h +++ b/include/linux/hugetlb_cgroup.h @@ -62,6 +62,7 @@ extern void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, struct page *page); extern void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, struct hugetlb_cgroup *h_cg); +extern int hugetlb_cgroup_file_init(int idx) __init; #else static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page) @@ -108,5 +109,10 @@ hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, return; } +static inline int __init hugetlb_cgroup_file_init(int idx) +{ + return 0; +} + #endif /* CONFIG_MEM_RES_CTLR_HUGETLB */ #endif diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 16a0f32c4820..c57740bb203a 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "internal.h" const unsigned long hugetlb_zero = 0, hugetlb_infinity = ~0UL; @@ -1930,6 +1931,13 @@ void __init hugetlb_add_hstate(unsigned order) h->next_nid_to_free = first_node(node_states[N_HIGH_MEMORY]); snprintf(h->name, HSTATE_NAME_LEN, "hugepages-%lukB", huge_page_size(h)/1024); + /* + * Add cgroup control files only if the huge page consists + * of more than two normal pages. This is because we use + * page[2].lru.next for storing cgoup details. + */ + if (order >= HUGETLB_CGROUP_MIN_ORDER) + hugetlb_cgroup_file_init(hugetlb_max_hstate - 1); parsed_hstate = h; } diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index bc518bedea98..d1ca1196e62f 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -26,6 +26,10 @@ struct hugetlb_cgroup { struct res_counter hugepage[HUGE_MAX_HSTATE]; }; +#define MEMFILE_PRIVATE(x, val) (((x) << 16) | (val)) +#define MEMFILE_IDX(val) (((val) >> 16) & 0xffff) +#define MEMFILE_ATTR(val) ((val) & 0xffff) + struct cgroup_subsys hugetlb_subsys __read_mostly; static struct hugetlb_cgroup *root_h_cgroup __read_mostly; @@ -257,6 +261,131 @@ void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, return; } +static ssize_t hugetlb_cgroup_read(struct cgroup *cgroup, struct cftype *cft, + struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + u64 val; + char str[64]; + int idx, name, len; + struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup); + + idx = MEMFILE_IDX(cft->private); + name = MEMFILE_ATTR(cft->private); + + val = res_counter_read_u64(&h_cg->hugepage[idx], name); + len = scnprintf(str, sizeof(str), "%llu\n", (unsigned long long)val); + return simple_read_from_buffer(buf, nbytes, ppos, str, len); +} + +static int hugetlb_cgroup_write(struct cgroup *cgroup, struct cftype *cft, + const char *buffer) +{ + int idx, name, ret; + unsigned long long val; + struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup); + + idx = MEMFILE_IDX(cft->private); + name = MEMFILE_ATTR(cft->private); + + switch (name) { + case RES_LIMIT: + if (hugetlb_cgroup_is_root(h_cg)) { + /* Can't set limit on root */ + ret = -EINVAL; + break; + } + /* This function does all necessary parse...reuse it */ + ret = res_counter_memparse_write_strategy(buffer, &val); + if (ret) + break; + ret = res_counter_set_limit(&h_cg->hugepage[idx], val); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int hugetlb_cgroup_reset(struct cgroup *cgroup, unsigned int event) +{ + int idx, name, ret = 0; + struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup); + + idx = MEMFILE_IDX(event); + name = MEMFILE_ATTR(event); + + switch (name) { + case RES_MAX_USAGE: + res_counter_reset_max(&h_cg->hugepage[idx]); + break; + case RES_FAILCNT: + res_counter_reset_failcnt(&h_cg->hugepage[idx]); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static char *mem_fmt(char *buf, int size, unsigned long hsize) +{ + if (hsize >= (1UL << 30)) + snprintf(buf, size, "%luGB", hsize >> 30); + else if (hsize >= (1UL << 20)) + snprintf(buf, size, "%luMB", hsize >> 20); + else + snprintf(buf, size, "%luKB", hsize >> 10); + return buf; +} + +int __init hugetlb_cgroup_file_init(int idx) +{ + char buf[32]; + struct cftype *cft; + struct hstate *h = &hstates[idx]; + + /* format the size */ + mem_fmt(buf, 32, huge_page_size(h)); + + /* Add the limit file */ + cft = &h->cgroup_files[0]; + snprintf(cft->name, MAX_CFTYPE_NAME, "%s.limit_in_bytes", buf); + cft->private = MEMFILE_PRIVATE(idx, RES_LIMIT); + cft->read = hugetlb_cgroup_read; + cft->write_string = hugetlb_cgroup_write; + + /* Add the usage file */ + cft = &h->cgroup_files[1]; + snprintf(cft->name, MAX_CFTYPE_NAME, "%s.usage_in_bytes", buf); + cft->private = MEMFILE_PRIVATE(idx, RES_USAGE); + cft->read = hugetlb_cgroup_read; + + /* Add the MAX usage file */ + cft = &h->cgroup_files[2]; + snprintf(cft->name, MAX_CFTYPE_NAME, "%s.max_usage_in_bytes", buf); + cft->private = MEMFILE_PRIVATE(idx, RES_MAX_USAGE); + cft->trigger = hugetlb_cgroup_reset; + cft->read = hugetlb_cgroup_read; + + /* Add the failcntfile */ + cft = &h->cgroup_files[3]; + snprintf(cft->name, MAX_CFTYPE_NAME, "%s.failcnt", buf); + cft->private = MEMFILE_PRIVATE(idx, RES_FAILCNT); + cft->trigger = hugetlb_cgroup_reset; + cft->read = hugetlb_cgroup_read; + + /* NULL terminate the last cft */ + cft = &h->cgroup_files[4]; + memset(cft, 0, sizeof(*cft)); + + WARN_ON(cgroup_add_cftypes(&hugetlb_subsys, h->cgroup_files)); + + return 0; +} + struct cgroup_subsys hugetlb_subsys = { .name = "hugetlb", .create = hugetlb_cgroup_create, -- cgit v1.2.3 From 8e6ac7fab374816de9a8b0a8fbb02ef761a30ff4 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 31 Jul 2012 16:42:27 -0700 Subject: hugetlb/cgroup: migrate hugetlb cgroup info from oldpage to new page during migration With HugeTLB pages, hugetlb cgroup is uncharged in compound page destructor. Since we are holding a hugepage reference, we can be sure that old page won't get uncharged till the last put_page(). Signed-off-by: Aneesh Kumar K.V Cc: David Rientjes Acked-by: KAMEZAWA Hiroyuki Cc: Hillf Danton Cc: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb_cgroup.h | 8 ++++++++ mm/hugetlb_cgroup.c | 20 ++++++++++++++++++++ mm/migrate.c | 5 +++++ 3 files changed, 33 insertions(+) (limited to 'include') diff --git a/include/linux/hugetlb_cgroup.h b/include/linux/hugetlb_cgroup.h index 73f1e600fc12..d73878c694b3 100644 --- a/include/linux/hugetlb_cgroup.h +++ b/include/linux/hugetlb_cgroup.h @@ -63,6 +63,8 @@ extern void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, extern void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, struct hugetlb_cgroup *h_cg); extern int hugetlb_cgroup_file_init(int idx) __init; +extern void hugetlb_cgroup_migrate(struct page *oldhpage, + struct page *newhpage); #else static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page) @@ -114,5 +116,11 @@ static inline int __init hugetlb_cgroup_file_init(int idx) return 0; } +static inline void hugetlb_cgroup_migrate(struct page *oldhpage, + struct page *newhpage) +{ + return; +} + #endif /* CONFIG_MEM_RES_CTLR_HUGETLB */ #endif diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index d1ca1196e62f..680e4819e077 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -386,6 +386,26 @@ int __init hugetlb_cgroup_file_init(int idx) return 0; } +void hugetlb_cgroup_migrate(struct page *oldhpage, struct page *newhpage) +{ + struct hugetlb_cgroup *h_cg; + + if (hugetlb_cgroup_disabled()) + return; + + VM_BUG_ON(!PageHuge(oldhpage)); + spin_lock(&hugetlb_lock); + h_cg = hugetlb_cgroup_from_page(oldhpage); + set_hugetlb_cgroup(oldhpage, NULL); + cgroup_exclude_rmdir(&h_cg->css); + + /* move the h_cg details to new cgroup */ + set_hugetlb_cgroup(newhpage, h_cg); + spin_unlock(&hugetlb_lock); + cgroup_release_and_wakeup_rmdir(&h_cg->css); + return; +} + struct cgroup_subsys hugetlb_subsys = { .name = "hugetlb", .create = hugetlb_cgroup_create, diff --git a/mm/migrate.c b/mm/migrate.c index fdce3a29fc4c..6c37c51565e5 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -931,6 +932,10 @@ static int unmap_and_move_huge_page(new_page_t get_new_page, if (anon_vma) put_anon_vma(anon_vma); + + if (!rc) + hugetlb_cgroup_migrate(hpage, new_hpage); + unlock_page(hpage); out: put_page(new_hpage); -- cgit v1.2.3 From c59e26104e3e0e952cd7d63e79cd71ee5a9ec25a Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Tue, 31 Jul 2012 16:42:49 -0700 Subject: mm/compaction: cleanup on compaction_deferred When CONFIG_COMPACTION is enabled, compaction_deferred() tries to recalculate the deferred limit again, which isn't necessary. When CONFIG_COMPACTION is disabled, compaction_deferred() should return "true" or "false" since it has "bool" for its return value. Signed-off-by: Gavin Shan Acked-by: Minchan Kim Acked-by: Johannes Weiner Acked-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/compaction.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/compaction.h b/include/linux/compaction.h index 51a90b7f2d60..133ddcf83397 100644 --- a/include/linux/compaction.h +++ b/include/linux/compaction.h @@ -58,7 +58,7 @@ static inline bool compaction_deferred(struct zone *zone, int order) if (++zone->compact_considered > defer_limit) zone->compact_considered = defer_limit; - return zone->compact_considered < (1UL << zone->compact_defer_shift); + return zone->compact_considered < defer_limit; } #else @@ -85,7 +85,7 @@ static inline void defer_compaction(struct zone *zone, int order) static inline bool compaction_deferred(struct zone *zone, int order) { - return 1; + return true; } #endif /* CONFIG_COMPACTION */ -- cgit v1.2.3 From c255a458055e459f65eb7b7f51dc5dbdd0caf1d8 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 31 Jul 2012 16:43:02 -0700 Subject: memcg: rename config variables Sanity: CONFIG_CGROUP_MEM_RES_CTLR -> CONFIG_MEMCG CONFIG_CGROUP_MEM_RES_CTLR_SWAP -> CONFIG_MEMCG_SWAP CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED -> CONFIG_MEMCG_SWAP_ENABLED CONFIG_CGROUP_MEM_RES_CTLR_KMEM -> CONFIG_MEMCG_KMEM [mhocko@suse.cz: fix missed bits] Cc: Glauber Costa Acked-by: Michal Hocko Cc: Johannes Weiner Cc: KAMEZAWA Hiroyuki Cc: Hugh Dickins Cc: Tejun Heo Cc: Aneesh Kumar K.V Cc: David Rientjes Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/cgroups/memory.txt | 10 +++++----- arch/powerpc/configs/chroma_defconfig | 4 ++-- arch/s390/defconfig | 2 +- arch/sh/configs/apsh4ad0a_defconfig | 2 +- arch/sh/configs/sdk7786_defconfig | 4 ++-- arch/sh/configs/se7206_defconfig | 2 +- arch/sh/configs/shx3_defconfig | 2 +- arch/sh/configs/urquell_defconfig | 4 ++-- arch/tile/configs/tilegx_defconfig | 4 ++-- arch/tile/configs/tilepro_defconfig | 4 ++-- arch/um/defconfig | 8 ++++---- include/linux/cgroup_subsys.h | 2 +- include/linux/memcontrol.h | 14 +++++++------- include/linux/mmzone.h | 8 ++++---- include/linux/page_cgroup.h | 10 +++++----- include/linux/sched.h | 2 +- include/linux/swap.h | 6 +++--- include/net/sock.h | 4 ++-- init/Kconfig | 14 +++++++------- kernel/fork.c | 2 +- mm/Makefile | 2 +- mm/hwpoison-inject.c | 2 +- mm/memcontrol.c | 20 ++++++++++---------- mm/memory-failure.c | 2 +- mm/mmzone.c | 2 +- mm/oom_kill.c | 2 +- mm/page_cgroup.c | 2 +- mm/vmscan.c | 4 ++-- net/core/sock.c | 2 +- net/ipv4/Makefile | 2 +- net/ipv4/sysctl_net_ipv4.c | 4 ++-- net/ipv4/tcp_ipv4.c | 2 +- net/ipv6/tcp_ipv6.c | 2 +- 33 files changed, 78 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt index dd88540bb995..672676ac9615 100644 --- a/Documentation/cgroups/memory.txt +++ b/Documentation/cgroups/memory.txt @@ -187,12 +187,12 @@ the cgroup that brought it in -- this will happen on memory pressure). But see section 8.2: when moving a task to another cgroup, its pages may be recharged to the new cgroup, if move_charge_at_immigrate has been chosen. -Exception: If CONFIG_CGROUP_CGROUP_MEM_RES_CTLR_SWAP is not used. +Exception: If CONFIG_CGROUP_CGROUP_MEMCG_SWAP is not used. When you do swapoff and make swapped-out pages of shmem(tmpfs) to be backed into memory in force, charges for pages are accounted against the caller of swapoff rather than the users of shmem. -2.4 Swap Extension (CONFIG_CGROUP_MEM_RES_CTLR_SWAP) +2.4 Swap Extension (CONFIG_MEMCG_SWAP) Swap Extension allows you to record charge for swap. A swapped-in page is charged back to original page allocator if possible. @@ -259,7 +259,7 @@ When oom event notifier is registered, event will be delivered. per-zone-per-cgroup LRU (cgroup's private LRU) is just guarded by zone->lru_lock, it has no lock of its own. -2.7 Kernel Memory Extension (CONFIG_CGROUP_MEM_RES_CTLR_KMEM) +2.7 Kernel Memory Extension (CONFIG_MEMCG_KMEM) With the Kernel memory extension, the Memory Controller is able to limit the amount of kernel memory used by the system. Kernel memory is fundamentally @@ -286,8 +286,8 @@ per cgroup, instead of globally. a. Enable CONFIG_CGROUPS b. Enable CONFIG_RESOURCE_COUNTERS -c. Enable CONFIG_CGROUP_MEM_RES_CTLR -d. Enable CONFIG_CGROUP_MEM_RES_CTLR_SWAP (to use swap extension) +c. Enable CONFIG_MEMCG +d. Enable CONFIG_MEMCG_SWAP (to use swap extension) 1. Prepare the cgroups (see cgroups.txt, Why are cgroups needed?) # mount -t tmpfs none /sys/fs/cgroup diff --git a/arch/powerpc/configs/chroma_defconfig b/arch/powerpc/configs/chroma_defconfig index b1f9597fe312..29bb11ec6c64 100644 --- a/arch/powerpc/configs/chroma_defconfig +++ b/arch/powerpc/configs/chroma_defconfig @@ -21,8 +21,8 @@ CONFIG_CGROUP_DEVICE=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y -CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEMCG_SWAP=y CONFIG_NAMESPACES=y CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y diff --git a/arch/s390/defconfig b/arch/s390/defconfig index 37d2bf267964..de57702a3f44 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig @@ -13,7 +13,7 @@ CONFIG_CGROUPS=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEMCG=y CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y diff --git a/arch/sh/configs/apsh4ad0a_defconfig b/arch/sh/configs/apsh4ad0a_defconfig index e7583484cc07..95ae23fcfdd6 100644 --- a/arch/sh/configs/apsh4ad0a_defconfig +++ b/arch/sh/configs/apsh4ad0a_defconfig @@ -11,7 +11,7 @@ CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEMCG=y CONFIG_BLK_CGROUP=y CONFIG_NAMESPACES=y CONFIG_BLK_DEV_INITRD=y diff --git a/arch/sh/configs/sdk7786_defconfig b/arch/sh/configs/sdk7786_defconfig index 8a7dd7b59c5c..76a76a295d74 100644 --- a/arch/sh/configs/sdk7786_defconfig +++ b/arch/sh/configs/sdk7786_defconfig @@ -18,8 +18,8 @@ CONFIG_CPUSETS=y # CONFIG_PROC_PID_CPUSET is not set CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y -CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEMCG_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_CGROUP=y diff --git a/arch/sh/configs/se7206_defconfig b/arch/sh/configs/se7206_defconfig index 72c3fad7383f..6bc30ab9fd18 100644 --- a/arch/sh/configs/se7206_defconfig +++ b/arch/sh/configs/se7206_defconfig @@ -11,7 +11,7 @@ CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEMCG=y CONFIG_RELAY=y CONFIG_NAMESPACES=y CONFIG_UTS_NS=y diff --git a/arch/sh/configs/shx3_defconfig b/arch/sh/configs/shx3_defconfig index 6bb413036892..cd6c519f8fad 100644 --- a/arch/sh/configs/shx3_defconfig +++ b/arch/sh/configs/shx3_defconfig @@ -13,7 +13,7 @@ CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEMCG=y CONFIG_RELAY=y CONFIG_NAMESPACES=y CONFIG_UTS_NS=y diff --git a/arch/sh/configs/urquell_defconfig b/arch/sh/configs/urquell_defconfig index 8bfa4d056d7a..d7f89be9f474 100644 --- a/arch/sh/configs/urquell_defconfig +++ b/arch/sh/configs/urquell_defconfig @@ -15,8 +15,8 @@ CONFIG_CPUSETS=y # CONFIG_PROC_PID_CPUSET is not set CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y -CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEMCG_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_DEV_INITRD=y diff --git a/arch/tile/configs/tilegx_defconfig b/arch/tile/configs/tilegx_defconfig index b8d99aca5431..0270620a1692 100644 --- a/arch/tile/configs/tilegx_defconfig +++ b/arch/tile/configs/tilegx_defconfig @@ -18,8 +18,8 @@ CONFIG_CGROUP_DEVICE=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y -CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEMCG_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_CGROUP=y diff --git a/arch/tile/configs/tilepro_defconfig b/arch/tile/configs/tilepro_defconfig index 2b1fd31894f1..c11de27a9bcb 100644 --- a/arch/tile/configs/tilepro_defconfig +++ b/arch/tile/configs/tilepro_defconfig @@ -17,8 +17,8 @@ CONFIG_CGROUP_DEVICE=y CONFIG_CPUSETS=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y -CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEMCG_SWAP=y CONFIG_CGROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_CGROUP=y diff --git a/arch/um/defconfig b/arch/um/defconfig index 7823ab12e6a4..fec0d5d27460 100644 --- a/arch/um/defconfig +++ b/arch/um/defconfig @@ -155,10 +155,10 @@ CONFIG_CPUSETS=y CONFIG_PROC_PID_CPUSET=y CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -CONFIG_CGROUP_MEM_RES_CTLR=y -CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y -# CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED is not set -# CONFIG_CGROUP_MEM_RES_CTLR_KMEM is not set +CONFIG_CGROUP_MEMCG=y +CONFIG_CGROUP_MEMCG_SWAP=y +# CONFIG_CGROUP_MEMCG_SWAP_ENABLED is not set +# CONFIG_CGROUP_MEMCG_KMEM is not set CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y # CONFIG_CFS_BANDWIDTH is not set diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h index 5b41ce079024..dfae957398c3 100644 --- a/include/linux/cgroup_subsys.h +++ b/include/linux/cgroup_subsys.h @@ -31,7 +31,7 @@ SUBSYS(cpuacct) /* */ -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG SUBSYS(mem_cgroup) #endif diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 83e7ba90d6e5..170076222431 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -38,7 +38,7 @@ struct mem_cgroup_reclaim_cookie { unsigned int generation; }; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG /* * All "charge" functions with gfp_mask should use GFP_KERNEL or * (gfp_mask & GFP_RECLAIM_MASK). In current implementatin, memcg doesn't @@ -124,7 +124,7 @@ extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, extern void mem_cgroup_replace_page_cache(struct page *oldpage, struct page *newpage); -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP extern int do_swap_account; #endif @@ -193,7 +193,7 @@ void mem_cgroup_split_huge_fixup(struct page *head); bool mem_cgroup_bad_page_check(struct page *page); void mem_cgroup_print_bad_page(struct page *page); #endif -#else /* CONFIG_CGROUP_MEM_RES_CTLR */ +#else /* CONFIG_MEMCG */ struct mem_cgroup; static inline int mem_cgroup_newpage_charge(struct page *page, @@ -384,9 +384,9 @@ static inline void mem_cgroup_replace_page_cache(struct page *oldpage, struct page *newpage) { } -#endif /* CONFIG_CGROUP_MEM_RES_CTLR */ +#endif /* CONFIG_MEMCG */ -#if !defined(CONFIG_CGROUP_MEM_RES_CTLR) || !defined(CONFIG_DEBUG_VM) +#if !defined(CONFIG_MEMCG) || !defined(CONFIG_DEBUG_VM) static inline bool mem_cgroup_bad_page_check(struct page *page) { @@ -406,7 +406,7 @@ enum { }; struct sock; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM void sock_update_memcg(struct sock *sk); void sock_release_memcg(struct sock *sk); #else @@ -416,6 +416,6 @@ static inline void sock_update_memcg(struct sock *sk) static inline void sock_release_memcg(struct sock *sk) { } -#endif /* CONFIG_CGROUP_MEM_RES_CTLR_KMEM */ +#endif /* CONFIG_MEMCG_KMEM */ #endif /* _LINUX_MEMCONTROL_H */ diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 458988bd55a1..3bdfa15b2012 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -201,7 +201,7 @@ struct zone_reclaim_stat { struct lruvec { struct list_head lists[NR_LRU_LISTS]; struct zone_reclaim_stat reclaim_stat; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG struct zone *zone; #endif }; @@ -671,7 +671,7 @@ typedef struct pglist_data { int nr_zones; #ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */ struct page *node_mem_map; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG struct page_cgroup *node_page_cgroup; #endif #endif @@ -736,7 +736,7 @@ extern void lruvec_init(struct lruvec *lruvec, struct zone *zone); static inline struct zone *lruvec_zone(struct lruvec *lruvec) { -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG return lruvec->zone; #else return container_of(lruvec, struct zone, lruvec); @@ -1052,7 +1052,7 @@ struct mem_section { /* See declaration of similar field in struct zone */ unsigned long *pageblock_flags; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG /* * If !SPARSEMEM, pgdat doesn't have page_cgroup pointer. We use * section. (see memcontrol.h/page_cgroup.h about this.) diff --git a/include/linux/page_cgroup.h b/include/linux/page_cgroup.h index a88cdba27809..777a524716db 100644 --- a/include/linux/page_cgroup.h +++ b/include/linux/page_cgroup.h @@ -12,7 +12,7 @@ enum { #ifndef __GENERATING_BOUNDS_H #include -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG #include /* @@ -82,7 +82,7 @@ static inline void unlock_page_cgroup(struct page_cgroup *pc) bit_spin_unlock(PCG_LOCK, &pc->flags); } -#else /* CONFIG_CGROUP_MEM_RES_CTLR */ +#else /* CONFIG_MEMCG */ struct page_cgroup; static inline void __meminit pgdat_page_cgroup_init(struct pglist_data *pgdat) @@ -102,11 +102,11 @@ static inline void __init page_cgroup_init_flatmem(void) { } -#endif /* CONFIG_CGROUP_MEM_RES_CTLR */ +#endif /* CONFIG_MEMCG */ #include -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP extern unsigned short swap_cgroup_cmpxchg(swp_entry_t ent, unsigned short old, unsigned short new); extern unsigned short swap_cgroup_record(swp_entry_t ent, unsigned short id); @@ -138,7 +138,7 @@ static inline void swap_cgroup_swapoff(int type) return; } -#endif /* CONFIG_CGROUP_MEM_RES_CTLR_SWAP */ +#endif /* CONFIG_MEMCG_SWAP */ #endif /* !__GENERATING_BOUNDS_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 68dcffaa62a0..865725adb9d3 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1584,7 +1584,7 @@ struct task_struct { /* bitmask and counter of trace recursion */ unsigned long trace_recursion; #endif /* CONFIG_TRACING */ -#ifdef CONFIG_CGROUP_MEM_RES_CTLR /* memcg uses this to do batch job */ +#ifdef CONFIG_MEMCG /* memcg uses this to do batch job */ struct memcg_batch_info { int do_batch; /* incremented when batch uncharge started */ struct mem_cgroup *memcg; /* target memcg of uncharge */ diff --git a/include/linux/swap.h b/include/linux/swap.h index c84ec68eaec9..9a16bb1cefd1 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -301,7 +301,7 @@ static inline void scan_unevictable_unregister_node(struct node *node) extern int kswapd_run(int nid); extern void kswapd_stop(int nid); -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG extern int mem_cgroup_swappiness(struct mem_cgroup *mem); #else static inline int mem_cgroup_swappiness(struct mem_cgroup *mem) @@ -309,7 +309,7 @@ static inline int mem_cgroup_swappiness(struct mem_cgroup *mem) return vm_swappiness; } #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP extern void mem_cgroup_uncharge_swap(swp_entry_t ent); #else static inline void mem_cgroup_uncharge_swap(swp_entry_t ent) @@ -360,7 +360,7 @@ extern int reuse_swap_page(struct page *); extern int try_to_free_swap(struct page *); struct backing_dev_info; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG extern void mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout); #else diff --git a/include/net/sock.h b/include/net/sock.h index e067f8c18f88..cee528c119ca 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -913,7 +913,7 @@ struct proto { #ifdef SOCK_REFCNT_DEBUG atomic_t socks; #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM /* * cgroup specific init/deinit functions. Called once for all * protocols that implement it, from cgroups populate function. @@ -994,7 +994,7 @@ inline void sk_refcnt_debug_release(const struct sock *sk) #define sk_refcnt_debug_release(sk) do { } while (0) #endif /* SOCK_REFCNT_DEBUG */ -#if defined(CONFIG_CGROUP_MEM_RES_CTLR_KMEM) && defined(CONFIG_NET) +#if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_NET) extern struct static_key memcg_socket_limit_enabled; static inline struct cg_proto *parent_cg_proto(struct proto *proto, struct cg_proto *cg_proto) diff --git a/init/Kconfig b/init/Kconfig index 72437760e90e..af6c7f8ba019 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -686,7 +686,7 @@ config RESOURCE_COUNTERS This option enables controller independent resource accounting infrastructure that works with cgroups. -config CGROUP_MEM_RES_CTLR +config MEMCG bool "Memory Resource Controller for Control Groups" depends on RESOURCE_COUNTERS select MM_OWNER @@ -709,9 +709,9 @@ config CGROUP_MEM_RES_CTLR This config option also selects MM_OWNER config option, which could in turn add some fork/exit overhead. -config CGROUP_MEM_RES_CTLR_SWAP +config MEMCG_SWAP bool "Memory Resource Controller Swap Extension" - depends on CGROUP_MEM_RES_CTLR && SWAP + depends on MEMCG && SWAP help Add swap management feature to memory resource controller. When you enable this, you can limit mem+swap usage per cgroup. In other words, @@ -726,9 +726,9 @@ config CGROUP_MEM_RES_CTLR_SWAP if boot option "swapaccount=0" is set, swap will not be accounted. Now, memory usage of swap_cgroup is 2 bytes per entry. If swap page size is 4096bytes, 512k per 1Gbytes of swap. -config CGROUP_MEM_RES_CTLR_SWAP_ENABLED +config MEMCG_SWAP_ENABLED bool "Memory Resource Controller Swap Extension enabled by default" - depends on CGROUP_MEM_RES_CTLR_SWAP + depends on MEMCG_SWAP default y help Memory Resource Controller Swap Extension comes with its price in @@ -739,9 +739,9 @@ config CGROUP_MEM_RES_CTLR_SWAP_ENABLED For those who want to have the feature enabled by default should select this option (if, for some reason, they need to disable it then swapaccount=0 does the trick). -config CGROUP_MEM_RES_CTLR_KMEM +config MEMCG_KMEM bool "Memory Resource Controller Kernel Memory accounting (EXPERIMENTAL)" - depends on CGROUP_MEM_RES_CTLR && EXPERIMENTAL + depends on MEMCG && EXPERIMENTAL default n help The Kernel Memory extension for Memory Resource Controller can limit diff --git a/kernel/fork.c b/kernel/fork.c index aaa8813c45d1..3bd2280d79f6 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1306,7 +1306,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, #ifdef CONFIG_DEBUG_MUTEXES p->blocked_on = NULL; /* not blocked yet */ #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG p->memcg_batch.do_batch = 0; p->memcg_batch.memcg = NULL; #endif diff --git a/mm/Makefile b/mm/Makefile index fd6fc1c1966c..290bbfe33698 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -49,7 +49,7 @@ obj-$(CONFIG_FS_XIP) += filemap_xip.o obj-$(CONFIG_MIGRATION) += migrate.o obj-$(CONFIG_QUICKLIST) += quicklist.o obj-$(CONFIG_TRANSPARENT_HUGEPAGE) += huge_memory.o -obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o page_cgroup.o +obj-$(CONFIG_MEMCG) += memcontrol.o page_cgroup.o obj-$(CONFIG_CGROUP_HUGETLB) += hugetlb_cgroup.o obj-$(CONFIG_MEMORY_FAILURE) += memory-failure.o obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o diff --git a/mm/hwpoison-inject.c b/mm/hwpoison-inject.c index cc448bb983ba..3a61efc518d5 100644 --- a/mm/hwpoison-inject.c +++ b/mm/hwpoison-inject.c @@ -123,7 +123,7 @@ static int pfn_inject_init(void) if (!dentry) goto fail; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP dentry = debugfs_create_u64("corrupt-filter-memcg", 0600, hwpoison_dir, &hwpoison_filter_memcg); if (!dentry) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index a2677e0a6387..55a85e1a342f 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -61,12 +61,12 @@ struct cgroup_subsys mem_cgroup_subsys __read_mostly; #define MEM_CGROUP_RECLAIM_RETRIES 5 static struct mem_cgroup *root_mem_cgroup __read_mostly; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP /* Turned on only when memory cgroup is enabled && really_do_swap_account = 1 */ int do_swap_account __read_mostly; /* for remember boot option*/ -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED +#ifdef CONFIG_MEMCG_SWAP_ENABLED static int really_do_swap_account __initdata = 1; #else static int really_do_swap_account __initdata = 0; @@ -407,7 +407,7 @@ static void mem_cgroup_get(struct mem_cgroup *memcg); static void mem_cgroup_put(struct mem_cgroup *memcg); /* Writing them here to avoid exposing memcg's inner layout */ -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM #include #include @@ -466,9 +466,9 @@ struct cg_proto *tcp_proto_cgroup(struct mem_cgroup *memcg) } EXPORT_SYMBOL(tcp_proto_cgroup); #endif /* CONFIG_INET */ -#endif /* CONFIG_CGROUP_MEM_RES_CTLR_KMEM */ +#endif /* CONFIG_MEMCG_KMEM */ -#if defined(CONFIG_INET) && defined(CONFIG_CGROUP_MEM_RES_CTLR_KMEM) +#if defined(CONFIG_INET) && defined(CONFIG_MEMCG_KMEM) static void disarm_sock_keys(struct mem_cgroup *memcg) { if (!memcg_proto_activated(&memcg->tcp_mem.cg_proto)) @@ -3085,7 +3085,7 @@ mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout) } #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP /* * called from swap_entry_free(). remove record in swap_cgroup and * uncharge "memsw" account. @@ -4518,7 +4518,7 @@ static int mem_cgroup_oom_control_write(struct cgroup *cgrp, return 0; } -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM static int memcg_init_kmem(struct mem_cgroup *memcg, struct cgroup_subsys *ss) { return mem_cgroup_sockets_init(memcg, ss); @@ -4608,7 +4608,7 @@ static struct cftype mem_cgroup_files[] = { .read_seq_string = mem_control_numa_stat_show, }, #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP { .name = "memsw.usage_in_bytes", .private = MEMFILE_PRIVATE(_MEMSWAP, RES_USAGE), @@ -4795,7 +4795,7 @@ struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *memcg) } EXPORT_SYMBOL(parent_mem_cgroup); -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP static void __init enable_swap_cgroup(void) { if (!mem_cgroup_disabled() && really_do_swap_account) @@ -5526,7 +5526,7 @@ struct cgroup_subsys mem_cgroup_subsys = { .__DEPRECATED_clear_css_refs = true, }; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP static int __init enable_swap_account(char *s) { /* consider enabled if no parameter or 1 is given */ diff --git a/mm/memory-failure.c b/mm/memory-failure.c index b04ff2d6f73d..a6e2141a6610 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -128,7 +128,7 @@ static int hwpoison_filter_flags(struct page *p) * can only guarantee that the page either belongs to the memcg tasks, or is * a freed page. */ -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP u64 hwpoison_filter_memcg; EXPORT_SYMBOL_GPL(hwpoison_filter_memcg); static int hwpoison_filter_task(struct page *p) diff --git a/mm/mmzone.c b/mm/mmzone.c index 6830eab5bf09..3cef80f6ac79 100644 --- a/mm/mmzone.c +++ b/mm/mmzone.c @@ -96,7 +96,7 @@ void lruvec_init(struct lruvec *lruvec, struct zone *zone) for_each_lru(lru) INIT_LIST_HEAD(&lruvec->lists[lru]); -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG lruvec->zone = zone; #endif } diff --git a/mm/oom_kill.c b/mm/oom_kill.c index c82ede69bf3f..e6c10640e56b 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -541,7 +541,7 @@ static void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, sysctl_panic_on_oom == 2 ? "compulsory" : "system-wide"); } -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, int order) { diff --git a/mm/page_cgroup.c b/mm/page_cgroup.c index eb750f851395..5ddad0c6daa6 100644 --- a/mm/page_cgroup.c +++ b/mm/page_cgroup.c @@ -317,7 +317,7 @@ void __meminit pgdat_page_cgroup_init(struct pglist_data *pgdat) #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP +#ifdef CONFIG_MEMCG_SWAP static DEFINE_MUTEX(swap_cgroup_mutex); struct swap_cgroup_ctrl { diff --git a/mm/vmscan.c b/mm/vmscan.c index 347b3ff2a478..6b1f89a91212 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -133,7 +133,7 @@ long vm_total_pages; /* The total number of pages which the VM controls */ static LIST_HEAD(shrinker_list); static DECLARE_RWSEM(shrinker_rwsem); -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG static bool global_reclaim(struct scan_control *sc) { return !sc->target_mem_cgroup; @@ -2142,7 +2142,7 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order, return nr_reclaimed; } -#ifdef CONFIG_CGROUP_MEM_RES_CTLR +#ifdef CONFIG_MEMCG unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *memcg, gfp_t gfp_mask, bool noswap, diff --git a/net/core/sock.c b/net/core/sock.c index 2676a88f533e..a67b06280e4c 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -142,7 +142,7 @@ static DEFINE_MUTEX(proto_list_mutex); static LIST_HEAD(proto_list); -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM int mem_cgroup_sockets_init(struct mem_cgroup *memcg, struct cgroup_subsys *ss) { struct proto *proto; diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index ae2ccf2890e4..15ca63ec604e 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -49,7 +49,7 @@ obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o -obj-$(CONFIG_CGROUP_MEM_RES_CTLR_KMEM) += tcp_memcontrol.o +obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o obj-$(CONFIG_NETLABEL) += cipso_ipv4.o obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 5840c3255721..ed7db3f1b6f2 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -184,7 +184,7 @@ static int ipv4_tcp_mem(ctl_table *ctl, int write, int ret; unsigned long vec[3]; struct net *net = current->nsproxy->net_ns; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM struct mem_cgroup *memcg; #endif @@ -203,7 +203,7 @@ static int ipv4_tcp_mem(ctl_table *ctl, int write, if (ret) return ret; -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM rcu_read_lock(); memcg = mem_cgroup_from_task(current); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 2fbd9921253f..4bc8f6769b57 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2633,7 +2633,7 @@ struct proto tcp_prot = { .compat_setsockopt = compat_tcp_setsockopt, .compat_getsockopt = compat_tcp_getsockopt, #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM .init_cgroup = tcp_init_cgroup, .destroy_cgroup = tcp_destroy_cgroup, .proto_cgroup = tcp_proto_cgroup, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 221224e72507..61c7b6d83176 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -2015,7 +2015,7 @@ struct proto tcpv6_prot = { .compat_setsockopt = compat_tcp_setsockopt, .compat_getsockopt = compat_tcp_getsockopt, #endif -#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM +#ifdef CONFIG_MEMCG_KMEM .proto_cgroup = tcp_proto_cgroup, #endif }; -- cgit v1.2.3 From ca28ddc908fcfef0e5c1b6e5df632db7fc26de10 Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Tue, 31 Jul 2012 16:43:04 -0700 Subject: mm: remove unused LRU_ALL_EVICTABLE Signed-off-by: Wanpeng Li Acked-by: KAMEZAWA Hiroyuki Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 3bdfa15b2012..1495d952e641 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -209,7 +209,6 @@ struct lruvec { /* Mask used at gathering information at once (see memcontrol.c) */ #define LRU_ALL_FILE (BIT(LRU_INACTIVE_FILE) | BIT(LRU_ACTIVE_FILE)) #define LRU_ALL_ANON (BIT(LRU_INACTIVE_ANON) | BIT(LRU_ACTIVE_ANON)) -#define LRU_ALL_EVICTABLE (LRU_ALL_FILE | LRU_ALL_ANON) #define LRU_ALL ((1 << NR_LRU_LISTS) - 1) /* Isolate clean file */ -- cgit v1.2.3 From 7db8889ab05b57200158432755af318fb68854a2 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Tue, 31 Jul 2012 16:43:12 -0700 Subject: mm: have order > 0 compaction start off where it left Order > 0 compaction stops when enough free pages of the correct page order have been coalesced. When doing subsequent higher order allocations, it is possible for compaction to be invoked many times. However, the compaction code always starts out looking for things to compact at the start of the zone, and for free pages to compact things to at the end of the zone. This can cause quadratic behaviour, with isolate_freepages starting at the end of the zone each time, even though previous invocations of the compaction code already filled up all free memory on that end of the zone. This can cause isolate_freepages to take enormous amounts of CPU with certain workloads on larger memory systems. The obvious solution is to have isolate_freepages remember where it left off last time, and continue at that point the next time it gets invoked for an order > 0 compaction. This could cause compaction to fail if cc->free_pfn and cc->migrate_pfn are close together initially, in that case we restart from the end of the zone and try once more. Forced full (order == -1) compactions are left alone. [akpm@linux-foundation.org: checkpatch fixes] [akpm@linux-foundation.org: s/laste/last/, use 80 cols] Signed-off-by: Rik van Riel Reported-by: Jim Schutt Tested-by: Jim Schutt Cc: Minchan Kim Reviewed-by: KAMEZAWA Hiroyuki Acked-by: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 4 ++++ mm/compaction.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++---- mm/internal.h | 6 +++++ mm/page_alloc.c | 5 ++++ 4 files changed, 73 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 1495d952e641..1aeadce4d56e 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -368,6 +368,10 @@ struct zone { */ spinlock_t lock; int all_unreclaimable; /* All pages pinned */ +#if defined CONFIG_COMPACTION || defined CONFIG_CMA + /* pfn where the last incremental compaction isolated free pages */ + unsigned long compact_cached_free_pfn; +#endif #ifdef CONFIG_MEMORY_HOTPLUG /* see spanned/present_pages for more description */ seqlock_t span_seqlock; diff --git a/mm/compaction.c b/mm/compaction.c index 2f42d9528539..e78cb9688421 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -422,6 +422,17 @@ static void isolate_freepages(struct zone *zone, pfn -= pageblock_nr_pages) { unsigned long isolated; + /* + * Skip ahead if another thread is compacting in the area + * simultaneously. If we wrapped around, we can only skip + * ahead if zone->compact_cached_free_pfn also wrapped to + * above our starting point. + */ + if (cc->order > 0 && (!cc->wrapped || + zone->compact_cached_free_pfn > + cc->start_free_pfn)) + pfn = min(pfn, zone->compact_cached_free_pfn); + if (!pfn_valid(pfn)) continue; @@ -461,8 +472,11 @@ static void isolate_freepages(struct zone *zone, * looking for free pages, the search will restart here as * page migration may have returned some pages to the allocator */ - if (isolated) + if (isolated) { high_pfn = max(high_pfn, pfn); + if (cc->order > 0) + zone->compact_cached_free_pfn = high_pfn; + } } /* split_free_page does not map the pages */ @@ -556,6 +570,20 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone, return ISOLATE_SUCCESS; } +/* + * Returns the start pfn of the last page block in a zone. This is the starting + * point for full compaction of a zone. Compaction searches for free pages from + * the end of each zone, while isolate_freepages_block scans forward inside each + * page block. + */ +static unsigned long start_free_pfn(struct zone *zone) +{ + unsigned long free_pfn; + free_pfn = zone->zone_start_pfn + zone->spanned_pages; + free_pfn &= ~(pageblock_nr_pages-1); + return free_pfn; +} + static int compact_finished(struct zone *zone, struct compact_control *cc) { @@ -565,8 +593,26 @@ static int compact_finished(struct zone *zone, if (fatal_signal_pending(current)) return COMPACT_PARTIAL; - /* Compaction run completes if the migrate and free scanner meet */ - if (cc->free_pfn <= cc->migrate_pfn) + /* + * A full (order == -1) compaction run starts at the beginning and + * end of a zone; it completes when the migrate and free scanner meet. + * A partial (order > 0) compaction can start with the free scanner + * at a random point in the zone, and may have to restart. + */ + if (cc->free_pfn <= cc->migrate_pfn) { + if (cc->order > 0 && !cc->wrapped) { + /* We started partway through; restart at the end. */ + unsigned long free_pfn = start_free_pfn(zone); + zone->compact_cached_free_pfn = free_pfn; + cc->free_pfn = free_pfn; + cc->wrapped = 1; + return COMPACT_CONTINUE; + } + return COMPACT_COMPLETE; + } + + /* We wrapped around and ended up where we started. */ + if (cc->wrapped && cc->free_pfn <= cc->start_free_pfn) return COMPACT_COMPLETE; /* @@ -664,8 +710,15 @@ static int compact_zone(struct zone *zone, struct compact_control *cc) /* Setup to move all movable pages to the end of the zone */ cc->migrate_pfn = zone->zone_start_pfn; - cc->free_pfn = cc->migrate_pfn + zone->spanned_pages; - cc->free_pfn &= ~(pageblock_nr_pages-1); + + if (cc->order > 0) { + /* Incremental compaction. Start where the last one stopped. */ + cc->free_pfn = zone->compact_cached_free_pfn; + cc->start_free_pfn = cc->free_pfn; + } else { + /* Order == -1 starts at the end of the zone. */ + cc->free_pfn = start_free_pfn(zone); + } migrate_prep_local(); diff --git a/mm/internal.h b/mm/internal.h index 2ba87fbfb75b..da6b9b2ed3fc 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -118,8 +118,14 @@ struct compact_control { unsigned long nr_freepages; /* Number of isolated free pages */ unsigned long nr_migratepages; /* Number of pages to migrate */ unsigned long free_pfn; /* isolate_freepages search base */ + unsigned long start_free_pfn; /* where we started the search */ unsigned long migrate_pfn; /* isolate_migratepages search base */ bool sync; /* Synchronous migration */ + bool wrapped; /* Order > 0 compactions are + incremental, once free_pfn + and migrate_pfn meet, we restart + from the top of the zone; + remember we wrapped around. */ int order; /* order a direct compactor needs */ int migratetype; /* MOVABLE, RECLAIMABLE etc */ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index fba2a1223f14..94fc475c3f94 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4397,6 +4397,11 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat, zone->spanned_pages = size; zone->present_pages = realsize; +#if defined CONFIG_COMPACTION || defined CONFIG_CMA + zone->compact_cached_free_pfn = zone->zone_start_pfn + + zone->spanned_pages; + zone->compact_cached_free_pfn &= ~(pageblock_nr_pages-1); +#endif #ifdef CONFIG_NUMA zone->node = nid; zone->min_unmapped_pages = (realsize*sysctl_min_unmapped_ratio) -- cgit v1.2.3 From fe03025db3f4ade1f231b174938e0fe224722759 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Tue, 31 Jul 2012 16:43:14 -0700 Subject: mm: CONFIG_HAVE_MEMBLOCK_NODE -> CONFIG_HAVE_MEMBLOCK_NODE_MAP 0ee332c14518699 ("memblock: Kill early_node_map[]") wanted to replace CONFIG_ARCH_POPULATES_NODE_MAP with CONFIG_HAVE_MEMBLOCK_NODE_MAP but ended up replacing one occurence with a reference to the non-existent symbol CONFIG_HAVE_MEMBLOCK_NODE. The resulting omission of code would probably have been causing problems to 32-bit machines with memory hotplug. Signed-off-by: Rabin Vincent Cc: Tejun Heo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 1aeadce4d56e..f64afa5929fe 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -776,7 +776,7 @@ extern int movable_zone; static inline int zone_movable_is_highmem(void) { -#if defined(CONFIG_HIGHMEM) && defined(CONFIG_HAVE_MEMBLOCK_NODE) +#if defined(CONFIG_HIGHMEM) && defined(CONFIG_HAVE_MEMBLOCK_NODE_MAP) return movable_zone == ZONE_HIGHMEM; #else return 0; -- cgit v1.2.3 From 8e125cd85517c9716695b0abfabc0a4a3fcb94f3 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Tue, 31 Jul 2012 16:43:16 -0700 Subject: vmscan: remove obsolete shrink_control comment 09f363c7 ("vmscan: fix shrinker callback bug in fs/super.c") fixed a shrinker callback which was returning -1 when nr_to_scan is zero, which caused excessive slab scanning. But 635697c6 ("vmscan: fix initial shrinker size handling") fixed the problem, again so we can freely return -1 although nr_to_scan is zero. So let's revert 09f363c7 because the comment added in 09f363c7 made an unnecessary rule. Signed-off-by: Minchan Kim Cc: Al Viro Cc: Mikulas Patocka Cc: Konstantin Khlebnikov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/super.c | 2 +- include/linux/shrinker.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/fs/super.c b/fs/super.c index 4c5d82f56ec4..4bf714459a4b 100644 --- a/fs/super.c +++ b/fs/super.c @@ -62,7 +62,7 @@ static int prune_super(struct shrinker *shrink, struct shrink_control *sc) return -1; if (!grab_super_passive(sb)) - return !sc->nr_to_scan ? 0 : -1; + return -1; if (sb->s_op && sb->s_op->nr_cached_objects) fs_objects = sb->s_op->nr_cached_objects(sb); diff --git a/include/linux/shrinker.h b/include/linux/shrinker.h index 07ceb97d53fa..ac6b8ee07825 100644 --- a/include/linux/shrinker.h +++ b/include/linux/shrinker.h @@ -20,7 +20,6 @@ struct shrink_control { * 'nr_to_scan' entries and attempt to free them up. It should return * the number of objects which remain in the cache. If it returns -1, it means * it cannot do any scanning at this time (eg. there is a risk of deadlock). - * The callback must not return -1 if nr_to_scan is zero. * * The 'gfpmask' refers to the allocation we are currently trying to * fulfil. -- cgit v1.2.3 From 9adb62a5df9c0fbef7b4665919329f73a34651ed Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Tue, 31 Jul 2012 16:43:28 -0700 Subject: mm/hotplug: correctly setup fallback zonelists when creating new pgdat When hotadd_new_pgdat() is called to create new pgdat for a new node, a fallback zonelist should be created for the new node. There's code to try to achieve that in hotadd_new_pgdat() as below: /* * The node we allocated has no zone fallback lists. For avoiding * to access not-initialized zonelist, build here. */ mutex_lock(&zonelists_mutex); build_all_zonelists(pgdat, NULL); mutex_unlock(&zonelists_mutex); But it doesn't work as expected. When hotadd_new_pgdat() is called, the new node is still in offline state because node_set_online(nid) hasn't been called yet. And build_all_zonelists() only builds zonelists for online nodes as: for_each_online_node(nid) { pg_data_t *pgdat = NODE_DATA(nid); build_zonelists(pgdat); build_zonelist_cache(pgdat); } Though we hope to create zonelist for the new pgdat, but it doesn't. So add a new parameter "pgdat" the build_all_zonelists() to build pgdat for the new pgdat too. Signed-off-by: Jiang Liu Signed-off-by: Xishi Qiu Cc: Mel Gorman Cc: Michal Hocko Cc: Minchan Kim Cc: Rusty Russell Cc: Yinghai Lu Cc: Tony Luck Cc: KAMEZAWA Hiroyuki Cc: KOSAKI Motohiro Cc: David Rientjes Cc: Keping Chen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 2 +- init/main.c | 2 +- kernel/cpu.c | 2 +- mm/memory_hotplug.c | 4 ++-- mm/page_alloc.c | 17 ++++++++++++----- 5 files changed, 17 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index f64afa5929fe..98f079bcf399 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -721,7 +721,7 @@ typedef struct pglist_data { #include extern struct mutex zonelists_mutex; -void build_all_zonelists(void *data); +void build_all_zonelists(pg_data_t *pgdat, struct zone *zone); void wakeup_kswapd(struct zone *zone, int order, enum zone_type classzone_idx); bool zone_watermark_ok(struct zone *z, int order, unsigned long mark, int classzone_idx, int alloc_flags); diff --git a/init/main.c b/init/main.c index 95316a1b4a76..e60679de61c3 100644 --- a/init/main.c +++ b/init/main.c @@ -506,7 +506,7 @@ asmlinkage void __init start_kernel(void) setup_per_cpu_areas(); smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */ - build_all_zonelists(NULL); + build_all_zonelists(NULL, NULL); page_alloc_init(); printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line); diff --git a/kernel/cpu.c b/kernel/cpu.c index a4eb5227a19e..14d32588cccd 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -416,7 +416,7 @@ int __cpuinit cpu_up(unsigned int cpu) if (pgdat->node_zonelists->_zonerefs->zone == NULL) { mutex_lock(&zonelists_mutex); - build_all_zonelists(NULL); + build_all_zonelists(NULL, NULL); mutex_unlock(&zonelists_mutex); } #endif diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 427bb291dd0f..b8731040b9f9 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -513,7 +513,7 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages) zone->present_pages += onlined_pages; zone->zone_pgdat->node_present_pages += onlined_pages; if (need_zonelists_rebuild) - build_all_zonelists(zone); + build_all_zonelists(NULL, zone); else zone_pcp_update(zone); @@ -562,7 +562,7 @@ static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start) * to access not-initialized zonelist, build here. */ mutex_lock(&zonelists_mutex); - build_all_zonelists(NULL); + build_all_zonelists(pgdat, NULL); mutex_unlock(&zonelists_mutex); return pgdat; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 6c7e3bd93a85..9ad6866ac49c 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3032,7 +3032,7 @@ int numa_zonelist_order_handler(ctl_table *table, int write, user_zonelist_order = oldval; } else if (oldval != user_zonelist_order) { mutex_lock(&zonelists_mutex); - build_all_zonelists(NULL); + build_all_zonelists(NULL, NULL); mutex_unlock(&zonelists_mutex); } } @@ -3415,10 +3415,17 @@ static __init_refok int __build_all_zonelists(void *data) { int nid; int cpu; + pg_data_t *self = data; #ifdef CONFIG_NUMA memset(node_load, 0, sizeof(node_load)); #endif + + if (self && !node_online(self->node_id)) { + build_zonelists(self); + build_zonelist_cache(self); + } + for_each_online_node(nid) { pg_data_t *pgdat = NODE_DATA(nid); @@ -3463,7 +3470,7 @@ static __init_refok int __build_all_zonelists(void *data) * Called with zonelists_mutex held always * unless system_state == SYSTEM_BOOTING. */ -void __ref build_all_zonelists(void *data) +void __ref build_all_zonelists(pg_data_t *pgdat, struct zone *zone) { set_zonelist_order(); @@ -3475,10 +3482,10 @@ void __ref build_all_zonelists(void *data) /* we have to stop all cpus to guarantee there is no user of zonelist */ #ifdef CONFIG_MEMORY_HOTPLUG - if (data) - setup_zone_pageset((struct zone *)data); + if (zone) + setup_zone_pageset(zone); #endif - stop_machine(__build_all_zonelists, NULL, NULL); + stop_machine(__build_all_zonelists, pgdat, NULL); /* cpuset refresh routine should be here */ } vm_total_pages = nr_free_pagecache_pages(); -- cgit v1.2.3 From 340175b7d14d5617559d0c1a54fa0ea204d9edcd Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Tue, 31 Jul 2012 16:43:32 -0700 Subject: mm/hotplug: free zone->pageset when a zone becomes empty When a zone becomes empty after memory offlining, free zone->pageset. Otherwise it will cause memory leak when adding memory to the empty zone again because build_all_zonelists() will allocate zone->pageset for an empty zone. Signed-off-by: Jiang Liu Signed-off-by: Wei Wang Cc: Mel Gorman Cc: Michal Hocko Cc: Minchan Kim Cc: Rusty Russell Cc: Yinghai Lu Cc: Tony Luck Cc: KAMEZAWA Hiroyuki Cc: KOSAKI Motohiro Cc: David Rientjes Cc: Keping Chen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 1 + mm/memory_hotplug.c | 3 +++ mm/page_alloc.c | 13 +++++++++++++ 3 files changed, 17 insertions(+) (limited to 'include') diff --git a/include/linux/mm.h b/include/linux/mm.h index 3955bedeeed1..7c6dfd2faa69 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1331,6 +1331,7 @@ void warn_alloc_failed(gfp_t gfp_mask, int order, const char *fmt, ...); extern void setup_per_cpu_pageset(void); extern void zone_pcp_update(struct zone *zone); +extern void zone_pcp_reset(struct zone *zone); /* nommu.c */ extern atomic_long_t mmap_pages_allocated; diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 597d371329d3..3ad25f9d1fc1 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -966,6 +966,9 @@ repeat: init_per_zone_wmark_min(); + if (!populated_zone(zone)) + zone_pcp_reset(zone); + if (!node_present_pages(node)) { node_clear_state(node, N_HIGH_MEMORY); kswapd_stop(node); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 9ad6866ac49c..9c9a31665a78 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5894,6 +5894,19 @@ void free_contig_range(unsigned long pfn, unsigned nr_pages) #endif #ifdef CONFIG_MEMORY_HOTREMOVE +void zone_pcp_reset(struct zone *zone) +{ + unsigned long flags; + + /* avoid races with drain_pages() */ + local_irq_save(flags); + if (zone->pageset != &boot_pageset) { + free_percpu(zone->pageset); + zone->pageset = &boot_pageset; + } + local_irq_restore(flags); +} + /* * All pages in the range must be isolated before calling this. */ -- cgit v1.2.3 From 62ce1c706f817cb9defef3ac2dfdd815149f2968 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 31 Jul 2012 16:43:39 -0700 Subject: mm, oom: move declaration for mem_cgroup_out_of_memory to oom.h mem_cgroup_out_of_memory() is defined in mm/oom_kill.c, so declare it in linux/oom.h rather than linux/memcontrol.h. Acked-by: KAMEZAWA Hiroyuki Acked-by: KOSAKI Motohiro Acked-by: Michal Hocko Signed-off-by: David Rientjes Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 2 -- include/linux/oom.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 170076222431..c0bff8976a69 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -72,8 +72,6 @@ extern void mem_cgroup_uncharge_end(void); extern void mem_cgroup_uncharge_page(struct page *page); extern void mem_cgroup_uncharge_cache_page(struct page *page); -extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, - int order); bool __mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg, struct mem_cgroup *memcg); int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *memcg); diff --git a/include/linux/oom.h b/include/linux/oom.h index e4c29bc72e70..eb9dc14362c5 100644 --- a/include/linux/oom.h +++ b/include/linux/oom.h @@ -49,6 +49,8 @@ extern unsigned long oom_badness(struct task_struct *p, extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); +extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, + int order); extern void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order, nodemask_t *mask, bool force_kill); extern int register_oom_notifier(struct notifier_block *nb); -- cgit v1.2.3 From 9cbb78bb314360a860a8b23723971cb6fcb54176 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 31 Jul 2012 16:43:44 -0700 Subject: mm, memcg: introduce own oom handler to iterate only over its own threads The global oom killer is serialized by the per-zonelist try_set_zonelist_oom() which is used in the page allocator. Concurrent oom kills are thus a rare event and only occur in systems using mempolicies and with a large number of nodes. Memory controller oom kills, however, can frequently be concurrent since there is no serialization once the oom killer is called for oom conditions in several different memcgs in parallel. This creates a massive contention on tasklist_lock since the oom killer requires the readside for the tasklist iteration. If several memcgs are calling the oom killer, this lock can be held for a substantial amount of time, especially if threads continue to enter it as other threads are exiting. Since the exit path grabs the writeside of the lock with irqs disabled in a few different places, this can cause a soft lockup on cpus as a result of tasklist_lock starvation. The kernel lacks unfair writelocks, and successful calls to the oom killer usually result in at least one thread entering the exit path, so an alternative solution is needed. This patch introduces a seperate oom handler for memcgs so that they do not require tasklist_lock for as much time. Instead, it iterates only over the threads attached to the oom memcg and grabs a reference to the selected thread before calling oom_kill_process() to ensure it doesn't prematurely exit. This still requires tasklist_lock for the tasklist dump, iterating children of the selected process, and killing all other threads on the system sharing the same memory as the selected victim. So while this isn't a complete solution to tasklist_lock starvation, it significantly reduces the amount of time that it is held. Acked-by: KAMEZAWA Hiroyuki Acked-by: Michal Hocko Signed-off-by: David Rientjes Cc: Oleg Nesterov Cc: KOSAKI Motohiro Reviewed-by: Sha Zhengju Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 9 ++----- include/linux/oom.h | 16 ++++++++++++ mm/memcontrol.c | 61 +++++++++++++++++++++++++++++++++++++++++++++- mm/oom_kill.c | 48 ++++++++++++------------------------ 4 files changed, 93 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index c0bff8976a69..2a80544aec99 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -180,7 +180,8 @@ static inline void mem_cgroup_dec_page_stat(struct page *page, unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order, gfp_t gfp_mask, unsigned long *total_scanned); -u64 mem_cgroup_get_limit(struct mem_cgroup *memcg); +extern void __mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, + int order); void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx); #ifdef CONFIG_TRANSPARENT_HUGEPAGE @@ -364,12 +365,6 @@ unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order, return 0; } -static inline -u64 mem_cgroup_get_limit(struct mem_cgroup *memcg) -{ - return 0; -} - static inline void mem_cgroup_split_huge_fixup(struct page *head) { } diff --git a/include/linux/oom.h b/include/linux/oom.h index eb9dc14362c5..5dc0e384ae9e 100644 --- a/include/linux/oom.h +++ b/include/linux/oom.h @@ -40,17 +40,33 @@ enum oom_constraint { CONSTRAINT_MEMCG, }; +enum oom_scan_t { + OOM_SCAN_OK, /* scan thread and find its badness */ + OOM_SCAN_CONTINUE, /* do not consider thread for oom kill */ + OOM_SCAN_ABORT, /* abort the iteration and return */ + OOM_SCAN_SELECT, /* always select this thread first */ +}; + extern void compare_swap_oom_score_adj(int old_val, int new_val); extern int test_set_oom_score_adj(int new_val); extern unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg, const nodemask_t *nodemask, unsigned long totalpages); +extern void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, + unsigned int points, unsigned long totalpages, + struct mem_cgroup *memcg, nodemask_t *nodemask, + const char *message); + extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); +extern enum oom_scan_t oom_scan_process_thread(struct task_struct *task, + unsigned long totalpages, const nodemask_t *nodemask, + bool force_kill); extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, int order); + extern void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order, nodemask_t *mask, bool force_kill); extern int register_oom_notifier(struct notifier_block *nb); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 4f73c823c59f..b78972e2f43f 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1453,7 +1453,7 @@ static int mem_cgroup_count_children(struct mem_cgroup *memcg) /* * Return the memory (and swap, if configured) limit for a memcg. */ -u64 mem_cgroup_get_limit(struct mem_cgroup *memcg) +static u64 mem_cgroup_get_limit(struct mem_cgroup *memcg) { u64 limit; u64 memsw; @@ -1469,6 +1469,65 @@ u64 mem_cgroup_get_limit(struct mem_cgroup *memcg) return min(limit, memsw); } +void __mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, + int order) +{ + struct mem_cgroup *iter; + unsigned long chosen_points = 0; + unsigned long totalpages; + unsigned int points = 0; + struct task_struct *chosen = NULL; + + totalpages = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT ? : 1; + for_each_mem_cgroup_tree(iter, memcg) { + struct cgroup *cgroup = iter->css.cgroup; + struct cgroup_iter it; + struct task_struct *task; + + cgroup_iter_start(cgroup, &it); + while ((task = cgroup_iter_next(cgroup, &it))) { + switch (oom_scan_process_thread(task, totalpages, NULL, + false)) { + case OOM_SCAN_SELECT: + if (chosen) + put_task_struct(chosen); + chosen = task; + chosen_points = ULONG_MAX; + get_task_struct(chosen); + /* fall through */ + case OOM_SCAN_CONTINUE: + continue; + case OOM_SCAN_ABORT: + cgroup_iter_end(cgroup, &it); + mem_cgroup_iter_break(memcg, iter); + if (chosen) + put_task_struct(chosen); + return; + case OOM_SCAN_OK: + break; + }; + points = oom_badness(task, memcg, NULL, totalpages); + if (points > chosen_points) { + if (chosen) + put_task_struct(chosen); + chosen = task; + chosen_points = points; + get_task_struct(chosen); + } + } + cgroup_iter_end(cgroup, &it); + } + + if (!chosen) + return; + points = chosen_points * 1000 / totalpages; + read_lock(&tasklist_lock); + oom_kill_process(chosen, gfp_mask, order, points, totalpages, memcg, + NULL, "Memory cgroup out of memory"); + read_unlock(&tasklist_lock); + put_task_struct(chosen); +} + static unsigned long mem_cgroup_reclaim(struct mem_cgroup *memcg, gfp_t gfp_mask, unsigned long flags) diff --git a/mm/oom_kill.c b/mm/oom_kill.c index f8eba9651c0c..c0c97aea837f 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -288,20 +288,13 @@ static enum oom_constraint constrained_alloc(struct zonelist *zonelist, } #endif -enum oom_scan_t { - OOM_SCAN_OK, /* scan thread and find its badness */ - OOM_SCAN_CONTINUE, /* do not consider thread for oom kill */ - OOM_SCAN_ABORT, /* abort the iteration and return */ - OOM_SCAN_SELECT, /* always select this thread first */ -}; - -static enum oom_scan_t oom_scan_process_thread(struct task_struct *task, - struct mem_cgroup *memcg, unsigned long totalpages, - const nodemask_t *nodemask, bool force_kill) +enum oom_scan_t oom_scan_process_thread(struct task_struct *task, + unsigned long totalpages, const nodemask_t *nodemask, + bool force_kill) { if (task->exit_state) return OOM_SCAN_CONTINUE; - if (oom_unkillable_task(task, memcg, nodemask)) + if (oom_unkillable_task(task, NULL, nodemask)) return OOM_SCAN_CONTINUE; /* @@ -348,8 +341,8 @@ static enum oom_scan_t oom_scan_process_thread(struct task_struct *task, * (not docbooked, we don't want this one cluttering up the manual) */ static struct task_struct *select_bad_process(unsigned int *ppoints, - unsigned long totalpages, struct mem_cgroup *memcg, - const nodemask_t *nodemask, bool force_kill) + unsigned long totalpages, const nodemask_t *nodemask, + bool force_kill) { struct task_struct *g, *p; struct task_struct *chosen = NULL; @@ -358,7 +351,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, do_each_thread(g, p) { unsigned int points; - switch (oom_scan_process_thread(p, memcg, totalpages, nodemask, + switch (oom_scan_process_thread(p, totalpages, nodemask, force_kill)) { case OOM_SCAN_SELECT: chosen = p; @@ -371,7 +364,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, case OOM_SCAN_OK: break; }; - points = oom_badness(p, memcg, nodemask, totalpages); + points = oom_badness(p, NULL, nodemask, totalpages); if (points > chosen_points) { chosen = p; chosen_points = points; @@ -443,10 +436,10 @@ static void dump_header(struct task_struct *p, gfp_t gfp_mask, int order, } #define K(x) ((x) << (PAGE_SHIFT-10)) -static void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, - unsigned int points, unsigned long totalpages, - struct mem_cgroup *memcg, nodemask_t *nodemask, - const char *message) +void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, + unsigned int points, unsigned long totalpages, + struct mem_cgroup *memcg, nodemask_t *nodemask, + const char *message) { struct task_struct *victim = p; struct task_struct *child; @@ -564,10 +557,6 @@ static void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, int order) { - unsigned long limit; - unsigned int points = 0; - struct task_struct *p; - /* * If current has a pending SIGKILL, then automatically select it. The * goal is to allow it to allocate so that it may quickly exit and free @@ -579,13 +568,7 @@ void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, } check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, order, NULL); - limit = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT ? : 1; - read_lock(&tasklist_lock); - p = select_bad_process(&points, limit, memcg, NULL, false); - if (p && PTR_ERR(p) != -1UL) - oom_kill_process(p, gfp_mask, order, points, limit, memcg, NULL, - "Memory cgroup out of memory"); - read_unlock(&tasklist_lock); + __mem_cgroup_out_of_memory(memcg, gfp_mask, order); } #endif @@ -710,7 +693,7 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, struct task_struct *p; unsigned long totalpages; unsigned long freed = 0; - unsigned int points; + unsigned int uninitialized_var(points); enum oom_constraint constraint = CONSTRAINT_NONE; int killed = 0; @@ -748,8 +731,7 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, goto out; } - p = select_bad_process(&points, totalpages, NULL, mpol_mask, - force_kill); + p = select_bad_process(&points, totalpages, mpol_mask, force_kill); /* Found nothing?!?! Either we hang forever, or we panic. */ if (!p) { dump_header(NULL, gfp_mask, order, NULL, mpol_mask); -- cgit v1.2.3 From 876aafbfd9ba5bb352f1b14622c27f3fe9a99013 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 31 Jul 2012 16:43:48 -0700 Subject: mm, memcg: move all oom handling to memcontrol.c By globally defining check_panic_on_oom(), the memcg oom handler can be moved entirely to mm/memcontrol.c. This removes the ugly #ifdef in the oom killer and cleans up the code. Signed-off-by: David Rientjes Cc: KAMEZAWA Hiroyuki Acked-by: Michal Hocko Cc: Oleg Nesterov Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 2 -- include/linux/oom.h | 3 +++ mm/memcontrol.c | 15 +++++++++++++-- mm/oom_kill.c | 23 ++--------------------- 4 files changed, 18 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 2a80544aec99..5a3ee6423634 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -180,8 +180,6 @@ static inline void mem_cgroup_dec_page_stat(struct page *page, unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order, gfp_t gfp_mask, unsigned long *total_scanned); -extern void __mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, - int order); void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx); #ifdef CONFIG_TRANSPARENT_HUGEPAGE diff --git a/include/linux/oom.h b/include/linux/oom.h index 5dc0e384ae9e..49a3031fda50 100644 --- a/include/linux/oom.h +++ b/include/linux/oom.h @@ -61,6 +61,9 @@ extern void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); +extern void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, + int order, const nodemask_t *nodemask); + extern enum oom_scan_t oom_scan_process_thread(struct task_struct *task, unsigned long totalpages, const nodemask_t *nodemask, bool force_kill); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 77a29cea5d76..0f692a2dbfcb 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1469,8 +1469,8 @@ static u64 mem_cgroup_get_limit(struct mem_cgroup *memcg) return min(limit, memsw); } -void __mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, - int order) +void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, + int order) { struct mem_cgroup *iter; unsigned long chosen_points = 0; @@ -1478,6 +1478,17 @@ void __mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, unsigned int points = 0; struct task_struct *chosen = NULL; + /* + * If current has a pending SIGKILL, then automatically select it. The + * goal is to allow it to allocate so that it may quickly exit and free + * its memory. + */ + if (fatal_signal_pending(current)) { + set_thread_flag(TIF_MEMDIE); + return; + } + + check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, order, NULL); totalpages = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT ? : 1; for_each_mem_cgroup_tree(iter, memcg) { struct cgroup *cgroup = iter->css.cgroup; diff --git a/mm/oom_kill.c b/mm/oom_kill.c index a3a32ae02e9d..198600861638 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -556,8 +556,8 @@ void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, /* * Determines whether the kernel must panic because of the panic_on_oom sysctl. */ -static void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, - int order, const nodemask_t *nodemask) +void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, + int order, const nodemask_t *nodemask) { if (likely(!sysctl_panic_on_oom)) return; @@ -575,25 +575,6 @@ static void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, sysctl_panic_on_oom == 2 ? "compulsory" : "system-wide"); } -#ifdef CONFIG_MEMCG -void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, - int order) -{ - /* - * If current has a pending SIGKILL, then automatically select it. The - * goal is to allow it to allocate so that it may quickly exit and free - * its memory. - */ - if (fatal_signal_pending(current)) { - set_thread_flag(TIF_MEMDIE); - return; - } - - check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, order, NULL); - __mem_cgroup_out_of_memory(memcg, gfp_mask, order); -} -#endif - static BLOCKING_NOTIFIER_HEAD(oom_notify_list); int register_oom_notifier(struct notifier_block *nb) -- cgit v1.2.3 From ee6f509c3274014d1f52e7a7a10aee9f85393c5e Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Tue, 31 Jul 2012 16:43:50 -0700 Subject: mm: factor out memory isolate functions mm/page_alloc.c has some memory isolation functions but they are used only when we enable CONFIG_{CMA|MEMORY_HOTPLUG|MEMORY_FAILURE}. So let's make it configurable by new CONFIG_MEMORY_ISOLATION so that it can reduce binary size and we can check it simple by CONFIG_MEMORY_ISOLATION, not if defined CONFIG_{CMA|MEMORY_HOTPLUG|MEMORY_FAILURE}. Signed-off-by: Minchan Kim Cc: Andi Kleen Cc: Marek Szyprowski Acked-by: KAMEZAWA Hiroyuki Cc: KOSAKI Motohiro Cc: Mel Gorman Cc: Michal Hocko Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/Kconfig | 1 + include/linux/page-isolation.h | 13 ++++--- mm/Kconfig | 5 +++ mm/Makefile | 5 +-- mm/page_alloc.c | 80 +++--------------------------------------- mm/page_isolation.c | 71 +++++++++++++++++++++++++++++++++++++ 6 files changed, 93 insertions(+), 82 deletions(-) (limited to 'include') diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 9b21469482ae..08b4c5209384 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -196,6 +196,7 @@ config CMA bool "Contiguous Memory Allocator (EXPERIMENTAL)" depends on HAVE_DMA_CONTIGUOUS && HAVE_MEMBLOCK && EXPERIMENTAL select MIGRATION + select MEMORY_ISOLATION help This enables the Contiguous Memory Allocator which allows drivers to allocate big physically-contiguous blocks of memory for use with diff --git a/include/linux/page-isolation.h b/include/linux/page-isolation.h index 3bdcab30ca41..105077aa7685 100644 --- a/include/linux/page-isolation.h +++ b/include/linux/page-isolation.h @@ -1,6 +1,11 @@ #ifndef __LINUX_PAGEISOLATION_H #define __LINUX_PAGEISOLATION_H + +bool has_unmovable_pages(struct zone *zone, struct page *page, int count); +void set_pageblock_migratetype(struct page *page, int migratetype); +int move_freepages_block(struct zone *zone, struct page *page, + int migratetype); /* * Changes migrate type in [start_pfn, end_pfn) to be MIGRATE_ISOLATE. * If specified range includes migrate types other than MOVABLE or CMA, @@ -10,7 +15,7 @@ * free all pages in the range. test_page_isolated() can be used for * test it. */ -extern int +int start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn, unsigned migratetype); @@ -18,7 +23,7 @@ start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn, * Changes MIGRATE_ISOLATE to MIGRATE_MOVABLE. * target range is [start_pfn, end_pfn) */ -extern int +int undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn, unsigned migratetype); @@ -30,8 +35,8 @@ int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn); /* * Internal functions. Changes pageblock's migrate type. */ -extern int set_migratetype_isolate(struct page *page); -extern void unset_migratetype_isolate(struct page *page, unsigned migratetype); +int set_migratetype_isolate(struct page *page); +void unset_migratetype_isolate(struct page *page, unsigned migratetype); #endif diff --git a/mm/Kconfig b/mm/Kconfig index 82fed4eb2b6f..d5c8019c6627 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -140,9 +140,13 @@ config ARCH_DISCARD_MEMBLOCK config NO_BOOTMEM boolean +config MEMORY_ISOLATION + boolean + # eventually, we can have this option just 'select SPARSEMEM' config MEMORY_HOTPLUG bool "Allow for memory hot-add" + select MEMORY_ISOLATION depends on SPARSEMEM || X86_64_ACPI_NUMA depends on HOTPLUG && ARCH_ENABLE_MEMORY_HOTPLUG depends on (IA64 || X86 || PPC_BOOK3S_64 || SUPERH || S390) @@ -272,6 +276,7 @@ config MEMORY_FAILURE depends on MMU depends on ARCH_SUPPORTS_MEMORY_FAILURE bool "Enable recovery from hardware memory errors" + select MEMORY_ISOLATION help Enables code to recover from some memory failures on systems with MCA recovery. This allows a system to continue running diff --git a/mm/Makefile b/mm/Makefile index 290bbfe33698..92753e2d82da 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -15,8 +15,8 @@ obj-y := filemap.o mempool.o oom_kill.o fadvise.o \ maccess.o page_alloc.o page-writeback.o \ readahead.o swap.o truncate.o vmscan.o shmem.o \ prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ - page_isolation.o mm_init.o mmu_context.o percpu.o \ - compaction.o slab_common.o $(mmu-y) + mm_init.o mmu_context.o percpu.o slab_common.o \ + compaction.o $(mmu-y) obj-y += init-mm.o @@ -56,3 +56,4 @@ obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o obj-$(CONFIG_CLEANCACHE) += cleancache.o +obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 667338e80e94..228194728ccd 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -51,7 +51,6 @@ #include #include #include -#include #include #include #include @@ -219,7 +218,7 @@ EXPORT_SYMBOL(nr_online_nodes); int page_group_by_mobility_disabled __read_mostly; -static void set_pageblock_migratetype(struct page *page, int migratetype) +void set_pageblock_migratetype(struct page *page, int migratetype) { if (unlikely(page_group_by_mobility_disabled)) @@ -954,7 +953,7 @@ static int move_freepages(struct zone *zone, return pages_moved; } -static int move_freepages_block(struct zone *zone, struct page *page, +int move_freepages_block(struct zone *zone, struct page *page, int migratetype) { unsigned long start_pfn, end_pfn; @@ -5463,8 +5462,7 @@ void set_pageblock_flags_group(struct page *page, unsigned long flags, * MIGRATE_MOVABLE block might include unmovable pages. It means you can't * expect this function should be exact. */ -static bool -__has_unmovable_pages(struct zone *zone, struct page *page, int count) +bool has_unmovable_pages(struct zone *zone, struct page *page, int count) { unsigned long pfn, iter, found; int mt; @@ -5541,77 +5539,7 @@ bool is_pageblock_removable_nolock(struct page *page) zone->zone_start_pfn + zone->spanned_pages <= pfn) return false; - return !__has_unmovable_pages(zone, page, 0); -} - -int set_migratetype_isolate(struct page *page) -{ - struct zone *zone; - unsigned long flags, pfn; - struct memory_isolate_notify arg; - int notifier_ret; - int ret = -EBUSY; - - zone = page_zone(page); - - spin_lock_irqsave(&zone->lock, flags); - - pfn = page_to_pfn(page); - arg.start_pfn = pfn; - arg.nr_pages = pageblock_nr_pages; - arg.pages_found = 0; - - /* - * It may be possible to isolate a pageblock even if the - * migratetype is not MIGRATE_MOVABLE. The memory isolation - * notifier chain is used by balloon drivers to return the - * number of pages in a range that are held by the balloon - * driver to shrink memory. If all the pages are accounted for - * by balloons, are free, or on the LRU, isolation can continue. - * Later, for example, when memory hotplug notifier runs, these - * pages reported as "can be isolated" should be isolated(freed) - * by the balloon driver through the memory notifier chain. - */ - notifier_ret = memory_isolate_notify(MEM_ISOLATE_COUNT, &arg); - notifier_ret = notifier_to_errno(notifier_ret); - if (notifier_ret) - goto out; - /* - * FIXME: Now, memory hotplug doesn't call shrink_slab() by itself. - * We just check MOVABLE pages. - */ - if (!__has_unmovable_pages(zone, page, arg.pages_found)) - ret = 0; - /* - * Unmovable means "not-on-lru" pages. If Unmovable pages are - * larger than removable-by-driver pages reported by notifier, - * we'll fail. - */ - -out: - if (!ret) { - set_pageblock_migratetype(page, MIGRATE_ISOLATE); - move_freepages_block(zone, page, MIGRATE_ISOLATE); - } - - spin_unlock_irqrestore(&zone->lock, flags); - if (!ret) - drain_all_pages(); - return ret; -} - -void unset_migratetype_isolate(struct page *page, unsigned migratetype) -{ - struct zone *zone; - unsigned long flags; - zone = page_zone(page); - spin_lock_irqsave(&zone->lock, flags); - if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE) - goto out; - set_pageblock_migratetype(page, migratetype); - move_freepages_block(zone, page, migratetype); -out: - spin_unlock_irqrestore(&zone->lock, flags); + return !has_unmovable_pages(zone, page, 0); } #ifdef CONFIG_CMA diff --git a/mm/page_isolation.c b/mm/page_isolation.c index c9f04774f2b8..fb482cf438da 100644 --- a/mm/page_isolation.c +++ b/mm/page_isolation.c @@ -5,8 +5,79 @@ #include #include #include +#include #include "internal.h" +int set_migratetype_isolate(struct page *page) +{ + struct zone *zone; + unsigned long flags, pfn; + struct memory_isolate_notify arg; + int notifier_ret; + int ret = -EBUSY; + + zone = page_zone(page); + + spin_lock_irqsave(&zone->lock, flags); + + pfn = page_to_pfn(page); + arg.start_pfn = pfn; + arg.nr_pages = pageblock_nr_pages; + arg.pages_found = 0; + + /* + * It may be possible to isolate a pageblock even if the + * migratetype is not MIGRATE_MOVABLE. The memory isolation + * notifier chain is used by balloon drivers to return the + * number of pages in a range that are held by the balloon + * driver to shrink memory. If all the pages are accounted for + * by balloons, are free, or on the LRU, isolation can continue. + * Later, for example, when memory hotplug notifier runs, these + * pages reported as "can be isolated" should be isolated(freed) + * by the balloon driver through the memory notifier chain. + */ + notifier_ret = memory_isolate_notify(MEM_ISOLATE_COUNT, &arg); + notifier_ret = notifier_to_errno(notifier_ret); + if (notifier_ret) + goto out; + /* + * FIXME: Now, memory hotplug doesn't call shrink_slab() by itself. + * We just check MOVABLE pages. + */ + if (!has_unmovable_pages(zone, page, arg.pages_found)) + ret = 0; + + /* + * immobile means "not-on-lru" paes. If immobile is larger than + * removable-by-driver pages reported by notifier, we'll fail. + */ + +out: + if (!ret) { + set_pageblock_migratetype(page, MIGRATE_ISOLATE); + move_freepages_block(zone, page, MIGRATE_ISOLATE); + } + + spin_unlock_irqrestore(&zone->lock, flags); + if (!ret) + drain_all_pages(); + return ret; +} + +void unset_migratetype_isolate(struct page *page, unsigned migratetype) +{ + struct zone *zone; + unsigned long flags; + zone = page_zone(page); + spin_lock_irqsave(&zone->lock, flags); + if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE) + goto out; + set_pageblock_migratetype(page, migratetype); + move_freepages_block(zone, page, migratetype); +out: + spin_unlock_irqrestore(&zone->lock, flags); +} + static inline struct page * __first_valid_page(unsigned long pfn, unsigned long nr_pages) { -- cgit v1.2.3 From 702d1a6e0766d45642c934444fd41f658d251305 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Tue, 31 Jul 2012 16:43:56 -0700 Subject: memory-hotplug: fix kswapd looping forever problem When hotplug offlining happens on zone A, it starts to mark freed page as MIGRATE_ISOLATE type in buddy for preventing further allocation. (MIGRATE_ISOLATE is very irony type because it's apparently on buddy but we can't allocate them). When the memory shortage happens during hotplug offlining, current task starts to reclaim, then wake up kswapd. Kswapd checks watermark, then go sleep because current zone_watermark_ok_safe doesn't consider MIGRATE_ISOLATE freed page count. Current task continue to reclaim in direct reclaim path without kswapd's helping. The problem is that zone->all_unreclaimable is set by only kswapd so that current task would be looping forever like below. __alloc_pages_slowpath restart: wake_all_kswapd rebalance: __alloc_pages_direct_reclaim do_try_to_free_pages if global_reclaim && !all_unreclaimable return 1; /* It means we did did_some_progress */ skip __alloc_pages_may_oom should_alloc_retry goto rebalance; If we apply KOSAKI's patch[1] which doesn't depends on kswapd about setting zone->all_unreclaimable, we can solve this problem by killing some task in direct reclaim path. But it doesn't wake up kswapd, still. It could be a problem still if other subsystem needs GFP_ATOMIC request. So kswapd should consider MIGRATE_ISOLATE when it calculate free pages BEFORE going sleep. This patch counts the number of MIGRATE_ISOLATE page block and zone_watermark_ok_safe will consider it if the system has such blocks (fortunately, it's very rare so no problem in POV overhead and kswapd is never hotpath). Copy/modify from Mel's quote " Ideal solution would be "allocating" the pageblock. It would keep the free space accounting as it is but historically, memory hotplug didn't allocate pages because it would be difficult to detect if a pageblock was isolated or if part of some balloon. Allocating just full pageblocks would work around this, However, it would play very badly with CMA. " [1] http://lkml.org/lkml/2012/6/14/74 [akpm@linux-foundation.org: simplify nr_zone_isolate_freepages(), rework zone_watermark_ok_safe() comment, simplify set_pageblock_isolate() and restore_pageblock_isolate()] [akpm@linux-foundation.org: fix CONFIG_MEMORY_ISOLATION=n build] Signed-off-by: Minchan Kim Suggested-by: KOSAKI Motohiro Tested-by: Aaditya Kumar Cc: KAMEZAWA Hiroyuki Cc: Mel Gorman Cc: Michal Hocko Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 8 ++++++++ mm/page_alloc.c | 30 ++++++++++++++++++++++++++++++ mm/page_isolation.c | 26 ++++++++++++++++++++++++-- 3 files changed, 62 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 98f079bcf399..64b2c3a48286 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -478,6 +478,14 @@ struct zone { * rarely used fields: */ const char *name; +#ifdef CONFIG_MEMORY_ISOLATION + /* + * the number of MIGRATE_ISOLATE *pageblock*. + * We need this for free page counting. Look at zone_watermark_ok_safe. + * It's protected by zone->lock + */ + int nr_pageblock_isolate; +#endif } ____cacheline_internodealigned_in_smp; typedef enum { diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 2e6635993558..6a29ed8e6e60 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -218,6 +218,11 @@ EXPORT_SYMBOL(nr_online_nodes); int page_group_by_mobility_disabled __read_mostly; +/* + * NOTE: + * Don't use set_pageblock_migratetype(page, MIGRATE_ISOLATE) directly. + * Instead, use {un}set_pageblock_isolate. + */ void set_pageblock_migratetype(struct page *page, int migratetype) { @@ -1619,6 +1624,20 @@ static bool __zone_watermark_ok(struct zone *z, int order, unsigned long mark, return true; } +#ifdef CONFIG_MEMORY_ISOLATION +static inline unsigned long nr_zone_isolate_freepages(struct zone *zone) +{ + if (unlikely(zone->nr_pageblock_isolate)) + return zone->nr_pageblock_isolate * pageblock_nr_pages; + return 0; +} +#else +static inline unsigned long nr_zone_isolate_freepages(struct zone *zone) +{ + return 0; +} +#endif + bool zone_watermark_ok(struct zone *z, int order, unsigned long mark, int classzone_idx, int alloc_flags) { @@ -1634,6 +1653,14 @@ bool zone_watermark_ok_safe(struct zone *z, int order, unsigned long mark, if (z->percpu_drift_mark && free_pages < z->percpu_drift_mark) free_pages = zone_page_state_snapshot(z, NR_FREE_PAGES); + /* + * If the zone has MIGRATE_ISOLATE type free pages, we should consider + * it. nr_zone_isolate_freepages is never accurate so kswapd might not + * sleep although it could do so. But this is more desirable for memory + * hotplug than sleeping which can cause a livelock in the direct + * reclaim path. + */ + free_pages -= nr_zone_isolate_freepages(z); return __zone_watermark_ok(z, order, mark, classzone_idx, alloc_flags, free_pages); } @@ -4398,6 +4425,9 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat, lruvec_init(&zone->lruvec, zone); zap_zone_vm_stats(zone); zone->flags = 0; +#ifdef CONFIG_MEMORY_ISOLATION + zone->nr_pageblock_isolate = 0; +#endif if (!size) continue; diff --git a/mm/page_isolation.c b/mm/page_isolation.c index fb482cf438da..247d1f175739 100644 --- a/mm/page_isolation.c +++ b/mm/page_isolation.c @@ -8,6 +8,28 @@ #include #include "internal.h" +/* called while holding zone->lock */ +static void set_pageblock_isolate(struct page *page) +{ + if (get_pageblock_migratetype(page) == MIGRATE_ISOLATE) + return; + + set_pageblock_migratetype(page, MIGRATE_ISOLATE); + page_zone(page)->nr_pageblock_isolate++; +} + +/* called while holding zone->lock */ +static void restore_pageblock_isolate(struct page *page, int migratetype) +{ + struct zone *zone = page_zone(page); + if (WARN_ON(get_pageblock_migratetype(page) != MIGRATE_ISOLATE)) + return; + + BUG_ON(zone->nr_pageblock_isolate <= 0); + set_pageblock_migratetype(page, migratetype); + zone->nr_pageblock_isolate--; +} + int set_migratetype_isolate(struct page *page) { struct zone *zone; @@ -54,7 +76,7 @@ int set_migratetype_isolate(struct page *page) out: if (!ret) { - set_pageblock_migratetype(page, MIGRATE_ISOLATE); + set_pageblock_isolate(page); move_freepages_block(zone, page, MIGRATE_ISOLATE); } @@ -72,8 +94,8 @@ void unset_migratetype_isolate(struct page *page, unsigned migratetype) spin_lock_irqsave(&zone->lock, flags); if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE) goto out; - set_pageblock_migratetype(page, migratetype); move_freepages_block(zone, page, migratetype); + restore_pageblock_isolate(page, migratetype); out: spin_unlock_irqrestore(&zone->lock, flags); } -- cgit v1.2.3 From 072bb0aa5e062902968c5c1007bba332c7820cf4 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:43:58 -0700 Subject: mm: sl[au]b: add knowledge of PFMEMALLOC reserve pages When a user or administrator requires swap for their application, they create a swap partition and file, format it with mkswap and activate it with swapon. Swap over the network is considered as an option in diskless systems. The two likely scenarios are when blade servers are used as part of a cluster where the form factor or maintenance costs do not allow the use of disks and thin clients. The Linux Terminal Server Project recommends the use of the Network Block Device (NBD) for swap according to the manual at https://sourceforge.net/projects/ltsp/files/Docs-Admin-Guide/LTSPManual.pdf/download There is also documentation and tutorials on how to setup swap over NBD at places like https://help.ubuntu.com/community/UbuntuLTSP/EnableNBDSWAP The nbd-client also documents the use of NBD as swap. Despite this, the fact is that a machine using NBD for swap can deadlock within minutes if swap is used intensively. This patch series addresses the problem. The core issue is that network block devices do not use mempools like normal block devices do. As the host cannot control where they receive packets from, they cannot reliably work out in advance how much memory they might need. Some years ago, Peter Zijlstra developed a series of patches that supported swap over an NFS that at least one distribution is carrying within their kernels. This patch series borrows very heavily from Peter's work to support swapping over NBD as a pre-requisite to supporting swap-over-NFS. The bulk of the complexity is concerned with preserving memory that is allocated from the PFMEMALLOC reserves for use by the network layer which is needed for both NBD and NFS. Patch 1 adds knowledge of the PFMEMALLOC reserves to SLAB and SLUB to preserve access to pages allocated under low memory situations to callers that are freeing memory. Patch 2 optimises the SLUB fast path to avoid pfmemalloc checks Patch 3 introduces __GFP_MEMALLOC to allow access to the PFMEMALLOC reserves without setting PFMEMALLOC. Patch 4 opens the possibility for softirqs to use PFMEMALLOC reserves for later use by network packet processing. Patch 5 only sets page->pfmemalloc when ALLOC_NO_WATERMARKS was required Patch 6 ignores memory policies when ALLOC_NO_WATERMARKS is set. Patches 7-12 allows network processing to use PFMEMALLOC reserves when the socket has been marked as being used by the VM to clean pages. If packets are received and stored in pages that were allocated under low-memory situations and are unrelated to the VM, the packets are dropped. Patch 11 reintroduces __skb_alloc_page which the networking folk may object to but is needed in some cases to propogate pfmemalloc from a newly allocated page to an skb. If there is a strong objection, this patch can be dropped with the impact being that swap-over-network will be slower in some cases but it should not fail. Patch 13 is a micro-optimisation to avoid a function call in the common case. Patch 14 tags NBD sockets as being SOCK_MEMALLOC so they can use PFMEMALLOC if necessary. Patch 15 notes that it is still possible for the PFMEMALLOC reserve to be depleted. To prevent this, direct reclaimers get throttled on a waitqueue if 50% of the PFMEMALLOC reserves are depleted. It is expected that kswapd and the direct reclaimers already running will clean enough pages for the low watermark to be reached and the throttled processes are woken up. Patch 16 adds a statistic to track how often processes get throttled Some basic performance testing was run using kernel builds, netperf on loopback for UDP and TCP, hackbench (pipes and sockets), iozone and sysbench. Each of them were expected to use the sl*b allocators reasonably heavily but there did not appear to be significant performance variances. For testing swap-over-NBD, a machine was booted with 2G of RAM with a swapfile backed by NBD. 8*NUM_CPU processes were started that create anonymous memory mappings and read them linearly in a loop. The total size of the mappings were 4*PHYSICAL_MEMORY to use swap heavily under memory pressure. Without the patches and using SLUB, the machine locks up within minutes and runs to completion with them applied. With SLAB, the story is different as an unpatched kernel run to completion. However, the patched kernel completed the test 45% faster. MICRO 3.5.0-rc2 3.5.0-rc2 vanilla swapnbd Unrecognised test vmscan-anon-mmap-write MMTests Statistics: duration Sys Time Running Test (seconds) 197.80 173.07 User+Sys Time Running Test (seconds) 206.96 182.03 Total Elapsed Time (seconds) 3240.70 1762.09 This patch: mm: sl[au]b: add knowledge of PFMEMALLOC reserve pages Allocations of pages below the min watermark run a risk of the machine hanging due to a lack of memory. To prevent this, only callers who have PF_MEMALLOC or TIF_MEMDIE set and are not processing an interrupt are allowed to allocate with ALLOC_NO_WATERMARKS. Once they are allocated to a slab though, nothing prevents other callers consuming free objects within those slabs. This patch limits access to slab pages that were alloced from the PFMEMALLOC reserves. When this patch is applied, pages allocated from below the low watermark are returned with page->pfmemalloc set and it is up to the caller to determine how the page should be protected. SLAB restricts access to any page with page->pfmemalloc set to callers which are known to able to access the PFMEMALLOC reserve. If one is not available, an attempt is made to allocate a new page rather than use a reserve. SLUB is a bit more relaxed in that it only records if the current per-CPU page was allocated from PFMEMALLOC reserve and uses another partial slab if the caller does not have the necessary GFP or process flags. This was found to be sufficient in tests to avoid hangs due to SLUB generally maintaining smaller lists than SLAB. In low-memory conditions it does mean that !PFMEMALLOC allocators can fail a slab allocation even though free objects are available because they are being preserved for callers that are freeing pages. [a.p.zijlstra@chello.nl: Original implementation] [sebastian@breakpoint.cc: Correct order of page flag clearing] Signed-off-by: Mel Gorman Cc: David Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm_types.h | 9 +++ include/linux/page-flags.h | 29 +++++++ mm/internal.h | 3 + mm/page_alloc.c | 27 +++++-- mm/slab.c | 192 ++++++++++++++++++++++++++++++++++++++++----- mm/slub.c | 29 ++++++- 6 files changed, 264 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 074eb98fe15d..375e79eb009b 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -54,6 +54,15 @@ struct page { union { pgoff_t index; /* Our offset within mapping. */ void *freelist; /* slub/slob first free object */ + bool pfmemalloc; /* If set by the page allocator, + * ALLOC_PFMEMALLOC was set + * and the low watermark was not + * met implying that the system + * is under some pressure. The + * caller should try ensure + * this page is only used to + * free other pages. + */ }; union { diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index c88d2a9451af..b5d13841604e 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -7,6 +7,7 @@ #include #include +#include #ifndef __GENERATING_BOUNDS_H #include #include @@ -453,6 +454,34 @@ static inline int PageTransTail(struct page *page) } #endif +/* + * If network-based swap is enabled, sl*b must keep track of whether pages + * were allocated from pfmemalloc reserves. + */ +static inline int PageSlabPfmemalloc(struct page *page) +{ + VM_BUG_ON(!PageSlab(page)); + return PageActive(page); +} + +static inline void SetPageSlabPfmemalloc(struct page *page) +{ + VM_BUG_ON(!PageSlab(page)); + SetPageActive(page); +} + +static inline void __ClearPageSlabPfmemalloc(struct page *page) +{ + VM_BUG_ON(!PageSlab(page)); + __ClearPageActive(page); +} + +static inline void ClearPageSlabPfmemalloc(struct page *page) +{ + VM_BUG_ON(!PageSlab(page)); + ClearPageActive(page); +} + #ifdef CONFIG_MMU #define __PG_MLOCKED (1 << PG_mlocked) #else diff --git a/mm/internal.h b/mm/internal.h index 3314f79d775a..eb76b67890d0 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -279,6 +279,9 @@ static inline struct page *mem_map_next(struct page *iter, #define __paginginit __init #endif +/* Returns true if the gfp_mask allows use of ALLOC_NO_WATERMARK */ +bool gfp_pfmemalloc_allowed(gfp_t gfp_mask); + /* Memory initialisation debug and verification */ enum mminit_level { MMINIT_WARNING, diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 6a29ed8e6e60..38e5be65f24e 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1513,6 +1513,7 @@ failed: #define ALLOC_HARDER 0x10 /* try to alloc harder */ #define ALLOC_HIGH 0x20 /* __GFP_HIGH set */ #define ALLOC_CPUSET 0x40 /* check for correct cpuset */ +#define ALLOC_PFMEMALLOC 0x80 /* Caller has PF_MEMALLOC set */ #ifdef CONFIG_FAIL_PAGE_ALLOC @@ -2293,16 +2294,22 @@ gfp_to_alloc_flags(gfp_t gfp_mask) } else if (unlikely(rt_task(current)) && !in_interrupt()) alloc_flags |= ALLOC_HARDER; - if (likely(!(gfp_mask & __GFP_NOMEMALLOC))) { - if (!in_interrupt() && - ((current->flags & PF_MEMALLOC) || - unlikely(test_thread_flag(TIF_MEMDIE)))) + if ((current->flags & PF_MEMALLOC) || + unlikely(test_thread_flag(TIF_MEMDIE))) { + alloc_flags |= ALLOC_PFMEMALLOC; + + if (likely(!(gfp_mask & __GFP_NOMEMALLOC)) && !in_interrupt()) alloc_flags |= ALLOC_NO_WATERMARKS; } return alloc_flags; } +bool gfp_pfmemalloc_allowed(gfp_t gfp_mask) +{ + return !!(gfp_to_alloc_flags(gfp_mask) & ALLOC_PFMEMALLOC); +} + static inline struct page * __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, struct zonelist *zonelist, enum zone_type high_zoneidx, @@ -2490,10 +2497,18 @@ nopage: warn_alloc_failed(gfp_mask, order, NULL); return page; got_pg: + /* + * page->pfmemalloc is set when the caller had PFMEMALLOC set or is + * been OOM killed. The expectation is that the caller is taking + * steps that will free more memory. The caller should avoid the + * page being used for !PFMEMALLOC purposes. + */ + page->pfmemalloc = !!(alloc_flags & ALLOC_PFMEMALLOC); + if (kmemcheck_enabled) kmemcheck_pagealloc_alloc(page, order, gfp_mask); - return page; + return page; } /* @@ -2544,6 +2559,8 @@ retry_cpuset: page = __alloc_pages_slowpath(gfp_mask, order, zonelist, high_zoneidx, nodemask, preferred_zone, migratetype); + else + page->pfmemalloc = false; trace_mm_page_alloc(page, order, gfp_mask, migratetype); diff --git a/mm/slab.c b/mm/slab.c index 1fcf3ac94b6c..55d84a22ad96 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -124,6 +124,8 @@ #include +#include "internal.h" + /* * DEBUG - 1 for kmem_cache_create() to honour; SLAB_RED_ZONE & SLAB_POISON. * 0 for faster, smaller code (especially in the critical paths). @@ -152,6 +154,12 @@ #define ARCH_KMALLOC_FLAGS SLAB_HWCACHE_ALIGN #endif +/* + * true if a page was allocated from pfmemalloc reserves for network-based + * swap + */ +static bool pfmemalloc_active __read_mostly; + /* Legal flag mask for kmem_cache_create(). */ #if DEBUG # define CREATE_MASK (SLAB_RED_ZONE | \ @@ -257,9 +265,30 @@ struct array_cache { * Must have this definition in here for the proper * alignment of array_cache. Also simplifies accessing * the entries. + * + * Entries should not be directly dereferenced as + * entries belonging to slabs marked pfmemalloc will + * have the lower bits set SLAB_OBJ_PFMEMALLOC */ }; +#define SLAB_OBJ_PFMEMALLOC 1 +static inline bool is_obj_pfmemalloc(void *objp) +{ + return (unsigned long)objp & SLAB_OBJ_PFMEMALLOC; +} + +static inline void set_obj_pfmemalloc(void **objp) +{ + *objp = (void *)((unsigned long)*objp | SLAB_OBJ_PFMEMALLOC); + return; +} + +static inline void clear_obj_pfmemalloc(void **objp) +{ + *objp = (void *)((unsigned long)*objp & ~SLAB_OBJ_PFMEMALLOC); +} + /* * bootstrap: The caches do not work without cpuarrays anymore, but the * cpuarrays are allocated from the generic caches... @@ -900,6 +929,102 @@ static struct array_cache *alloc_arraycache(int node, int entries, return nc; } +static inline bool is_slab_pfmemalloc(struct slab *slabp) +{ + struct page *page = virt_to_page(slabp->s_mem); + + return PageSlabPfmemalloc(page); +} + +/* Clears pfmemalloc_active if no slabs have pfmalloc set */ +static void recheck_pfmemalloc_active(struct kmem_cache *cachep, + struct array_cache *ac) +{ + struct kmem_list3 *l3 = cachep->nodelists[numa_mem_id()]; + struct slab *slabp; + unsigned long flags; + + if (!pfmemalloc_active) + return; + + spin_lock_irqsave(&l3->list_lock, flags); + list_for_each_entry(slabp, &l3->slabs_full, list) + if (is_slab_pfmemalloc(slabp)) + goto out; + + list_for_each_entry(slabp, &l3->slabs_partial, list) + if (is_slab_pfmemalloc(slabp)) + goto out; + + list_for_each_entry(slabp, &l3->slabs_free, list) + if (is_slab_pfmemalloc(slabp)) + goto out; + + pfmemalloc_active = false; +out: + spin_unlock_irqrestore(&l3->list_lock, flags); +} + +static void *ac_get_obj(struct kmem_cache *cachep, struct array_cache *ac, + gfp_t flags, bool force_refill) +{ + int i; + void *objp = ac->entry[--ac->avail]; + + /* Ensure the caller is allowed to use objects from PFMEMALLOC slab */ + if (unlikely(is_obj_pfmemalloc(objp))) { + struct kmem_list3 *l3; + + if (gfp_pfmemalloc_allowed(flags)) { + clear_obj_pfmemalloc(&objp); + return objp; + } + + /* The caller cannot use PFMEMALLOC objects, find another one */ + for (i = 1; i < ac->avail; i++) { + /* If a !PFMEMALLOC object is found, swap them */ + if (!is_obj_pfmemalloc(ac->entry[i])) { + objp = ac->entry[i]; + ac->entry[i] = ac->entry[ac->avail]; + ac->entry[ac->avail] = objp; + return objp; + } + } + + /* + * If there are empty slabs on the slabs_free list and we are + * being forced to refill the cache, mark this one !pfmemalloc. + */ + l3 = cachep->nodelists[numa_mem_id()]; + if (!list_empty(&l3->slabs_free) && force_refill) { + struct slab *slabp = virt_to_slab(objp); + ClearPageSlabPfmemalloc(virt_to_page(slabp->s_mem)); + clear_obj_pfmemalloc(&objp); + recheck_pfmemalloc_active(cachep, ac); + return objp; + } + + /* No !PFMEMALLOC objects available */ + ac->avail++; + objp = NULL; + } + + return objp; +} + +static void ac_put_obj(struct kmem_cache *cachep, struct array_cache *ac, + void *objp) +{ + if (unlikely(pfmemalloc_active)) { + /* Some pfmemalloc slabs exist, check if this is one */ + struct page *page = virt_to_page(objp); + if (PageSlabPfmemalloc(page)) + set_obj_pfmemalloc(&objp); + } + + ac->entry[ac->avail++] = objp; +} + /* * Transfer objects in one arraycache to another. * Locking must be handled by the caller. @@ -1076,7 +1201,7 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp) STATS_INC_ACOVERFLOW(cachep); __drain_alien_cache(cachep, alien, nodeid); } - alien->entry[alien->avail++] = objp; + ac_put_obj(cachep, alien, objp); spin_unlock(&alien->lock); } else { spin_lock(&(cachep->nodelists[nodeid])->list_lock); @@ -1759,6 +1884,10 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid) return NULL; } + /* Record if ALLOC_PFMEMALLOC was set when allocating the slab */ + if (unlikely(page->pfmemalloc)) + pfmemalloc_active = true; + nr_pages = (1 << cachep->gfporder); if (cachep->flags & SLAB_RECLAIM_ACCOUNT) add_zone_page_state(page_zone(page), @@ -1766,9 +1895,13 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid) else add_zone_page_state(page_zone(page), NR_SLAB_UNRECLAIMABLE, nr_pages); - for (i = 0; i < nr_pages; i++) + for (i = 0; i < nr_pages; i++) { __SetPageSlab(page + i); + if (page->pfmemalloc) + SetPageSlabPfmemalloc(page + i); + } + if (kmemcheck_enabled && !(cachep->flags & SLAB_NOTRACK)) { kmemcheck_alloc_shadow(page, cachep->gfporder, flags, nodeid); @@ -1800,6 +1933,7 @@ static void kmem_freepages(struct kmem_cache *cachep, void *addr) NR_SLAB_UNRECLAIMABLE, nr_freed); while (i--) { BUG_ON(!PageSlab(page)); + __ClearPageSlabPfmemalloc(page); __ClearPageSlab(page); page++; } @@ -3015,16 +3149,19 @@ bad: #define check_slabp(x,y) do { } while(0) #endif -static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags) +static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags, + bool force_refill) { int batchcount; struct kmem_list3 *l3; struct array_cache *ac; int node; -retry: check_irq_off(); node = numa_mem_id(); + if (unlikely(force_refill)) + goto force_grow; +retry: ac = cpu_cache_get(cachep); batchcount = ac->batchcount; if (!ac->touched && batchcount > BATCHREFILL_LIMIT) { @@ -3074,8 +3211,8 @@ retry: STATS_INC_ACTIVE(cachep); STATS_SET_HIGH(cachep); - ac->entry[ac->avail++] = slab_get_obj(cachep, slabp, - node); + ac_put_obj(cachep, ac, slab_get_obj(cachep, slabp, + node)); } check_slabp(cachep, slabp); @@ -3094,18 +3231,22 @@ alloc_done: if (unlikely(!ac->avail)) { int x; +force_grow: x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL); /* cache_grow can reenable interrupts, then ac could change. */ ac = cpu_cache_get(cachep); - if (!x && ac->avail == 0) /* no objects in sight? abort */ + + /* no objects in sight? abort */ + if (!x && (ac->avail == 0 || force_refill)) return NULL; if (!ac->avail) /* objects refilled by interrupt? */ goto retry; } ac->touched = 1; - return ac->entry[--ac->avail]; + + return ac_get_obj(cachep, ac, flags, force_refill); } static inline void cache_alloc_debugcheck_before(struct kmem_cache *cachep, @@ -3187,23 +3328,35 @@ static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags) { void *objp; struct array_cache *ac; + bool force_refill = false; check_irq_off(); ac = cpu_cache_get(cachep); if (likely(ac->avail)) { - STATS_INC_ALLOCHIT(cachep); ac->touched = 1; - objp = ac->entry[--ac->avail]; - } else { - STATS_INC_ALLOCMISS(cachep); - objp = cache_alloc_refill(cachep, flags); + objp = ac_get_obj(cachep, ac, flags, false); + /* - * the 'ac' may be updated by cache_alloc_refill(), - * and kmemleak_erase() requires its correct value. + * Allow for the possibility all avail objects are not allowed + * by the current flags */ - ac = cpu_cache_get(cachep); + if (objp) { + STATS_INC_ALLOCHIT(cachep); + goto out; + } + force_refill = true; } + + STATS_INC_ALLOCMISS(cachep); + objp = cache_alloc_refill(cachep, flags, force_refill); + /* + * the 'ac' may be updated by cache_alloc_refill(), + * and kmemleak_erase() requires its correct value. + */ + ac = cpu_cache_get(cachep); + +out: /* * To avoid a false negative, if an object that is in one of the * per-CPU caches is leaked, we need to make sure kmemleak doesn't @@ -3525,9 +3678,12 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects, struct kmem_list3 *l3; for (i = 0; i < nr_objects; i++) { - void *objp = objpp[i]; + void *objp; struct slab *slabp; + clear_obj_pfmemalloc(&objpp[i]); + objp = objpp[i]; + slabp = virt_to_slab(objp); l3 = cachep->nodelists[node]; list_del(&slabp->list); @@ -3645,7 +3801,7 @@ static inline void __cache_free(struct kmem_cache *cachep, void *objp, cache_flusharray(cachep, ac); } - ac->entry[ac->avail++] = objp; + ac_put_obj(cachep, ac, objp); } /** diff --git a/mm/slub.c b/mm/slub.c index e517d435e5dc..c3f05e1599c0 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -34,6 +34,8 @@ #include +#include "internal.h" + /* * Lock order: * 1. slab_mutex (Global Mutex) @@ -1354,6 +1356,8 @@ static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node) inc_slabs_node(s, page_to_nid(page), page->objects); page->slab = s; __SetPageSlab(page); + if (page->pfmemalloc) + SetPageSlabPfmemalloc(page); start = page_address(page); @@ -1397,6 +1401,7 @@ static void __free_slab(struct kmem_cache *s, struct page *page) NR_SLAB_RECLAIMABLE : NR_SLAB_UNRECLAIMABLE, -pages); + __ClearPageSlabPfmemalloc(page); __ClearPageSlab(page); reset_page_mapcount(page); if (current->reclaim_state) @@ -2126,6 +2131,14 @@ static inline void *new_slab_objects(struct kmem_cache *s, gfp_t flags, return freelist; } +static inline bool pfmemalloc_match(struct page *page, gfp_t gfpflags) +{ + if (unlikely(PageSlabPfmemalloc(page))) + return gfp_pfmemalloc_allowed(gfpflags); + + return true; +} + /* * Check the page->freelist of a page and either transfer the freelist to the per cpu freelist * or deactivate the page. @@ -2206,6 +2219,18 @@ redo: goto new_slab; } + /* + * By rights, we should be searching for a slab page that was + * PFMEMALLOC but right now, we are losing the pfmemalloc + * information when the page leaves the per-cpu allocator + */ + if (unlikely(!pfmemalloc_match(page, gfpflags))) { + deactivate_slab(s, page, c->freelist); + c->page = NULL; + c->freelist = NULL; + goto new_slab; + } + /* must check again c->freelist in case of cpu migration or IRQ */ freelist = c->freelist; if (freelist) @@ -2312,8 +2337,8 @@ redo: object = c->freelist; page = c->page; - if (unlikely(!object || !node_match(page, node))) - + if (unlikely(!object || !node_match(page, node) || + !pfmemalloc_match(page, gfpflags))) object = __slab_alloc(s, gfpflags, node, addr, c); else { -- cgit v1.2.3 From b37f1dd0f543d9714f96c2f9b9f74f7bdfdfdf31 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:03 -0700 Subject: mm: introduce __GFP_MEMALLOC to allow access to emergency reserves __GFP_MEMALLOC will allow the allocation to disregard the watermarks, much like PF_MEMALLOC. It allows one to pass along the memalloc state in object related allocation flags as opposed to task related flags, such as sk->sk_allocation. This removes the need for ALLOC_PFMEMALLOC as callers using __GFP_MEMALLOC can get the ALLOC_NO_WATERMARK flag which is now enough to identify allocations related to page reclaim. Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Cc: David Miller Cc: Neil Brown Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/gfp.h | 10 ++++++++-- include/linux/mm_types.h | 2 +- include/trace/events/gfpflags.h | 1 + mm/page_alloc.c | 22 ++++++++++------------ mm/slab.c | 2 +- 5 files changed, 21 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/gfp.h b/include/linux/gfp.h index 1e49be49d324..cbd7400e5862 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -23,6 +23,7 @@ struct vm_area_struct; #define ___GFP_REPEAT 0x400u #define ___GFP_NOFAIL 0x800u #define ___GFP_NORETRY 0x1000u +#define ___GFP_MEMALLOC 0x2000u #define ___GFP_COMP 0x4000u #define ___GFP_ZERO 0x8000u #define ___GFP_NOMEMALLOC 0x10000u @@ -76,9 +77,14 @@ struct vm_area_struct; #define __GFP_REPEAT ((__force gfp_t)___GFP_REPEAT) /* See above */ #define __GFP_NOFAIL ((__force gfp_t)___GFP_NOFAIL) /* See above */ #define __GFP_NORETRY ((__force gfp_t)___GFP_NORETRY) /* See above */ +#define __GFP_MEMALLOC ((__force gfp_t)___GFP_MEMALLOC)/* Allow access to emergency reserves */ #define __GFP_COMP ((__force gfp_t)___GFP_COMP) /* Add compound page metadata */ #define __GFP_ZERO ((__force gfp_t)___GFP_ZERO) /* Return zeroed page on success */ -#define __GFP_NOMEMALLOC ((__force gfp_t)___GFP_NOMEMALLOC) /* Don't use emergency reserves */ +#define __GFP_NOMEMALLOC ((__force gfp_t)___GFP_NOMEMALLOC) /* Don't use emergency reserves. + * This takes precedence over the + * __GFP_MEMALLOC flag if both are + * set + */ #define __GFP_HARDWALL ((__force gfp_t)___GFP_HARDWALL) /* Enforce hardwall cpuset memory allocs */ #define __GFP_THISNODE ((__force gfp_t)___GFP_THISNODE)/* No fallback, no policies */ #define __GFP_RECLAIMABLE ((__force gfp_t)___GFP_RECLAIMABLE) /* Page is reclaimable */ @@ -129,7 +135,7 @@ struct vm_area_struct; /* Control page allocator reclaim behavior */ #define GFP_RECLAIM_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO|__GFP_FS|\ __GFP_NOWARN|__GFP_REPEAT|__GFP_NOFAIL|\ - __GFP_NORETRY|__GFP_NOMEMALLOC) + __GFP_NORETRY|__GFP_MEMALLOC|__GFP_NOMEMALLOC) /* Control slab gfp mask during early boot */ #define GFP_BOOT_MASK (__GFP_BITS_MASK & ~(__GFP_WAIT|__GFP_IO|__GFP_FS)) diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 375e79eb009b..bf7867200b95 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -55,7 +55,7 @@ struct page { pgoff_t index; /* Our offset within mapping. */ void *freelist; /* slub/slob first free object */ bool pfmemalloc; /* If set by the page allocator, - * ALLOC_PFMEMALLOC was set + * ALLOC_NO_WATERMARKS was set * and the low watermark was not * met implying that the system * is under some pressure. The diff --git a/include/trace/events/gfpflags.h b/include/trace/events/gfpflags.h index 9fe3a36646e9..d6fd8e5b14b7 100644 --- a/include/trace/events/gfpflags.h +++ b/include/trace/events/gfpflags.h @@ -30,6 +30,7 @@ {(unsigned long)__GFP_COMP, "GFP_COMP"}, \ {(unsigned long)__GFP_ZERO, "GFP_ZERO"}, \ {(unsigned long)__GFP_NOMEMALLOC, "GFP_NOMEMALLOC"}, \ + {(unsigned long)__GFP_MEMALLOC, "GFP_MEMALLOC"}, \ {(unsigned long)__GFP_HARDWALL, "GFP_HARDWALL"}, \ {(unsigned long)__GFP_THISNODE, "GFP_THISNODE"}, \ {(unsigned long)__GFP_RECLAIMABLE, "GFP_RECLAIMABLE"}, \ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 38e5be65f24e..8f65abeb9ad6 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1513,7 +1513,6 @@ failed: #define ALLOC_HARDER 0x10 /* try to alloc harder */ #define ALLOC_HIGH 0x20 /* __GFP_HIGH set */ #define ALLOC_CPUSET 0x40 /* check for correct cpuset */ -#define ALLOC_PFMEMALLOC 0x80 /* Caller has PF_MEMALLOC set */ #ifdef CONFIG_FAIL_PAGE_ALLOC @@ -2294,11 +2293,10 @@ gfp_to_alloc_flags(gfp_t gfp_mask) } else if (unlikely(rt_task(current)) && !in_interrupt()) alloc_flags |= ALLOC_HARDER; - if ((current->flags & PF_MEMALLOC) || - unlikely(test_thread_flag(TIF_MEMDIE))) { - alloc_flags |= ALLOC_PFMEMALLOC; - - if (likely(!(gfp_mask & __GFP_NOMEMALLOC)) && !in_interrupt()) + if (likely(!(gfp_mask & __GFP_NOMEMALLOC))) { + if (gfp_mask & __GFP_MEMALLOC) + alloc_flags |= ALLOC_NO_WATERMARKS; + else if (likely(!(gfp_mask & __GFP_NOMEMALLOC)) && !in_interrupt()) alloc_flags |= ALLOC_NO_WATERMARKS; } @@ -2307,7 +2305,7 @@ gfp_to_alloc_flags(gfp_t gfp_mask) bool gfp_pfmemalloc_allowed(gfp_t gfp_mask) { - return !!(gfp_to_alloc_flags(gfp_mask) & ALLOC_PFMEMALLOC); + return !!(gfp_to_alloc_flags(gfp_mask) & ALLOC_NO_WATERMARKS); } static inline struct page * @@ -2498,12 +2496,12 @@ nopage: return page; got_pg: /* - * page->pfmemalloc is set when the caller had PFMEMALLOC set or is - * been OOM killed. The expectation is that the caller is taking - * steps that will free more memory. The caller should avoid the - * page being used for !PFMEMALLOC purposes. + * page->pfmemalloc is set when the caller had PFMEMALLOC set, is + * been OOM killed or specified __GFP_MEMALLOC. The expectation is + * that the caller is taking steps that will free more memory. The + * caller should avoid the page being used for !PFMEMALLOC purposes. */ - page->pfmemalloc = !!(alloc_flags & ALLOC_PFMEMALLOC); + page->pfmemalloc = !!(alloc_flags & ALLOC_NO_WATERMARKS); if (kmemcheck_enabled) kmemcheck_pagealloc_alloc(page, order, gfp_mask); diff --git a/mm/slab.c b/mm/slab.c index 55d84a22ad96..77be18dab73c 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1884,7 +1884,7 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid) return NULL; } - /* Record if ALLOC_PFMEMALLOC was set when allocating the slab */ + /* Record if ALLOC_NO_WATERMARKS was set when allocating the slab */ if (unlikely(page->pfmemalloc)) pfmemalloc_active = true; -- cgit v1.2.3 From 907aed48f65efeecf91575397e3d79335d93a466 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:07 -0700 Subject: mm: allow PF_MEMALLOC from softirq context This is needed to allow network softirq packet processing to make use of PF_MEMALLOC. Currently softirq context cannot use PF_MEMALLOC due to it not being associated with a task, and therefore not having task flags to fiddle with - thus the gfp to alloc flag mapping ignores the task flags when in interrupts (hard or soft) context. Allowing softirqs to make use of PF_MEMALLOC therefore requires some trickery. This patch borrows the task flags from whatever process happens to be preempted by the softirq. It then modifies the gfp to alloc flags mapping to not exclude task flags in softirq context, and modify the softirq code to save, clear and restore the PF_MEMALLOC flag. The save and clear, ensures the preempted task's PF_MEMALLOC flag doesn't leak into the softirq. The restore ensures a softirq's PF_MEMALLOC flag cannot leak back into the preempted process. This should be safe due to the following reasons Softirqs can run on multiple CPUs sure but the same task should not be executing the same softirq code. Neither should the softirq handler be preempted by any other softirq handler so the flags should not leak to an unrelated softirq. Softirqs re-enable hardware interrupts in __do_softirq() so can be preempted by hardware interrupts so PF_MEMALLOC is inherited by the hard IRQ. However, this is similar to a process in reclaim being preempted by a hardirq. While PF_MEMALLOC is set, gfp_to_alloc_flags() distinguishes between hard and soft irqs and avoids giving a hardirq the ALLOC_NO_WATERMARKS flag. If the softirq is deferred to ksoftirq then its flags may be used instead of a normal tasks but as the softirq cannot be preempted, the PF_MEMALLOC flag does not leak to other code by accident. [davem@davemloft.net: Document why PF_MEMALLOC is safe] Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Cc: David Miller Cc: Neil Brown Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 7 +++++++ kernel/softirq.c | 9 +++++++++ mm/page_alloc.c | 6 +++++- 3 files changed, 21 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 865725adb9d3..c147e7024f11 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1894,6 +1894,13 @@ static inline void rcu_copy_process(struct task_struct *p) #endif +static inline void tsk_restore_flags(struct task_struct *task, + unsigned long orig_flags, unsigned long flags) +{ + task->flags &= ~flags; + task->flags |= orig_flags & flags; +} + #ifdef CONFIG_SMP extern void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask); diff --git a/kernel/softirq.c b/kernel/softirq.c index 671f9594e368..b73e681df09e 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -210,6 +210,14 @@ asmlinkage void __do_softirq(void) __u32 pending; int max_restart = MAX_SOFTIRQ_RESTART; int cpu; + unsigned long old_flags = current->flags; + + /* + * Mask out PF_MEMALLOC s current task context is borrowed for the + * softirq. A softirq handled such as network RX might set PF_MEMALLOC + * again if the socket is related to swap + */ + current->flags &= ~PF_MEMALLOC; pending = local_softirq_pending(); account_system_vtime(current); @@ -265,6 +273,7 @@ restart: account_system_vtime(current); __local_bh_enable(SOFTIRQ_OFFSET); + tsk_restore_flags(current, old_flags, PF_MEMALLOC); } #ifndef __ARCH_HAS_DO_SOFTIRQ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 8f65abeb9ad6..cd5390f2f18d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2296,7 +2296,11 @@ gfp_to_alloc_flags(gfp_t gfp_mask) if (likely(!(gfp_mask & __GFP_NOMEMALLOC))) { if (gfp_mask & __GFP_MEMALLOC) alloc_flags |= ALLOC_NO_WATERMARKS; - else if (likely(!(gfp_mask & __GFP_NOMEMALLOC)) && !in_interrupt()) + else if (in_serving_softirq() && (current->flags & PF_MEMALLOC)) + alloc_flags |= ALLOC_NO_WATERMARKS; + else if (!in_interrupt() && + ((current->flags & PF_MEMALLOC) || + unlikely(test_thread_flag(TIF_MEMDIE)))) alloc_flags |= ALLOC_NO_WATERMARKS; } -- cgit v1.2.3 From 99a1dec70d5acbd8c6b3928cdebb4a2d1da676c8 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:14 -0700 Subject: net: introduce sk_gfp_atomic() to allow addition of GFP flags depending on the individual socket Introduce sk_gfp_atomic(), this function allows to inject sock specific flags to each sock related allocation. It is only used on allocation paths that may be required for writing pages back to network storage. [davem@davemloft.net: Use sk_gfp_atomic only when necessary] Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Acked-by: David S. Miller Cc: Neil Brown Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/net/sock.h | 5 +++++ net/ipv4/tcp_output.c | 12 +++++++----- net/ipv6/tcp_ipv6.c | 8 +++++--- 3 files changed, 17 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index cee528c119ca..11ccde65c4cf 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -658,6 +658,11 @@ static inline bool sock_flag(const struct sock *sk, enum sock_flags flag) return test_bit(flag, &sk->sk_flags); } +static inline gfp_t sk_gfp_atomic(struct sock *sk, gfp_t gfp_mask) +{ + return GFP_ATOMIC; +} + static inline void sk_acceptq_removed(struct sock *sk) { sk->sk_ack_backlog--; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 33cd065cfbd8..3f1bcff0b10b 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2045,7 +2045,8 @@ void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss, if (unlikely(sk->sk_state == TCP_CLOSE)) return; - if (tcp_write_xmit(sk, cur_mss, nonagle, 0, GFP_ATOMIC)) + if (tcp_write_xmit(sk, cur_mss, nonagle, 0, + sk_gfp_atomic(sk, GFP_ATOMIC))) tcp_check_probe_timer(sk); } @@ -2666,7 +2667,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, if (cvp != NULL && cvp->s_data_constant && cvp->s_data_desired) s_data_desired = cvp->s_data_desired; - skb = alloc_skb(MAX_TCP_HEADER + 15 + s_data_desired, GFP_ATOMIC); + skb = alloc_skb(MAX_TCP_HEADER + 15 + s_data_desired, + sk_gfp_atomic(sk, GFP_ATOMIC)); if (unlikely(!skb)) { dst_release(dst); return NULL; @@ -3064,7 +3066,7 @@ void tcp_send_ack(struct sock *sk) * tcp_transmit_skb() will set the ownership to this * sock. */ - buff = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC); + buff = alloc_skb(MAX_TCP_HEADER, sk_gfp_atomic(sk, GFP_ATOMIC)); if (buff == NULL) { inet_csk_schedule_ack(sk); inet_csk(sk)->icsk_ack.ato = TCP_ATO_MIN; @@ -3079,7 +3081,7 @@ void tcp_send_ack(struct sock *sk) /* Send it off, this clears delayed acks for us. */ TCP_SKB_CB(buff)->when = tcp_time_stamp; - tcp_transmit_skb(sk, buff, 0, GFP_ATOMIC); + tcp_transmit_skb(sk, buff, 0, sk_gfp_atomic(sk, GFP_ATOMIC)); } /* This routine sends a packet with an out of date sequence @@ -3099,7 +3101,7 @@ static int tcp_xmit_probe_skb(struct sock *sk, int urgent) struct sk_buff *skb; /* We don't queue it, tcp_transmit_skb() sets ownership. */ - skb = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC); + skb = alloc_skb(MAX_TCP_HEADER, sk_gfp_atomic(sk, GFP_ATOMIC)); if (skb == NULL) return -1; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 61c7b6d83176..c66b90f71c9b 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1299,7 +1299,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, /* Clone pktoptions received with SYN */ newnp->pktoptions = NULL; if (treq->pktopts != NULL) { - newnp->pktoptions = skb_clone(treq->pktopts, GFP_ATOMIC); + newnp->pktoptions = skb_clone(treq->pktopts, + sk_gfp_atomic(sk, GFP_ATOMIC)); consume_skb(treq->pktopts); treq->pktopts = NULL; if (newnp->pktoptions) @@ -1349,7 +1350,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, * across. Shucks. */ tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newnp->daddr, - AF_INET6, key->key, key->keylen, GFP_ATOMIC); + AF_INET6, key->key, key->keylen, + sk_gfp_atomic(sk, GFP_ATOMIC)); } #endif @@ -1442,7 +1444,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) --ANK (980728) */ if (np->rxopt.all) - opt_skb = skb_clone(skb, GFP_ATOMIC); + opt_skb = skb_clone(skb, sk_gfp_atomic(sk, GFP_ATOMIC)); if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ sock_rps_save_rxhash(sk, skb); -- cgit v1.2.3 From 7cb0240492caea2f6467f827313478f41877e6ef Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:16 -0700 Subject: netvm: allow the use of __GFP_MEMALLOC by specific sockets Allow specific sockets to be tagged SOCK_MEMALLOC and use __GFP_MEMALLOC for their allocations. These sockets will be able to go below watermarks and allocate from the emergency reserve. Such sockets are to be used to service the VM (iow. to swap over). They must be handled kernel side, exposing such a socket to user-space is a bug. There is a risk that the reserves be depleted so for now, the administrator is responsible for increasing min_free_kbytes as necessary to prevent deadlock for their workloads. [a.p.zijlstra@chello.nl: Original patches] Signed-off-by: Mel Gorman Acked-by: David S. Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/net/sock.h | 5 ++++- net/core/sock.c | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 11ccde65c4cf..bfa7d20e6646 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -621,6 +621,7 @@ enum sock_flags { SOCK_RCVTSTAMPNS, /* %SO_TIMESTAMPNS setting */ SOCK_LOCALROUTE, /* route locally only, %SO_DONTROUTE setting */ SOCK_QUEUE_SHRUNK, /* write queue has been shrunk recently */ + SOCK_MEMALLOC, /* VM depends on this socket for swapping */ SOCK_TIMESTAMPING_TX_HARDWARE, /* %SOF_TIMESTAMPING_TX_HARDWARE */ SOCK_TIMESTAMPING_TX_SOFTWARE, /* %SOF_TIMESTAMPING_TX_SOFTWARE */ SOCK_TIMESTAMPING_RX_HARDWARE, /* %SOF_TIMESTAMPING_RX_HARDWARE */ @@ -660,7 +661,7 @@ static inline bool sock_flag(const struct sock *sk, enum sock_flags flag) static inline gfp_t sk_gfp_atomic(struct sock *sk, gfp_t gfp_mask) { - return GFP_ATOMIC; + return GFP_ATOMIC | (sk->sk_allocation & __GFP_MEMALLOC); } static inline void sk_acceptq_removed(struct sock *sk) @@ -803,6 +804,8 @@ extern int sk_stream_wait_memory(struct sock *sk, long *timeo_p); extern void sk_stream_wait_close(struct sock *sk, long timeo_p); extern int sk_stream_error(struct sock *sk, int flags, int err); extern void sk_stream_kill_queues(struct sock *sk); +extern void sk_set_memalloc(struct sock *sk); +extern void sk_clear_memalloc(struct sock *sk); extern int sk_wait_data(struct sock *sk, long *timeo); diff --git a/net/core/sock.c b/net/core/sock.c index a67b06280e4c..3617f652f6b0 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -271,6 +271,28 @@ __u32 sysctl_rmem_default __read_mostly = SK_RMEM_MAX; int sysctl_optmem_max __read_mostly = sizeof(unsigned long)*(2*UIO_MAXIOV+512); EXPORT_SYMBOL(sysctl_optmem_max); +/** + * sk_set_memalloc - sets %SOCK_MEMALLOC + * @sk: socket to set it on + * + * Set %SOCK_MEMALLOC on a socket for access to emergency reserves. + * It's the responsibility of the admin to adjust min_free_kbytes + * to meet the requirements + */ +void sk_set_memalloc(struct sock *sk) +{ + sock_set_flag(sk, SOCK_MEMALLOC); + sk->sk_allocation |= __GFP_MEMALLOC; +} +EXPORT_SYMBOL_GPL(sk_set_memalloc); + +void sk_clear_memalloc(struct sock *sk) +{ + sock_reset_flag(sk, SOCK_MEMALLOC); + sk->sk_allocation &= ~__GFP_MEMALLOC; +} +EXPORT_SYMBOL_GPL(sk_clear_memalloc); + #if defined(CONFIG_CGROUPS) #if !defined(CONFIG_NET_CLS_CGROUP) int net_cls_subsys_id = -1; -- cgit v1.2.3 From c93bdd0e03e848555d144eb44a1f275b871a8dd5 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:19 -0700 Subject: netvm: allow skb allocation to use PFMEMALLOC reserves Change the skb allocation API to indicate RX usage and use this to fall back to the PFMEMALLOC reserve when needed. SKBs allocated from the reserve are tagged in skb->pfmemalloc. If an SKB is allocated from the reserve and the socket is later found to be unrelated to page reclaim, the packet is dropped so that the memory remains available for page reclaim. Network protocols are expected to recover from this packet loss. [a.p.zijlstra@chello.nl: Ideas taken from various patches] [davem@davemloft.net: Use static branches, coding style corrections] [sebastian@breakpoint.cc: Avoid unnecessary cast, fix !CONFIG_NET build] Signed-off-by: Mel Gorman Acked-by: David S. Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/gfp.h | 3 ++ include/linux/skbuff.h | 14 +++++- include/net/sock.h | 15 ++++++ mm/internal.h | 3 -- net/core/filter.c | 8 ++++ net/core/skbuff.c | 124 +++++++++++++++++++++++++++++++++++++++---------- net/core/sock.c | 5 ++ 7 files changed, 142 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/linux/gfp.h b/include/linux/gfp.h index cbd7400e5862..4883f393f50a 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -385,6 +385,9 @@ void drain_local_pages(void *dummy); */ extern gfp_t gfp_allowed_mask; +/* Returns true if the gfp_mask allows use of ALLOC_NO_WATERMARK */ +bool gfp_pfmemalloc_allowed(gfp_t gfp_mask); + extern void pm_restrict_gfp_mask(void); extern void pm_restore_gfp_mask(void); diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index d205c4be7f5b..0336f02e3667 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -462,6 +462,7 @@ struct sk_buff { #ifdef CONFIG_IPV6_NDISC_NODETYPE __u8 ndisc_nodetype:2; #endif + __u8 pfmemalloc:1; __u8 ooo_okay:1; __u8 l4_rxhash:1; __u8 wifi_acked_valid:1; @@ -502,6 +503,15 @@ struct sk_buff { #include +#define SKB_ALLOC_FCLONE 0x01 +#define SKB_ALLOC_RX 0x02 + +/* Returns true if the skb was allocated from PFMEMALLOC reserves */ +static inline bool skb_pfmemalloc(const struct sk_buff *skb) +{ + return unlikely(skb->pfmemalloc); +} + /* * skb might have a dst pointer attached, refcounted or not. * _skb_refdst low order bit is set if refcount was _not_ taken @@ -565,7 +575,7 @@ extern bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, bool *fragstolen, int *delta_truesize); extern struct sk_buff *__alloc_skb(unsigned int size, - gfp_t priority, int fclone, int node); + gfp_t priority, int flags, int node); extern struct sk_buff *build_skb(void *data, unsigned int frag_size); static inline struct sk_buff *alloc_skb(unsigned int size, gfp_t priority) @@ -576,7 +586,7 @@ static inline struct sk_buff *alloc_skb(unsigned int size, static inline struct sk_buff *alloc_skb_fclone(unsigned int size, gfp_t priority) { - return __alloc_skb(size, priority, 1, NUMA_NO_NODE); + return __alloc_skb(size, priority, SKB_ALLOC_FCLONE, NUMA_NO_NODE); } extern void skb_recycle(struct sk_buff *skb); diff --git a/include/net/sock.h b/include/net/sock.h index bfa7d20e6646..81198632ac2a 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -659,6 +659,21 @@ static inline bool sock_flag(const struct sock *sk, enum sock_flags flag) return test_bit(flag, &sk->sk_flags); } +#ifdef CONFIG_NET +extern struct static_key memalloc_socks; +static inline int sk_memalloc_socks(void) +{ + return static_key_false(&memalloc_socks); +} +#else + +static inline int sk_memalloc_socks(void) +{ + return 0; +} + +#endif + static inline gfp_t sk_gfp_atomic(struct sock *sk, gfp_t gfp_mask) { return GFP_ATOMIC | (sk->sk_allocation & __GFP_MEMALLOC); diff --git a/mm/internal.h b/mm/internal.h index eb76b67890d0..3314f79d775a 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -279,9 +279,6 @@ static inline struct page *mem_map_next(struct page *iter, #define __paginginit __init #endif -/* Returns true if the gfp_mask allows use of ALLOC_NO_WATERMARK */ -bool gfp_pfmemalloc_allowed(gfp_t gfp_mask); - /* Memory initialisation debug and verification */ enum mminit_level { MMINIT_WARNING, diff --git a/net/core/filter.c b/net/core/filter.c index d4ce2dc712e3..907efd27ec77 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -83,6 +83,14 @@ int sk_filter(struct sock *sk, struct sk_buff *skb) int err; struct sk_filter *filter; + /* + * If the skb was allocated from pfmemalloc reserves, only + * allow SOCK_MEMALLOC sockets to use it as this socket is + * helping free memory + */ + if (skb_pfmemalloc(skb) && !sock_flag(sk, SOCK_MEMALLOC)) + return -ENOMEM; + err = security_sock_rcv_skb(sk, skb); if (err) return err; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 368f65c15e4f..fe00d1208167 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -145,6 +145,43 @@ static void skb_under_panic(struct sk_buff *skb, int sz, void *here) BUG(); } + +/* + * kmalloc_reserve is a wrapper around kmalloc_node_track_caller that tells + * the caller if emergency pfmemalloc reserves are being used. If it is and + * the socket is later found to be SOCK_MEMALLOC then PFMEMALLOC reserves + * may be used. Otherwise, the packet data may be discarded until enough + * memory is free + */ +#define kmalloc_reserve(size, gfp, node, pfmemalloc) \ + __kmalloc_reserve(size, gfp, node, _RET_IP_, pfmemalloc) +void *__kmalloc_reserve(size_t size, gfp_t flags, int node, unsigned long ip, + bool *pfmemalloc) +{ + void *obj; + bool ret_pfmemalloc = false; + + /* + * Try a regular allocation, when that fails and we're not entitled + * to the reserves, fail. + */ + obj = kmalloc_node_track_caller(size, + flags | __GFP_NOMEMALLOC | __GFP_NOWARN, + node); + if (obj || !(gfp_pfmemalloc_allowed(flags))) + goto out; + + /* Try again but now we are using pfmemalloc reserves */ + ret_pfmemalloc = true; + obj = kmalloc_node_track_caller(size, flags, node); + +out: + if (pfmemalloc) + *pfmemalloc = ret_pfmemalloc; + + return obj; +} + /* Allocate a new skbuff. We do this ourselves so we can fill in a few * 'private' fields and also do memory statistics to find all the * [BEEP] leaks. @@ -155,8 +192,10 @@ static void skb_under_panic(struct sk_buff *skb, int sz, void *here) * __alloc_skb - allocate a network buffer * @size: size to allocate * @gfp_mask: allocation mask - * @fclone: allocate from fclone cache instead of head cache - * and allocate a cloned (child) skb + * @flags: If SKB_ALLOC_FCLONE is set, allocate from fclone cache + * instead of head cache and allocate a cloned (child) skb. + * If SKB_ALLOC_RX is set, __GFP_MEMALLOC will be used for + * allocations in case the data is required for writeback * @node: numa node to allocate memory on * * Allocate a new &sk_buff. The returned buffer has no headroom and a @@ -167,14 +206,19 @@ static void skb_under_panic(struct sk_buff *skb, int sz, void *here) * %GFP_ATOMIC. */ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, - int fclone, int node) + int flags, int node) { struct kmem_cache *cache; struct skb_shared_info *shinfo; struct sk_buff *skb; u8 *data; + bool pfmemalloc; - cache = fclone ? skbuff_fclone_cache : skbuff_head_cache; + cache = (flags & SKB_ALLOC_FCLONE) + ? skbuff_fclone_cache : skbuff_head_cache; + + if (sk_memalloc_socks() && (flags & SKB_ALLOC_RX)) + gfp_mask |= __GFP_MEMALLOC; /* Get the HEAD */ skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node); @@ -189,7 +233,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, */ size = SKB_DATA_ALIGN(size); size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - data = kmalloc_node_track_caller(size, gfp_mask, node); + data = kmalloc_reserve(size, gfp_mask, node, &pfmemalloc); if (!data) goto nodata; /* kmalloc(size) might give us more room than requested. @@ -207,6 +251,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, memset(skb, 0, offsetof(struct sk_buff, tail)); /* Account for allocated memory : skb + skb->head */ skb->truesize = SKB_TRUESIZE(size); + skb->pfmemalloc = pfmemalloc; atomic_set(&skb->users, 1); skb->head = data; skb->data = data; @@ -222,7 +267,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, atomic_set(&shinfo->dataref, 1); kmemcheck_annotate_variable(shinfo->destructor_arg); - if (fclone) { + if (flags & SKB_ALLOC_FCLONE) { struct sk_buff *child = skb + 1; atomic_t *fclone_ref = (atomic_t *) (child + 1); @@ -232,6 +277,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, atomic_set(fclone_ref, 1); child->fclone = SKB_FCLONE_UNAVAILABLE; + child->pfmemalloc = pfmemalloc; } out: return skb; @@ -302,14 +348,7 @@ static DEFINE_PER_CPU(struct netdev_alloc_cache, netdev_alloc_cache); #define NETDEV_PAGECNT_BIAS (PAGE_SIZE / SMP_CACHE_BYTES) -/** - * netdev_alloc_frag - allocate a page fragment - * @fragsz: fragment size - * - * Allocates a frag from a page for receive buffer. - * Uses GFP_ATOMIC allocations. - */ -void *netdev_alloc_frag(unsigned int fragsz) +static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask) { struct netdev_alloc_cache *nc; void *data = NULL; @@ -319,7 +358,7 @@ void *netdev_alloc_frag(unsigned int fragsz) nc = &__get_cpu_var(netdev_alloc_cache); if (unlikely(!nc->page)) { refill: - nc->page = alloc_page(GFP_ATOMIC | __GFP_COLD); + nc->page = alloc_page(gfp_mask); if (unlikely(!nc->page)) goto end; recycle: @@ -343,6 +382,18 @@ end: local_irq_restore(flags); return data; } + +/** + * netdev_alloc_frag - allocate a page fragment + * @fragsz: fragment size + * + * Allocates a frag from a page for receive buffer. + * Uses GFP_ATOMIC allocations. + */ +void *netdev_alloc_frag(unsigned int fragsz) +{ + return __netdev_alloc_frag(fragsz, GFP_ATOMIC | __GFP_COLD); +} EXPORT_SYMBOL(netdev_alloc_frag); /** @@ -366,7 +417,12 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); if (fragsz <= PAGE_SIZE && !(gfp_mask & (__GFP_WAIT | GFP_DMA))) { - void *data = netdev_alloc_frag(fragsz); + void *data; + + if (sk_memalloc_socks()) + gfp_mask |= __GFP_MEMALLOC; + + data = __netdev_alloc_frag(fragsz, gfp_mask); if (likely(data)) { skb = build_skb(data, fragsz); @@ -374,7 +430,8 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, put_page(virt_to_head_page(data)); } } else { - skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, 0, NUMA_NO_NODE); + skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, + SKB_ALLOC_RX, NUMA_NO_NODE); } if (likely(skb)) { skb_reserve(skb, NET_SKB_PAD); @@ -656,6 +713,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) #if IS_ENABLED(CONFIG_IP_VS) new->ipvs_property = old->ipvs_property; #endif + new->pfmemalloc = old->pfmemalloc; new->protocol = old->protocol; new->mark = old->mark; new->skb_iif = old->skb_iif; @@ -814,6 +872,9 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask) n->fclone = SKB_FCLONE_CLONE; atomic_inc(fclone_ref); } else { + if (skb_pfmemalloc(skb)) + gfp_mask |= __GFP_MEMALLOC; + n = kmem_cache_alloc(skbuff_head_cache, gfp_mask); if (!n) return NULL; @@ -850,6 +911,13 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type; } +static inline int skb_alloc_rx_flag(const struct sk_buff *skb) +{ + if (skb_pfmemalloc(skb)) + return SKB_ALLOC_RX; + return 0; +} + /** * skb_copy - create private copy of an sk_buff * @skb: buffer to copy @@ -871,7 +939,8 @@ struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask) { int headerlen = skb_headroom(skb); unsigned int size = skb_end_offset(skb) + skb->data_len; - struct sk_buff *n = alloc_skb(size, gfp_mask); + struct sk_buff *n = __alloc_skb(size, gfp_mask, + skb_alloc_rx_flag(skb), NUMA_NO_NODE); if (!n) return NULL; @@ -906,7 +975,8 @@ EXPORT_SYMBOL(skb_copy); struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask) { unsigned int size = skb_headlen(skb) + headroom; - struct sk_buff *n = alloc_skb(size, gfp_mask); + struct sk_buff *n = __alloc_skb(size, gfp_mask, + skb_alloc_rx_flag(skb), NUMA_NO_NODE); if (!n) goto out; @@ -979,8 +1049,10 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, size = SKB_DATA_ALIGN(size); - data = kmalloc(size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), - gfp_mask); + if (skb_pfmemalloc(skb)) + gfp_mask |= __GFP_MEMALLOC; + data = kmalloc_reserve(size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), + gfp_mask, NUMA_NO_NODE, NULL); if (!data) goto nodata; size = SKB_WITH_OVERHEAD(ksize(data)); @@ -1092,8 +1164,9 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb, /* * Allocate the copy buffer */ - struct sk_buff *n = alloc_skb(newheadroom + skb->len + newtailroom, - gfp_mask); + struct sk_buff *n = __alloc_skb(newheadroom + skb->len + newtailroom, + gfp_mask, skb_alloc_rx_flag(skb), + NUMA_NO_NODE); int oldheadroom = skb_headroom(skb); int head_copy_len, head_copy_off; int off; @@ -2775,8 +2848,9 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) skb_release_head_state(nskb); __skb_push(nskb, doffset); } else { - nskb = alloc_skb(hsize + doffset + headroom, - GFP_ATOMIC); + nskb = __alloc_skb(hsize + doffset + headroom, + GFP_ATOMIC, skb_alloc_rx_flag(skb), + NUMA_NO_NODE); if (unlikely(!nskb)) goto err; diff --git a/net/core/sock.c b/net/core/sock.c index 3617f652f6b0..c8c5816289fe 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -271,6 +271,9 @@ __u32 sysctl_rmem_default __read_mostly = SK_RMEM_MAX; int sysctl_optmem_max __read_mostly = sizeof(unsigned long)*(2*UIO_MAXIOV+512); EXPORT_SYMBOL(sysctl_optmem_max); +struct static_key memalloc_socks = STATIC_KEY_INIT_FALSE; +EXPORT_SYMBOL_GPL(memalloc_socks); + /** * sk_set_memalloc - sets %SOCK_MEMALLOC * @sk: socket to set it on @@ -283,6 +286,7 @@ void sk_set_memalloc(struct sock *sk) { sock_set_flag(sk, SOCK_MEMALLOC); sk->sk_allocation |= __GFP_MEMALLOC; + static_key_slow_inc(&memalloc_socks); } EXPORT_SYMBOL_GPL(sk_set_memalloc); @@ -290,6 +294,7 @@ void sk_clear_memalloc(struct sock *sk) { sock_reset_flag(sk, SOCK_MEMALLOC); sk->sk_allocation &= ~__GFP_MEMALLOC; + static_key_slow_dec(&memalloc_socks); } EXPORT_SYMBOL_GPL(sk_clear_memalloc); -- cgit v1.2.3 From c48a11c7ad2623b99bbd6859b0b4234e7f11176f Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:23 -0700 Subject: netvm: propagate page->pfmemalloc to skb The skb->pfmemalloc flag gets set to true iff during the slab allocation of data in __alloc_skb that the the PFMEMALLOC reserves were used. If the packet is fragmented, it is possible that pages will be allocated from the PFMEMALLOC reserve without propagating this information to the skb. This patch propagates page->pfmemalloc from pages allocated for fragments to the skb. Signed-off-by: Mel Gorman Acked-by: David S. Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/skbuff.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 0336f02e3667..b814bb8fd5ab 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1247,6 +1247,17 @@ static inline void __skb_fill_page_desc(struct sk_buff *skb, int i, { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + /* + * Propagate page->pfmemalloc to the skb if we can. The problem is + * that not all callers have unique ownership of the page. If + * pfmemalloc is set, we check the mapping as a mapping implies + * page->index is set (index and pfmemalloc share space). + * If it's a valid mapping, we cannot use page->pfmemalloc but we + * do not lose pfmemalloc information as the pages would not be + * allocated using __GFP_MEMALLOC. + */ + if (page->pfmemalloc && !page->mapping) + skb->pfmemalloc = true; frag->page.p = page; frag->page_offset = off; skb_frag_size_set(frag, size); -- cgit v1.2.3 From 0614002bb5f7411e61ffa0dfe5be1f2c84df3da3 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:24 -0700 Subject: netvm: propagate page->pfmemalloc from skb_alloc_page to skb The skb->pfmemalloc flag gets set to true iff during the slab allocation of data in __alloc_skb that the the PFMEMALLOC reserves were used. If page splitting is used, it is possible that pages will be allocated from the PFMEMALLOC reserve without propagating this information to the skb. This patch propagates page->pfmemalloc from pages allocated for fragments to the skb. It works by reintroducing and expanding the skb_alloc_page() API to take an skb. If the page was allocated from pfmemalloc reserves, it is automatically copied. If the driver allocates the page before the skb, it should call skb_propagate_pfmemalloc() after the skb is allocated to ensure the flag is copied properly. Failure to do so is not critical. The resulting driver may perform slower if it is used for swap-over-NBD or swap-over-NFS but it should not result in failure. [davem@davemloft.net: API rename and consistency] Signed-off-by: Mel Gorman Acked-by: David S. Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/net/ethernet/chelsio/cxgb4/sge.c | 2 +- drivers/net/ethernet/chelsio/cxgb4vf/sge.c | 2 +- drivers/net/ethernet/intel/igb/igb_main.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 4 +- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 1 - drivers/net/usb/cdc-phonet.c | 2 +- drivers/usb/gadget/f_phonet.c | 2 +- include/linux/skbuff.h | 55 +++++++++++++++++++++++ 8 files changed, 62 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 8596acaa402b..d49933ed551f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -528,7 +528,7 @@ static unsigned int refill_fl(struct adapter *adap, struct sge_fl *q, int n, #endif while (n--) { - pg = alloc_page(gfp); + pg = __skb_alloc_page(gfp, NULL); if (unlikely(!pg)) { q->alloc_failed++; break; diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index f2d1ecdcaf98..8877fbfefb63 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -653,7 +653,7 @@ static unsigned int refill_fl(struct adapter *adapter, struct sge_fl *fl, alloc_small_pages: while (n--) { - page = alloc_page(gfp | __GFP_NOWARN | __GFP_COLD); + page = __skb_alloc_page(gfp | __GFP_NOWARN, NULL); if (unlikely(!page)) { fl->alloc_failed++; break; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 1050411e7ca3..b7c2d5050572 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -6235,7 +6235,7 @@ static bool igb_alloc_mapped_page(struct igb_ring *rx_ring, return true; if (!page) { - page = alloc_page(GFP_ATOMIC | __GFP_COLD); + page = __skb_alloc_page(GFP_ATOMIC, bi->skb); bi->page = page; if (unlikely(!page)) { rx_ring->rx_stats.alloc_failed++; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index c709eae58c63..4326f74f7137 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1141,8 +1141,8 @@ static bool ixgbe_alloc_mapped_page(struct ixgbe_ring *rx_ring, /* alloc new page for storage */ if (likely(!page)) { - page = alloc_pages(GFP_ATOMIC | __GFP_COLD | __GFP_COMP, - ixgbe_rx_pg_order(rx_ring)); + page = __skb_alloc_pages(GFP_ATOMIC | __GFP_COLD | __GFP_COMP, + bi->skb, ixgbe_rx_pg_order(rx_ring)); if (unlikely(!page)) { rx_ring->rx_stats.alloc_rx_page_failed++; return false; diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 3f9841d619ad..60ef64587412 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -352,7 +352,6 @@ static void ixgbevf_alloc_rx_buffers(struct ixgbevf_adapter *adapter, adapter->alloc_rx_buff_failed++; goto no_buffers; } - bi->skb = skb; } if (!bi->dma) { diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index 187c144c5e5b..64610048ce87 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -130,7 +130,7 @@ static int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags) struct page *page; int err; - page = alloc_page(gfp_flags); + page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL); if (!page) return -ENOMEM; diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index 965a6293206a..8ee9268fe253 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -301,7 +301,7 @@ pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags) struct page *page; int err; - page = alloc_page(gfp_flags); + page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL); if (!page) return -ENOMEM; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index b814bb8fd5ab..7632c87da2c9 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1774,6 +1774,61 @@ static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev, return __netdev_alloc_skb_ip_align(dev, length, GFP_ATOMIC); } +/* + * __skb_alloc_page - allocate pages for ps-rx on a skb and preserve pfmemalloc data + * @gfp_mask: alloc_pages_node mask. Set __GFP_NOMEMALLOC if not for network packet RX + * @skb: skb to set pfmemalloc on if __GFP_MEMALLOC is used + * @order: size of the allocation + * + * Allocate a new page. + * + * %NULL is returned if there is no free memory. +*/ +static inline struct page *__skb_alloc_pages(gfp_t gfp_mask, + struct sk_buff *skb, + unsigned int order) +{ + struct page *page; + + gfp_mask |= __GFP_COLD; + + if (!(gfp_mask & __GFP_NOMEMALLOC)) + gfp_mask |= __GFP_MEMALLOC; + + page = alloc_pages_node(NUMA_NO_NODE, gfp_mask, order); + if (skb && page && page->pfmemalloc) + skb->pfmemalloc = true; + + return page; +} + +/** + * __skb_alloc_page - allocate a page for ps-rx for a given skb and preserve pfmemalloc data + * @gfp_mask: alloc_pages_node mask. Set __GFP_NOMEMALLOC if not for network packet RX + * @skb: skb to set pfmemalloc on if __GFP_MEMALLOC is used + * + * Allocate a new page. + * + * %NULL is returned if there is no free memory. + */ +static inline struct page *__skb_alloc_page(gfp_t gfp_mask, + struct sk_buff *skb) +{ + return __skb_alloc_pages(gfp_mask, skb, 0); +} + +/** + * skb_propagate_pfmemalloc - Propagate pfmemalloc if skb is allocated after RX page + * @page: The page that was allocated from skb_alloc_page + * @skb: The skb that may need pfmemalloc set + */ +static inline void skb_propagate_pfmemalloc(struct page *page, + struct sk_buff *skb) +{ + if (page && page->pfmemalloc) + skb->pfmemalloc = true; +} + /** * skb_frag_page - retrieve the page refered to by a paged fragment * @frag: the paged fragment -- cgit v1.2.3 From b4b9e3558508980fc0cd161a545ffb55a1f13ee9 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:26 -0700 Subject: netvm: set PF_MEMALLOC as appropriate during SKB processing In order to make sure pfmemalloc packets receive all memory needed to proceed, ensure processing of pfmemalloc SKBs happens under PF_MEMALLOC. This is limited to a subset of protocols that are expected to be used for writing to swap. Taps are not allowed to use PF_MEMALLOC as these are expected to communicate with userspace processes which could be paged out. [a.p.zijlstra@chello.nl: Ideas taken from various patches] [jslaby@suse.cz: Lock imbalance fix] Signed-off-by: Mel Gorman Acked-by: David S. Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/net/sock.h | 5 +++++ net/core/dev.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ net/core/sock.c | 16 ++++++++++++++++ 3 files changed, 68 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 81198632ac2a..43a470d40d76 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -754,8 +754,13 @@ static inline __must_check int sk_add_backlog(struct sock *sk, struct sk_buff *s return 0; } +extern int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb); + static inline int sk_backlog_rcv(struct sock *sk, struct sk_buff *skb) { + if (sk_memalloc_socks() && skb_pfmemalloc(skb)) + return __sk_backlog_rcv(sk, skb); + return sk->sk_backlog_rcv(sk, skb); } diff --git a/net/core/dev.c b/net/core/dev.c index 0ebaea16632f..ce132443d5d1 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3155,6 +3155,23 @@ void netdev_rx_handler_unregister(struct net_device *dev) } EXPORT_SYMBOL_GPL(netdev_rx_handler_unregister); +/* + * Limit the use of PFMEMALLOC reserves to those protocols that implement + * the special handling of PFMEMALLOC skbs. + */ +static bool skb_pfmemalloc_protocol(struct sk_buff *skb) +{ + switch (skb->protocol) { + case __constant_htons(ETH_P_ARP): + case __constant_htons(ETH_P_IP): + case __constant_htons(ETH_P_IPV6): + case __constant_htons(ETH_P_8021Q): + return true; + default: + return false; + } +} + static int __netif_receive_skb(struct sk_buff *skb) { struct packet_type *ptype, *pt_prev; @@ -3164,14 +3181,27 @@ static int __netif_receive_skb(struct sk_buff *skb) bool deliver_exact = false; int ret = NET_RX_DROP; __be16 type; + unsigned long pflags = current->flags; net_timestamp_check(!netdev_tstamp_prequeue, skb); trace_netif_receive_skb(skb); + /* + * PFMEMALLOC skbs are special, they should + * - be delivered to SOCK_MEMALLOC sockets only + * - stay away from userspace + * - have bounded memory usage + * + * Use PF_MEMALLOC as this saves us from propagating the allocation + * context down to all allocation sites. + */ + if (sk_memalloc_socks() && skb_pfmemalloc(skb)) + current->flags |= PF_MEMALLOC; + /* if we've gotten here through NAPI, check netpoll */ if (netpoll_receive_skb(skb)) - return NET_RX_DROP; + goto out; orig_dev = skb->dev; @@ -3191,7 +3221,7 @@ another_round: if (skb->protocol == cpu_to_be16(ETH_P_8021Q)) { skb = vlan_untag(skb); if (unlikely(!skb)) - goto out; + goto unlock; } #ifdef CONFIG_NET_CLS_ACT @@ -3201,6 +3231,9 @@ another_round: } #endif + if (sk_memalloc_socks() && skb_pfmemalloc(skb)) + goto skip_taps; + list_for_each_entry_rcu(ptype, &ptype_all, list) { if (!ptype->dev || ptype->dev == skb->dev) { if (pt_prev) @@ -3209,13 +3242,18 @@ another_round: } } +skip_taps: #ifdef CONFIG_NET_CLS_ACT skb = handle_ing(skb, &pt_prev, &ret, orig_dev); if (!skb) - goto out; + goto unlock; ncls: #endif + if (sk_memalloc_socks() && skb_pfmemalloc(skb) + && !skb_pfmemalloc_protocol(skb)) + goto drop; + rx_handler = rcu_dereference(skb->dev->rx_handler); if (vlan_tx_tag_present(skb)) { if (pt_prev) { @@ -3225,7 +3263,7 @@ ncls: if (vlan_do_receive(&skb, !rx_handler)) goto another_round; else if (unlikely(!skb)) - goto out; + goto unlock; } if (rx_handler) { @@ -3235,7 +3273,7 @@ ncls: } switch (rx_handler(&skb)) { case RX_HANDLER_CONSUMED: - goto out; + goto unlock; case RX_HANDLER_ANOTHER: goto another_round; case RX_HANDLER_EXACT: @@ -3268,6 +3306,7 @@ ncls: else ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev); } else { +drop: atomic_long_inc(&skb->dev->rx_dropped); kfree_skb(skb); /* Jamal, now you will not able to escape explaining @@ -3276,8 +3315,10 @@ ncls: ret = NET_RX_DROP; } -out: +unlock: rcu_read_unlock(); +out: + tsk_restore_flags(current, pflags, PF_MEMALLOC); return ret; } diff --git a/net/core/sock.c b/net/core/sock.c index c8c5816289fe..32fdcd2d6e8f 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -298,6 +298,22 @@ void sk_clear_memalloc(struct sock *sk) } EXPORT_SYMBOL_GPL(sk_clear_memalloc); +int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb) +{ + int ret; + unsigned long pflags = current->flags; + + /* these should have been dropped before queueing */ + BUG_ON(!sock_flag(sk, SOCK_MEMALLOC)); + + current->flags |= PF_MEMALLOC; + ret = sk->sk_backlog_rcv(sk, skb); + tsk_restore_flags(current, pflags, PF_MEMALLOC); + + return ret; +} +EXPORT_SYMBOL(__sk_backlog_rcv); + #if defined(CONFIG_CGROUPS) #if !defined(CONFIG_NET_CLS_CGROUP) int net_cls_subsys_id = -1; -- cgit v1.2.3 From 5515061d22f0f9976ae7815864bfd22042d36848 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:35 -0700 Subject: mm: throttle direct reclaimers if PF_MEMALLOC reserves are low and swap is backed by network storage If swap is backed by network storage such as NBD, there is a risk that a large number of reclaimers can hang the system by consuming all PF_MEMALLOC reserves. To avoid these hangs, the administrator must tune min_free_kbytes in advance which is a bit fragile. This patch throttles direct reclaimers if half the PF_MEMALLOC reserves are in use. If the system is routinely getting throttled the system administrator can increase min_free_kbytes so degradation is smoother but the system will keep running. Signed-off-by: Mel Gorman Cc: David Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 1 + mm/page_alloc.c | 1 + mm/vmscan.c | 128 +++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 122 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 64b2c3a48286..2daa54f55db7 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -705,6 +705,7 @@ typedef struct pglist_data { range, including holes */ int node_id; wait_queue_head_t kswapd_wait; + wait_queue_head_t pfmemalloc_wait; struct task_struct *kswapd; /* Protected by lock_memory_hotplug() */ int kswapd_max_order; enum zone_type classzone_idx; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index ef2d1e72fc07..48aee0f5902f 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4389,6 +4389,7 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat, pgdat_resize_init(pgdat); pgdat->nr_zones = 0; init_waitqueue_head(&pgdat->kswapd_wait); + init_waitqueue_head(&pgdat->pfmemalloc_wait); pgdat->kswapd_max_order = 0; pgdat_page_cgroup_init(pgdat); diff --git a/mm/vmscan.c b/mm/vmscan.c index 6b1f89a91212..021a44a7bd20 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2112,6 +2112,80 @@ out: return 0; } +static bool pfmemalloc_watermark_ok(pg_data_t *pgdat) +{ + struct zone *zone; + unsigned long pfmemalloc_reserve = 0; + unsigned long free_pages = 0; + int i; + bool wmark_ok; + + for (i = 0; i <= ZONE_NORMAL; i++) { + zone = &pgdat->node_zones[i]; + pfmemalloc_reserve += min_wmark_pages(zone); + free_pages += zone_page_state(zone, NR_FREE_PAGES); + } + + wmark_ok = free_pages > pfmemalloc_reserve / 2; + + /* kswapd must be awake if processes are being throttled */ + if (!wmark_ok && waitqueue_active(&pgdat->kswapd_wait)) { + pgdat->classzone_idx = min(pgdat->classzone_idx, + (enum zone_type)ZONE_NORMAL); + wake_up_interruptible(&pgdat->kswapd_wait); + } + + return wmark_ok; +} + +/* + * Throttle direct reclaimers if backing storage is backed by the network + * and the PFMEMALLOC reserve for the preferred node is getting dangerously + * depleted. kswapd will continue to make progress and wake the processes + * when the low watermark is reached + */ +static void throttle_direct_reclaim(gfp_t gfp_mask, struct zonelist *zonelist, + nodemask_t *nodemask) +{ + struct zone *zone; + int high_zoneidx = gfp_zone(gfp_mask); + pg_data_t *pgdat; + + /* + * Kernel threads should not be throttled as they may be indirectly + * responsible for cleaning pages necessary for reclaim to make forward + * progress. kjournald for example may enter direct reclaim while + * committing a transaction where throttling it could forcing other + * processes to block on log_wait_commit(). + */ + if (current->flags & PF_KTHREAD) + return; + + /* Check if the pfmemalloc reserves are ok */ + first_zones_zonelist(zonelist, high_zoneidx, NULL, &zone); + pgdat = zone->zone_pgdat; + if (pfmemalloc_watermark_ok(pgdat)) + return; + + /* + * If the caller cannot enter the filesystem, it's possible that it + * is due to the caller holding an FS lock or performing a journal + * transaction in the case of a filesystem like ext[3|4]. In this case, + * it is not safe to block on pfmemalloc_wait as kswapd could be + * blocked waiting on the same lock. Instead, throttle for up to a + * second before continuing. + */ + if (!(gfp_mask & __GFP_FS)) { + wait_event_interruptible_timeout(pgdat->pfmemalloc_wait, + pfmemalloc_watermark_ok(pgdat), HZ); + return; + } + + /* Throttle until kswapd wakes the process */ + wait_event_killable(zone->zone_pgdat->pfmemalloc_wait, + pfmemalloc_watermark_ok(pgdat)); +} + unsigned long try_to_free_pages(struct zonelist *zonelist, int order, gfp_t gfp_mask, nodemask_t *nodemask) { @@ -2131,6 +2205,15 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order, .gfp_mask = sc.gfp_mask, }; + throttle_direct_reclaim(gfp_mask, zonelist, nodemask); + + /* + * Do not enter reclaim if fatal signal is pending. 1 is returned so + * that the page allocator does not consider triggering OOM + */ + if (fatal_signal_pending(current)) + return 1; + trace_mm_vmscan_direct_reclaim_begin(order, sc.may_writepage, gfp_mask); @@ -2275,8 +2358,13 @@ static bool pgdat_balanced(pg_data_t *pgdat, unsigned long balanced_pages, return balanced_pages >= (present_pages >> 2); } -/* is kswapd sleeping prematurely? */ -static bool sleeping_prematurely(pg_data_t *pgdat, int order, long remaining, +/* + * Prepare kswapd for sleeping. This verifies that there are no processes + * waiting in throttle_direct_reclaim() and that watermarks have been met. + * + * Returns true if kswapd is ready to sleep + */ +static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, long remaining, int classzone_idx) { int i; @@ -2285,7 +2373,21 @@ static bool sleeping_prematurely(pg_data_t *pgdat, int order, long remaining, /* If a direct reclaimer woke kswapd within HZ/10, it's premature */ if (remaining) - return true; + return false; + + /* + * There is a potential race between when kswapd checks its watermarks + * and a process gets throttled. There is also a potential race if + * processes get throttled, kswapd wakes, a large process exits therby + * balancing the zones that causes kswapd to miss a wakeup. If kswapd + * is going to sleep, no process should be sleeping on pfmemalloc_wait + * so wake them now if necessary. If necessary, processes will wake + * kswapd and get throttled again + */ + if (waitqueue_active(&pgdat->pfmemalloc_wait)) { + wake_up(&pgdat->pfmemalloc_wait); + return false; + } /* Check the watermark levels */ for (i = 0; i <= classzone_idx; i++) { @@ -2318,9 +2420,9 @@ static bool sleeping_prematurely(pg_data_t *pgdat, int order, long remaining, * must be balanced */ if (order) - return !pgdat_balanced(pgdat, balanced, classzone_idx); + return pgdat_balanced(pgdat, balanced, classzone_idx); else - return !all_zones_ok; + return all_zones_ok; } /* @@ -2546,6 +2648,16 @@ loop_again: } } + + /* + * If the low watermark is met there is no need for processes + * to be throttled on pfmemalloc_wait as they should not be + * able to safely make forward progress. Wake them + */ + if (waitqueue_active(&pgdat->pfmemalloc_wait) && + pfmemalloc_watermark_ok(pgdat)) + wake_up(&pgdat->pfmemalloc_wait); + if (all_zones_ok || (order && pgdat_balanced(pgdat, balanced, *classzone_idx))) break; /* kswapd: all done */ /* @@ -2647,7 +2759,7 @@ out: } /* - * Return the order we were reclaiming at so sleeping_prematurely() + * Return the order we were reclaiming at so prepare_kswapd_sleep() * makes a decision on the order we were last reclaiming at. However, * if another caller entered the allocator slow path while kswapd * was awake, order will remain at the higher level @@ -2667,7 +2779,7 @@ static void kswapd_try_to_sleep(pg_data_t *pgdat, int order, int classzone_idx) prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE); /* Try to sleep for a short interval */ - if (!sleeping_prematurely(pgdat, order, remaining, classzone_idx)) { + if (prepare_kswapd_sleep(pgdat, order, remaining, classzone_idx)) { remaining = schedule_timeout(HZ/10); finish_wait(&pgdat->kswapd_wait, &wait); prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE); @@ -2677,7 +2789,7 @@ static void kswapd_try_to_sleep(pg_data_t *pgdat, int order, int classzone_idx) * After a short sleep, check if it was a premature sleep. If not, then * go fully to sleep until explicitly woken up. */ - if (!sleeping_prematurely(pgdat, order, remaining, classzone_idx)) { + if (prepare_kswapd_sleep(pgdat, order, remaining, classzone_idx)) { trace_mm_vmscan_kswapd_sleep(pgdat->node_id); /* -- cgit v1.2.3 From 68243e76ee343d63c6cf76978588a885951e2818 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:39 -0700 Subject: mm: account for the number of times direct reclaimers get throttled Under significant pressure when writing back to network-backed storage, direct reclaimers may get throttled. This is expected to be a short-lived event and the processes get woken up again but processes do get stalled. This patch counts how many times such stalling occurs. It's up to the administrator whether to reduce these stalls by increasing min_free_kbytes. Signed-off-by: Mel Gorman Cc: David Miller Cc: Neil Brown Cc: Peter Zijlstra Cc: Mike Christie Cc: Eric B Munson Cc: Eric Dumazet Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/vm_event_item.h | 1 + mm/vmscan.c | 3 +++ mm/vmstat.c | 1 + 3 files changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h index 06f8e3858251..57f7b1091511 100644 --- a/include/linux/vm_event_item.h +++ b/include/linux/vm_event_item.h @@ -30,6 +30,7 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT, FOR_ALL_ZONES(PGSTEAL_DIRECT), FOR_ALL_ZONES(PGSCAN_KSWAPD), FOR_ALL_ZONES(PGSCAN_DIRECT), + PGSCAN_DIRECT_THROTTLE, #ifdef CONFIG_NUMA PGSCAN_ZONE_RECLAIM_FAILED, #endif diff --git a/mm/vmscan.c b/mm/vmscan.c index 021a44a7bd20..88804017e7d6 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2167,6 +2167,9 @@ static void throttle_direct_reclaim(gfp_t gfp_mask, struct zonelist *zonelist, if (pfmemalloc_watermark_ok(pgdat)) return; + /* Account for the throttling */ + count_vm_event(PGSCAN_DIRECT_THROTTLE); + /* * If the caller cannot enter the filesystem, it's possible that it * is due to the caller holding an FS lock or performing a journal diff --git a/mm/vmstat.c b/mm/vmstat.c index 1bbbbd9776ad..df7a6748231d 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -745,6 +745,7 @@ const char * const vmstat_text[] = { TEXTS_FOR_ZONES("pgsteal_direct") TEXTS_FOR_ZONES("pgscan_kswapd") TEXTS_FOR_ZONES("pgscan_direct") + "pgscan_direct_throttle", #ifdef CONFIG_NUMA "zone_reclaim_failed", -- cgit v1.2.3 From c76562b6709fee5eff8a6a779be41c0bce661fd7 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:41 -0700 Subject: netvm: prevent a stream-specific deadlock This patch series is based on top of "Swap-over-NBD without deadlocking v15" as it depends on the same reservation of PF_MEMALLOC reserves logic. When a user or administrator requires swap for their application, they create a swap partition and file, format it with mkswap and activate it with swapon. In diskless systems this is not an option so if swap if required then swapping over the network is considered. The two likely scenarios are when blade servers are used as part of a cluster where the form factor or maintenance costs do not allow the use of disks and thin clients. The Linux Terminal Server Project recommends the use of the Network Block Device (NBD) for swap but this is not always an option. There is no guarantee that the network attached storage (NAS) device is running Linux or supports NBD. However, it is likely that it supports NFS so there are users that want support for swapping over NFS despite any performance concern. Some distributions currently carry patches that support swapping over NFS but it would be preferable to support it in the mainline kernel. Patch 1 avoids a stream-specific deadlock that potentially affects TCP. Patch 2 is a small modification to SELinux to avoid using PFMEMALLOC reserves. Patch 3 adds three helpers for filesystems to handle swap cache pages. For example, page_file_mapping() returns page->mapping for file-backed pages and the address_space of the underlying swap file for swap cache pages. Patch 4 adds two address_space_operations to allow a filesystem to pin all metadata relevant to a swapfile in memory. Upon successful activation, the swapfile is marked SWP_FILE and the address space operation ->direct_IO is used for writing and ->readpage for reading in swap pages. Patch 5 notes that patch 3 is bolting filesystem-specific-swapfile-support onto the side and that the default handlers have different information to what is available to the filesystem. This patch refactors the code so that there are generic handlers for each of the new address_space operations. Patch 6 adds an API to allow a vector of kernel addresses to be translated to struct pages and pinned for IO. Patch 7 adds support for using highmem pages for swap by kmapping the pages before calling the direct_IO handler. Patch 8 updates NFS to use the helpers from patch 3 where necessary. Patch 9 avoids setting PF_private on PG_swapcache pages within NFS. Patch 10 implements the new swapfile-related address_space operations for NFS and teaches the direct IO handler how to manage kernel addresses. Patch 11 prevents page allocator recursions in NFS by using GFP_NOIO where appropriate. Patch 12 fixes a NULL pointer dereference that occurs when using swap-over-NFS. With the patches applied, it is possible to mount a swapfile that is on an NFS filesystem. Swap performance is not great with a swap stress test taking roughly twice as long to complete than if the swap device was backed by NBD. This patch: netvm: prevent a stream-specific deadlock It could happen that all !SOCK_MEMALLOC sockets have buffered so much data that we're over the global rmem limit. This will prevent SOCK_MEMALLOC buffers from receiving data, which will prevent userspace from running, which is needed to reduce the buffered data. Fix this by exempting the SOCK_MEMALLOC sockets from the rmem limit. Once this change it applied, it is important that sockets that set SOCK_MEMALLOC do not clear the flag until the socket is being torn down. If this happens, a warning is generated and the tokens reclaimed to avoid accounting errors until the bug is fixed. [davem@davemloft.net: Warning about clearing SOCK_MEMALLOC] Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Acked-by: David S. Miller Acked-by: Rik van Riel Cc: Trond Myklebust Cc: Neil Brown Cc: Christoph Hellwig Cc: Mike Christie Cc: Eric B Munson Cc: Sebastian Andrzej Siewior Cc: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/net/sock.h | 8 +++++--- net/caif/caif_socket.c | 2 +- net/core/sock.c | 14 +++++++++++++- net/ipv4/tcp_input.c | 21 +++++++++++---------- net/sctp/ulpevent.c | 3 ++- 5 files changed, 32 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 43a470d40d76..b3730239bf18 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1329,12 +1329,14 @@ static inline bool sk_wmem_schedule(struct sock *sk, int size) __sk_mem_schedule(sk, size, SK_MEM_SEND); } -static inline bool sk_rmem_schedule(struct sock *sk, int size) +static inline bool +sk_rmem_schedule(struct sock *sk, struct sk_buff *skb, unsigned int size) { if (!sk_has_account(sk)) return true; - return size <= sk->sk_forward_alloc || - __sk_mem_schedule(sk, size, SK_MEM_RECV); + return size<= sk->sk_forward_alloc || + __sk_mem_schedule(sk, size, SK_MEM_RECV) || + skb_pfmemalloc(skb); } static inline void sk_mem_reclaim(struct sock *sk) diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index 78f1cdad5b33..095259f83902 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c @@ -141,7 +141,7 @@ static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) err = sk_filter(sk, skb); if (err) return err; - if (!sk_rmem_schedule(sk, skb->truesize) && rx_flow_is_on(cf_sk)) { + if (!sk_rmem_schedule(sk, skb, skb->truesize) && rx_flow_is_on(cf_sk)) { set_rx_flow_off(cf_sk); net_dbg_ratelimited("sending flow OFF due to rmem_schedule\n"); caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ); diff --git a/net/core/sock.c b/net/core/sock.c index 32fdcd2d6e8f..6b654b3ddfda 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -295,6 +295,18 @@ void sk_clear_memalloc(struct sock *sk) sock_reset_flag(sk, SOCK_MEMALLOC); sk->sk_allocation &= ~__GFP_MEMALLOC; static_key_slow_dec(&memalloc_socks); + + /* + * SOCK_MEMALLOC is allowed to ignore rmem limits to ensure forward + * progress of swapping. However, if SOCK_MEMALLOC is cleared while + * it has rmem allocations there is a risk that the user of the + * socket cannot make forward progress due to exceeding the rmem + * limits. By rights, sk_clear_memalloc() should only be called + * on sockets being torn down but warn and reset the accounting if + * that assumption breaks. + */ + if (WARN_ON(sk->sk_forward_alloc)) + sk_mem_reclaim(sk); } EXPORT_SYMBOL_GPL(sk_clear_memalloc); @@ -396,7 +408,7 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) if (err) return err; - if (!sk_rmem_schedule(sk, skb->truesize)) { + if (!sk_rmem_schedule(sk, skb, skb->truesize)) { atomic_inc(&sk->sk_drops); return -ENOBUFS; } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a356e1fecf9a..00b91b4b8665 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4351,19 +4351,20 @@ static void tcp_ofo_queue(struct sock *sk) static bool tcp_prune_ofo_queue(struct sock *sk); static int tcp_prune_queue(struct sock *sk); -static int tcp_try_rmem_schedule(struct sock *sk, unsigned int size) +static int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb, + unsigned int size) { if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf || - !sk_rmem_schedule(sk, size)) { + !sk_rmem_schedule(sk, skb, size)) { if (tcp_prune_queue(sk) < 0) return -1; - if (!sk_rmem_schedule(sk, size)) { + if (!sk_rmem_schedule(sk, skb, size)) { if (!tcp_prune_ofo_queue(sk)) return -1; - if (!sk_rmem_schedule(sk, size)) + if (!sk_rmem_schedule(sk, skb, size)) return -1; } } @@ -4418,7 +4419,7 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) TCP_ECN_check_ce(tp, skb); - if (unlikely(tcp_try_rmem_schedule(sk, skb->truesize))) { + if (unlikely(tcp_try_rmem_schedule(sk, skb, skb->truesize))) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPOFODROP); __kfree_skb(skb); return; @@ -4552,17 +4553,17 @@ static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size) { - struct sk_buff *skb; + struct sk_buff *skb = NULL; struct tcphdr *th; bool fragstolen; - if (tcp_try_rmem_schedule(sk, size + sizeof(*th))) - goto err; - skb = alloc_skb(size + sizeof(*th), sk->sk_allocation); if (!skb) goto err; + if (tcp_try_rmem_schedule(sk, skb, size + sizeof(*th))) + goto err_free; + th = (struct tcphdr *)skb_put(skb, sizeof(*th)); skb_reset_transport_header(skb); memset(th, 0, sizeof(*th)); @@ -4633,7 +4634,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) if (eaten <= 0) { queue_and_out: if (eaten < 0 && - tcp_try_rmem_schedule(sk, skb->truesize)) + tcp_try_rmem_schedule(sk, skb, skb->truesize)) goto drop; eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen); diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index 33d894776192..10c018a5b9fe 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -702,7 +702,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc, if (rx_count >= asoc->base.sk->sk_rcvbuf) { if ((asoc->base.sk->sk_userlocks & SOCK_RCVBUF_LOCK) || - (!sk_rmem_schedule(asoc->base.sk, chunk->skb->truesize))) + (!sk_rmem_schedule(asoc->base.sk, chunk->skb, + chunk->skb->truesize))) goto fail; } -- cgit v1.2.3 From f981c5950fa85916ba49bea5d9a7a5078f47e569 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:47 -0700 Subject: mm: methods for teaching filesystems about PG_swapcache pages In order to teach filesystems to handle swap cache pages, three new page functions are introduced: pgoff_t page_file_index(struct page *); loff_t page_file_offset(struct page *); struct address_space *page_file_mapping(struct page *); page_file_index() - gives the offset of this page in the file in PAGE_CACHE_SIZE blocks. Like page->index is for mapped pages, this function also gives the correct index for PG_swapcache pages. page_file_offset() - uses page_file_index(), so that it will give the expected result, even for PG_swapcache pages. page_file_mapping() - gives the mapping backing the actual page; that is for swap cache pages it will give swap_file->f_mapping. Signed-off-by: Peter Zijlstra Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Christoph Hellwig Cc: David S. Miller Cc: Eric B Munson Cc: Eric Paris Cc: James Morris Cc: Mel Gorman Cc: Mike Christie Cc: Neil Brown Cc: Sebastian Andrzej Siewior Cc: Trond Myklebust Cc: Xiaotian Feng Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 25 +++++++++++++++++++++++++ include/linux/pagemap.h | 5 +++++ include/linux/swap.h | 1 + mm/swapfile.c | 26 ++++++++++++++++++++++++++ 4 files changed, 57 insertions(+) (limited to 'include') diff --git a/include/linux/mm.h b/include/linux/mm.h index 7c6dfd2faa69..7cdac1676b59 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -805,6 +805,17 @@ static inline void *page_rmapping(struct page *page) return (void *)((unsigned long)page->mapping & ~PAGE_MAPPING_FLAGS); } +extern struct address_space *__page_file_mapping(struct page *); + +static inline +struct address_space *page_file_mapping(struct page *page) +{ + if (unlikely(PageSwapCache(page))) + return __page_file_mapping(page); + + return page->mapping; +} + static inline int PageAnon(struct page *page) { return ((unsigned long)page->mapping & PAGE_MAPPING_ANON) != 0; @@ -821,6 +832,20 @@ static inline pgoff_t page_index(struct page *page) return page->index; } +extern pgoff_t __page_file_index(struct page *page); + +/* + * Return the file index of the page. Regular pagecache pages use ->index + * whereas swapcache pages use swp_offset(->private) + */ +static inline pgoff_t page_file_index(struct page *page) +{ + if (unlikely(PageSwapCache(page))) + return __page_file_index(page); + + return page->index; +} + /* * Return true if this page is mapped into pagetables. */ diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 7cfad3bbb0cc..e42c762f0dc7 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -286,6 +286,11 @@ static inline loff_t page_offset(struct page *page) return ((loff_t)page->index) << PAGE_CACHE_SHIFT; } +static inline loff_t page_file_offset(struct page *page) +{ + return ((loff_t)page_file_index(page)) << PAGE_CACHE_SHIFT; +} + extern pgoff_t linear_hugepage_index(struct vm_area_struct *vma, unsigned long address); diff --git a/include/linux/swap.h b/include/linux/swap.h index 9a16bb1cefd1..e62425ded2ed 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -356,6 +356,7 @@ extern unsigned int count_swap_pages(int, int); extern sector_t map_swap_page(struct page *, struct block_device **); extern sector_t swapdev_block(int, pgoff_t); extern int page_swapcount(struct page *); +extern struct swap_info_struct *page_swap_info(struct page *); extern int reuse_swap_page(struct page *); extern int try_to_free_swap(struct page *); struct backing_dev_info; diff --git a/mm/swapfile.c b/mm/swapfile.c index 71373d03fcee..f89af5ba2eb2 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -2285,6 +2286,31 @@ int swapcache_prepare(swp_entry_t entry) return __swap_duplicate(entry, SWAP_HAS_CACHE); } +struct swap_info_struct *page_swap_info(struct page *page) +{ + swp_entry_t swap = { .val = page_private(page) }; + BUG_ON(!PageSwapCache(page)); + return swap_info[swp_type(swap)]; +} + +/* + * out-of-line __page_file_ methods to avoid include hell. + */ +struct address_space *__page_file_mapping(struct page *page) +{ + VM_BUG_ON(!PageSwapCache(page)); + return page_swap_info(page)->swap_file->f_mapping; +} +EXPORT_SYMBOL_GPL(__page_file_mapping); + +pgoff_t __page_file_index(struct page *page) +{ + swp_entry_t swap = { .val = page_private(page) }; + VM_BUG_ON(!PageSwapCache(page)); + return swp_offset(swap); +} +EXPORT_SYMBOL_GPL(__page_file_index); + /* * add_swap_count_continuation - called when a swap count is duplicated * beyond SWAP_MAP_MAX, it allocates a new page and links that to the entry's -- cgit v1.2.3 From 18022c5d8627a7a9ba8097a0f238b513fae6f5b8 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:51 -0700 Subject: mm: add get_kernel_page[s] for pinning of kernel addresses for I/O This patch adds two new APIs get_kernel_pages() and get_kernel_page() that may be used to pin a vector of kernel addresses for IO. The initial user is expected to be NFS for allowing pages to be written to swap using aops->direct_IO(). Strictly speaking, swap-over-NFS only needs to pin one page for IO but it makes sense to express the API in terms of a vector and add a helper for pinning single pages. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Christoph Hellwig Cc: David S. Miller Cc: Eric B Munson Cc: Eric Paris Cc: James Morris Cc: Mel Gorman Cc: Mike Christie Cc: Neil Brown Cc: Peter Zijlstra Cc: Sebastian Andrzej Siewior Cc: Trond Myklebust Cc: Xiaotian Feng Cc: Mark Salter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/blk_types.h | 2 ++ include/linux/fs.h | 2 ++ include/linux/mm.h | 4 ++++ mm/swap.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+) (limited to 'include') diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 0edb65dd8edd..7b7ac9ccec7a 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -160,6 +160,7 @@ enum rq_flag_bits { __REQ_FLUSH_SEQ, /* request for flush sequence */ __REQ_IO_STAT, /* account I/O stat */ __REQ_MIXED_MERGE, /* merge of different types, fail separately */ + __REQ_KERNEL, /* direct IO to kernel pages */ __REQ_NR_BITS, /* stops here */ }; @@ -201,5 +202,6 @@ enum rq_flag_bits { #define REQ_IO_STAT (1 << __REQ_IO_STAT) #define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE) #define REQ_SECURE (1 << __REQ_SECURE) +#define REQ_KERNEL (1 << __REQ_KERNEL) #endif /* __LINUX_BLK_TYPES_H */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 8fabb037a48d..9d77309da153 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -165,6 +165,8 @@ struct inodes_stat_t { #define READ 0 #define WRITE RW_MASK #define READA RWA_MASK +#define KERNEL_READ (READ|REQ_KERNEL) +#define KERNEL_WRITE (WRITE|REQ_KERNEL) #define READ_SYNC (READ | REQ_SYNC) #define WRITE_SYNC (WRITE | REQ_SYNC | REQ_NOIDLE) diff --git a/include/linux/mm.h b/include/linux/mm.h index 7cdac1676b59..bd079a1b0fdc 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1019,6 +1019,10 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, struct page **pages, struct vm_area_struct **vmas); int get_user_pages_fast(unsigned long start, int nr_pages, int write, struct page **pages); +struct kvec; +int get_kernel_pages(const struct kvec *iov, int nr_pages, int write, + struct page **pages); +int get_kernel_page(unsigned long start, int write, struct page **pages); struct page *get_dump_page(unsigned long addr); extern int try_to_release_page(struct page * page, gfp_t gfp_mask); diff --git a/mm/swap.c b/mm/swap.c index 4e7e2ec67078..7d7f80c8044a 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -236,6 +236,59 @@ void put_pages_list(struct list_head *pages) } EXPORT_SYMBOL(put_pages_list); +/* + * get_kernel_pages() - pin kernel pages in memory + * @kiov: An array of struct kvec structures + * @nr_segs: number of segments to pin + * @write: pinning for read/write, currently ignored + * @pages: array that receives pointers to the pages pinned. + * Should be at least nr_segs long. + * + * Returns number of pages pinned. This may be fewer than the number + * requested. If nr_pages is 0 or negative, returns 0. If no pages + * were pinned, returns -errno. Each page returned must be released + * with a put_page() call when it is finished with. + */ +int get_kernel_pages(const struct kvec *kiov, int nr_segs, int write, + struct page **pages) +{ + int seg; + + for (seg = 0; seg < nr_segs; seg++) { + if (WARN_ON(kiov[seg].iov_len != PAGE_SIZE)) + return seg; + + /* virt_to_page sanity checks the PFN */ + pages[seg] = virt_to_page(kiov[seg].iov_base); + page_cache_get(pages[seg]); + } + + return seg; +} +EXPORT_SYMBOL_GPL(get_kernel_pages); + +/* + * get_kernel_page() - pin a kernel page in memory + * @start: starting kernel address + * @write: pinning for read/write, currently ignored + * @pages: array that receives pointer to the page pinned. + * Must be at least nr_segs long. + * + * Returns 1 if page is pinned. If the page was not pinned, returns + * -errno. The page returned must be released with a put_page() call + * when it is finished with. + */ +int get_kernel_page(unsigned long start, int write, struct page **pages) +{ + const struct kvec kiov = { + .iov_base = (void *)start, + .iov_len = PAGE_SIZE + }; + + return get_kernel_pages(&kiov, 1, write, pages); +} +EXPORT_SYMBOL_GPL(get_kernel_page); + static void pagevec_lru_move_fn(struct pagevec *pvec, void (*move_fn)(struct page *page, struct lruvec *lruvec, void *arg), void *arg) -- cgit v1.2.3 From 62c230bc1790923a1b35da03596a68a6c9b5b100 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:55 -0700 Subject: mm: add support for a filesystem to activate swap files and use direct_IO for writing swap pages Currently swapfiles are managed entirely by the core VM by using ->bmap to allocate space and write to the blocks directly. This effectively ensures that the underlying blocks are allocated and avoids the need for the swap subsystem to locate what physical blocks store offsets within a file. If the swap subsystem is to use the filesystem information to locate the blocks, it is critical that information such as block groups, block bitmaps and the block descriptor table that map the swap file were resident in memory. This patch adds address_space_operations that the VM can call when activating or deactivating swap backed by a file. int swap_activate(struct file *); int swap_deactivate(struct file *); The ->swap_activate() method is used to communicate to the file that the VM relies on it, and the address_space should take adequate measures such as reserving space in the underlying device, reserving memory for mempools and pinning information such as the block descriptor table in memory. The ->swap_deactivate() method is called on sys_swapoff() if ->swap_activate() returned success. After a successful swapfile ->swap_activate, the swapfile is marked SWP_FILE and swapper_space.a_ops will proxy to sis->swap_file->f_mappings->a_ops using ->direct_io to write swapcache pages and ->readpage to read. It is perfectly possible that direct_IO be used to read the swap pages but it is an unnecessary complication. Similarly, it is possible that ->writepage be used instead of direct_io to write the pages but filesystem developers have stated that calling writepage from the VM is undesirable for a variety of reasons and using direct_IO opens up the possibility of writing back batches of swap pages in the future. [a.p.zijlstra@chello.nl: Original patch] Signed-off-by: Mel Gorman Acked-by: Rik van Riel Cc: Christoph Hellwig Cc: David S. Miller Cc: Eric B Munson Cc: Eric Paris Cc: James Morris Cc: Mel Gorman Cc: Mike Christie Cc: Neil Brown Cc: Peter Zijlstra Cc: Sebastian Andrzej Siewior Cc: Trond Myklebust Cc: Xiaotian Feng Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/Locking | 13 ++++++++++ Documentation/filesystems/vfs.txt | 12 +++++++++ include/linux/fs.h | 4 +++ include/linux/swap.h | 2 ++ mm/page_io.c | 52 +++++++++++++++++++++++++++++++++++++++ mm/swap_state.c | 2 +- mm/swapfile.c | 23 +++++++++++++++-- 7 files changed, 105 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index e0cce2a5f820..2db1900d7538 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -206,6 +206,8 @@ prototypes: int (*launder_page)(struct page *); int (*is_partially_uptodate)(struct page *, read_descriptor_t *, unsigned long); int (*error_remove_page)(struct address_space *, struct page *); + int (*swap_activate)(struct file *); + int (*swap_deactivate)(struct file *); locking rules: All except set_page_dirty and freepage may block @@ -229,6 +231,8 @@ migratepage: yes (both) launder_page: yes is_partially_uptodate: yes error_remove_page: yes +swap_activate: no +swap_deactivate: no ->write_begin(), ->write_end(), ->sync_page() and ->readpage() may be called from the request handler (/dev/loop). @@ -330,6 +334,15 @@ cleaned, or an error value if not. Note that in order to prevent the page getting mapped back in and redirtied, it needs to be kept locked across the entire operation. + ->swap_activate will be called with a non-zero argument on +files backing (non block device backed) swapfiles. A return value +of zero indicates success, in which case this file can be used for +backing swapspace. The swapspace operations will be proxied to the +address space operations. + + ->swap_deactivate() will be called in the sys_swapoff() +path after ->swap_activate() returned success. + ----------------------- file_lock_operations ------------------------------ prototypes: void (*fl_copy_lock)(struct file_lock *, struct file_lock *); diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index aa754e01464e..065aa2dc0835 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -592,6 +592,8 @@ struct address_space_operations { int (*migratepage) (struct page *, struct page *); int (*launder_page) (struct page *); int (*error_remove_page) (struct mapping *mapping, struct page *page); + int (*swap_activate)(struct file *); + int (*swap_deactivate)(struct file *); }; writepage: called by the VM to write a dirty page to backing store. @@ -760,6 +762,16 @@ struct address_space_operations { Setting this implies you deal with pages going away under you, unless you have them locked or reference counts increased. + swap_activate: Called when swapon is used on a file to allocate + space if necessary and pin the block lookup information in + memory. A return value of zero indicates success, + in which case this file can be used to back swapspace. The + swapspace operations will be proxied to this address space's + ->swap_{out,in} methods. + + swap_deactivate: Called during swapoff on files where swap_activate + was successful. + The File Object =============== diff --git a/include/linux/fs.h b/include/linux/fs.h index 9d77309da153..38356ab827c9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -638,6 +638,10 @@ struct address_space_operations { int (*is_partially_uptodate) (struct page *, read_descriptor_t *, unsigned long); int (*error_remove_page)(struct address_space *, struct page *); + + /* swapfile support */ + int (*swap_activate)(struct file *file); + int (*swap_deactivate)(struct file *file); }; extern const struct address_space_operations empty_aops; diff --git a/include/linux/swap.h b/include/linux/swap.h index e62425ded2ed..ab230b1ebf61 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -151,6 +151,7 @@ enum { SWP_SOLIDSTATE = (1 << 4), /* blkdev seeks are cheap */ SWP_CONTINUED = (1 << 5), /* swap_map has count continuation */ SWP_BLKDEV = (1 << 6), /* its a block device */ + SWP_FILE = (1 << 7), /* set after swap_activate success */ /* add others here before... */ SWP_SCANNING = (1 << 8), /* refcount in scan_swap_map */ }; @@ -320,6 +321,7 @@ static inline void mem_cgroup_uncharge_swap(swp_entry_t ent) /* linux/mm/page_io.c */ extern int swap_readpage(struct page *); extern int swap_writepage(struct page *page, struct writeback_control *wbc); +extern int swap_set_page_dirty(struct page *page); extern void end_swap_bio_read(struct bio *bio, int err); /* linux/mm/swap_state.c */ diff --git a/mm/page_io.c b/mm/page_io.c index 34f02923744c..307a3e795290 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -94,6 +95,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc) { struct bio *bio; int ret = 0, rw = WRITE; + struct swap_info_struct *sis = page_swap_info(page); if (try_to_free_swap(page)) { unlock_page(page); @@ -105,6 +107,32 @@ int swap_writepage(struct page *page, struct writeback_control *wbc) end_page_writeback(page); goto out; } + + if (sis->flags & SWP_FILE) { + struct kiocb kiocb; + struct file *swap_file = sis->swap_file; + struct address_space *mapping = swap_file->f_mapping; + struct iovec iov = { + .iov_base = page_address(page), + .iov_len = PAGE_SIZE, + }; + + init_sync_kiocb(&kiocb, swap_file); + kiocb.ki_pos = page_file_offset(page); + kiocb.ki_left = PAGE_SIZE; + kiocb.ki_nbytes = PAGE_SIZE; + + unlock_page(page); + ret = mapping->a_ops->direct_IO(KERNEL_WRITE, + &kiocb, &iov, + kiocb.ki_pos, 1); + if (ret == PAGE_SIZE) { + count_vm_event(PSWPOUT); + ret = 0; + } + return ret; + } + bio = get_swap_bio(GFP_NOIO, page, end_swap_bio_write); if (bio == NULL) { set_page_dirty(page); @@ -126,6 +154,7 @@ int swap_readpage(struct page *page) { struct bio *bio; int ret = 0; + struct swap_info_struct *sis = page_swap_info(page); VM_BUG_ON(!PageLocked(page)); VM_BUG_ON(PageUptodate(page)); @@ -134,6 +163,17 @@ int swap_readpage(struct page *page) unlock_page(page); goto out; } + + if (sis->flags & SWP_FILE) { + struct file *swap_file = sis->swap_file; + struct address_space *mapping = swap_file->f_mapping; + + ret = mapping->a_ops->readpage(swap_file, page); + if (!ret) + count_vm_event(PSWPIN); + return ret; + } + bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read); if (bio == NULL) { unlock_page(page); @@ -145,3 +185,15 @@ int swap_readpage(struct page *page) out: return ret; } + +int swap_set_page_dirty(struct page *page) +{ + struct swap_info_struct *sis = page_swap_info(page); + + if (sis->flags & SWP_FILE) { + struct address_space *mapping = sis->swap_file->f_mapping; + return mapping->a_ops->set_page_dirty(page); + } else { + return __set_page_dirty_no_writeback(page); + } +} diff --git a/mm/swap_state.c b/mm/swap_state.c index c85b5590cccd..0cb36fb1f61c 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -27,7 +27,7 @@ */ static const struct address_space_operations swap_aops = { .writepage = swap_writepage, - .set_page_dirty = __set_page_dirty_no_writeback, + .set_page_dirty = swap_set_page_dirty, .migratepage = migrate_page, }; diff --git a/mm/swapfile.c b/mm/swapfile.c index f89af5ba2eb2..6ffc87602f4a 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1329,6 +1329,14 @@ static void destroy_swap_extents(struct swap_info_struct *sis) list_del(&se->list); kfree(se); } + + if (sis->flags & SWP_FILE) { + struct file *swap_file = sis->swap_file; + struct address_space *mapping = swap_file->f_mapping; + + sis->flags &= ~SWP_FILE; + mapping->a_ops->swap_deactivate(swap_file); + } } /* @@ -1410,7 +1418,9 @@ add_swap_extent(struct swap_info_struct *sis, unsigned long start_page, */ static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span) { - struct inode *inode; + struct file *swap_file = sis->swap_file; + struct address_space *mapping = swap_file->f_mapping; + struct inode *inode = mapping->host; unsigned blocks_per_page; unsigned long page_no; unsigned blkbits; @@ -1421,13 +1431,22 @@ static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span) int nr_extents = 0; int ret; - inode = sis->swap_file->f_mapping->host; if (S_ISBLK(inode->i_mode)) { ret = add_swap_extent(sis, 0, sis->max, 0); *span = sis->pages; goto out; } + if (mapping->a_ops->swap_activate) { + ret = mapping->a_ops->swap_activate(swap_file); + if (!ret) { + sis->flags |= SWP_FILE; + ret = add_swap_extent(sis, 0, sis->max, 0); + *span = sis->pages; + } + goto out; + } + blkbits = inode->i_blkbits; blocks_per_page = PAGE_SIZE >> blkbits; -- cgit v1.2.3 From a509bc1a9e487d952d9404318f7f990166ab57a7 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:44:57 -0700 Subject: mm: swap: implement generic handler for swap_activate The version of swap_activate introduced is sufficient for swap-over-NFS but would not provide enough information to implement a generic handler. This patch shuffles things slightly to ensure the same information is available for aops->swap_activate() as is available to the core. No functionality change. Signed-off-by: Mel Gorman Acked-by: Rik van Riel Cc: Christoph Hellwig Cc: David S. Miller Cc: Eric B Munson Cc: Eric Paris Cc: James Morris Cc: Mel Gorman Cc: Mike Christie Cc: Neil Brown Cc: Peter Zijlstra Cc: Sebastian Andrzej Siewior Cc: Trond Myklebust Cc: Xiaotian Feng Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/fs.h | 6 ++-- include/linux/swap.h | 5 +++ mm/page_io.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ mm/swapfile.c | 91 +++------------------------------------------------ 4 files changed, 106 insertions(+), 88 deletions(-) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index 38356ab827c9..c8667f8b5358 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -429,6 +429,7 @@ struct kstatfs; struct vm_area_struct; struct vfsmount; struct cred; +struct swap_info_struct; extern void __init inode_init(void); extern void __init inode_init_early(void); @@ -640,8 +641,9 @@ struct address_space_operations { int (*error_remove_page)(struct address_space *, struct page *); /* swapfile support */ - int (*swap_activate)(struct file *file); - int (*swap_deactivate)(struct file *file); + int (*swap_activate)(struct swap_info_struct *sis, struct file *file, + sector_t *span); + void (*swap_deactivate)(struct file *file); }; extern const struct address_space_operations empty_aops; diff --git a/include/linux/swap.h b/include/linux/swap.h index ab230b1ebf61..388e70601413 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -324,6 +324,11 @@ extern int swap_writepage(struct page *page, struct writeback_control *wbc); extern int swap_set_page_dirty(struct page *page); extern void end_swap_bio_read(struct bio *bio, int err); +int add_swap_extent(struct swap_info_struct *sis, unsigned long start_page, + unsigned long nr_pages, sector_t start_block); +int generic_swapfile_activate(struct swap_info_struct *, struct file *, + sector_t *); + /* linux/mm/swap_state.c */ extern struct address_space swapper_space; #define total_swapcache_pages swapper_space.nrpages diff --git a/mm/page_io.c b/mm/page_io.c index 307a3e795290..4a379629e31f 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -87,6 +87,98 @@ void end_swap_bio_read(struct bio *bio, int err) bio_put(bio); } +int generic_swapfile_activate(struct swap_info_struct *sis, + struct file *swap_file, + sector_t *span) +{ + struct address_space *mapping = swap_file->f_mapping; + struct inode *inode = mapping->host; + unsigned blocks_per_page; + unsigned long page_no; + unsigned blkbits; + sector_t probe_block; + sector_t last_block; + sector_t lowest_block = -1; + sector_t highest_block = 0; + int nr_extents = 0; + int ret; + + blkbits = inode->i_blkbits; + blocks_per_page = PAGE_SIZE >> blkbits; + + /* + * Map all the blocks into the extent list. This code doesn't try + * to be very smart. + */ + probe_block = 0; + page_no = 0; + last_block = i_size_read(inode) >> blkbits; + while ((probe_block + blocks_per_page) <= last_block && + page_no < sis->max) { + unsigned block_in_page; + sector_t first_block; + + first_block = bmap(inode, probe_block); + if (first_block == 0) + goto bad_bmap; + + /* + * It must be PAGE_SIZE aligned on-disk + */ + if (first_block & (blocks_per_page - 1)) { + probe_block++; + goto reprobe; + } + + for (block_in_page = 1; block_in_page < blocks_per_page; + block_in_page++) { + sector_t block; + + block = bmap(inode, probe_block + block_in_page); + if (block == 0) + goto bad_bmap; + if (block != first_block + block_in_page) { + /* Discontiguity */ + probe_block++; + goto reprobe; + } + } + + first_block >>= (PAGE_SHIFT - blkbits); + if (page_no) { /* exclude the header page */ + if (first_block < lowest_block) + lowest_block = first_block; + if (first_block > highest_block) + highest_block = first_block; + } + + /* + * We found a PAGE_SIZE-length, PAGE_SIZE-aligned run of blocks + */ + ret = add_swap_extent(sis, page_no, 1, first_block); + if (ret < 0) + goto out; + nr_extents += ret; + page_no++; + probe_block += blocks_per_page; +reprobe: + continue; + } + ret = nr_extents; + *span = 1 + highest_block - lowest_block; + if (page_no == 0) + page_no = 1; /* force Empty message */ + sis->max = page_no; + sis->pages = page_no - 1; + sis->highest_bit = page_no - 1; +out: + return ret; +bad_bmap: + printk(KERN_ERR "swapon: swapfile has holes\n"); + ret = -EINVAL; + goto out; +} + /* * We may have stale swap cache pages in memory: notice * them here and get rid of the unnecessary final write. diff --git a/mm/swapfile.c b/mm/swapfile.c index 6ffc87602f4a..7307fc928d7b 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1345,7 +1345,7 @@ static void destroy_swap_extents(struct swap_info_struct *sis) * * This function rather assumes that it is called in ascending page order. */ -static int +int add_swap_extent(struct swap_info_struct *sis, unsigned long start_page, unsigned long nr_pages, sector_t start_block) { @@ -1421,106 +1421,25 @@ static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span) struct file *swap_file = sis->swap_file; struct address_space *mapping = swap_file->f_mapping; struct inode *inode = mapping->host; - unsigned blocks_per_page; - unsigned long page_no; - unsigned blkbits; - sector_t probe_block; - sector_t last_block; - sector_t lowest_block = -1; - sector_t highest_block = 0; - int nr_extents = 0; int ret; if (S_ISBLK(inode->i_mode)) { ret = add_swap_extent(sis, 0, sis->max, 0); *span = sis->pages; - goto out; + return ret; } if (mapping->a_ops->swap_activate) { - ret = mapping->a_ops->swap_activate(swap_file); + ret = mapping->a_ops->swap_activate(sis, swap_file, span); if (!ret) { sis->flags |= SWP_FILE; ret = add_swap_extent(sis, 0, sis->max, 0); *span = sis->pages; } - goto out; + return ret; } - blkbits = inode->i_blkbits; - blocks_per_page = PAGE_SIZE >> blkbits; - - /* - * Map all the blocks into the extent list. This code doesn't try - * to be very smart. - */ - probe_block = 0; - page_no = 0; - last_block = i_size_read(inode) >> blkbits; - while ((probe_block + blocks_per_page) <= last_block && - page_no < sis->max) { - unsigned block_in_page; - sector_t first_block; - - first_block = bmap(inode, probe_block); - if (first_block == 0) - goto bad_bmap; - - /* - * It must be PAGE_SIZE aligned on-disk - */ - if (first_block & (blocks_per_page - 1)) { - probe_block++; - goto reprobe; - } - - for (block_in_page = 1; block_in_page < blocks_per_page; - block_in_page++) { - sector_t block; - - block = bmap(inode, probe_block + block_in_page); - if (block == 0) - goto bad_bmap; - if (block != first_block + block_in_page) { - /* Discontiguity */ - probe_block++; - goto reprobe; - } - } - - first_block >>= (PAGE_SHIFT - blkbits); - if (page_no) { /* exclude the header page */ - if (first_block < lowest_block) - lowest_block = first_block; - if (first_block > highest_block) - highest_block = first_block; - } - - /* - * We found a PAGE_SIZE-length, PAGE_SIZE-aligned run of blocks - */ - ret = add_swap_extent(sis, page_no, 1, first_block); - if (ret < 0) - goto out; - nr_extents += ret; - page_no++; - probe_block += blocks_per_page; -reprobe: - continue; - } - ret = nr_extents; - *span = 1 + highest_block - lowest_block; - if (page_no == 0) - page_no = 1; /* force Empty message */ - sis->max = page_no; - sis->pages = page_no - 1; - sis->highest_bit = page_no - 1; -out: - return ret; -bad_bmap: - printk(KERN_ERR "swapon: swapfile has holes\n"); - ret = -EINVAL; - goto out; + return generic_swapfile_activate(sis, swap_file, span); } static void enable_swap_info(struct swap_info_struct *p, int prio, -- cgit v1.2.3 From 5a178119b0fbe37f7dfb602b37df9cc4b1dc9d71 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:45:02 -0700 Subject: mm: add support for direct_IO to highmem pages The patch "mm: add support for a filesystem to activate swap files and use direct_IO for writing swap pages" added support for using direct_IO to write swap pages but it is insufficient for highmem pages. To support highmem pages, this patch kmaps() the page before calling the direct_IO() handler. As direct_IO deals with virtual addresses an additional helper is necessary for get_kernel_pages() to lookup the struct page for a kmap virtual address. Signed-off-by: Mel Gorman Acked-by: Rik van Riel Cc: Christoph Hellwig Cc: David S. Miller Cc: Eric B Munson Cc: Eric Paris Cc: James Morris Cc: Mel Gorman Cc: Mike Christie Cc: Neil Brown Cc: Peter Zijlstra Cc: Sebastian Andrzej Siewior Cc: Trond Myklebust Cc: Xiaotian Feng Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/highmem.h | 7 +++++++ mm/highmem.c | 12 ++++++++++++ mm/page_io.c | 3 ++- mm/swap.c | 3 +-- 4 files changed, 22 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/highmem.h b/include/linux/highmem.h index 774fa47b3b5b..ef788b5b4a35 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -39,10 +39,17 @@ extern unsigned long totalhigh_pages; void kmap_flush_unused(void); +struct page *kmap_to_page(void *addr); + #else /* CONFIG_HIGHMEM */ static inline unsigned int nr_free_highpages(void) { return 0; } +static inline struct page *kmap_to_page(void *addr) +{ + return virt_to_page(addr); +} + #define totalhigh_pages 0UL #ifndef ARCH_HAS_KMAP diff --git a/mm/highmem.c b/mm/highmem.c index 57d82c6250c3..d517cd16a6eb 100644 --- a/mm/highmem.c +++ b/mm/highmem.c @@ -94,6 +94,18 @@ static DECLARE_WAIT_QUEUE_HEAD(pkmap_map_wait); do { spin_unlock(&kmap_lock); (void)(flags); } while (0) #endif +struct page *kmap_to_page(void *vaddr) +{ + unsigned long addr = (unsigned long)vaddr; + + if (addr >= PKMAP_ADDR(0) && addr <= PKMAP_ADDR(LAST_PKMAP)) { + int i = (addr - PKMAP_ADDR(0)) >> PAGE_SHIFT; + return pte_page(pkmap_page_table[i]); + } + + return virt_to_page(addr); +} + static void flush_all_zero_pkmaps(void) { int i; diff --git a/mm/page_io.c b/mm/page_io.c index 4a379629e31f..78eee32ee486 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -205,7 +205,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc) struct file *swap_file = sis->swap_file; struct address_space *mapping = swap_file->f_mapping; struct iovec iov = { - .iov_base = page_address(page), + .iov_base = kmap(page), .iov_len = PAGE_SIZE, }; @@ -218,6 +218,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc) ret = mapping->a_ops->direct_IO(KERNEL_WRITE, &kiocb, &iov, kiocb.ki_pos, 1); + kunmap(page); if (ret == PAGE_SIZE) { count_vm_event(PSWPOUT); ret = 0; diff --git a/mm/swap.c b/mm/swap.c index 7d7f80c8044a..77825883298f 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -258,8 +258,7 @@ int get_kernel_pages(const struct kvec *kiov, int nr_segs, int write, if (WARN_ON(kiov[seg].iov_len != PAGE_SIZE)) return seg; - /* virt_to_page sanity checks the PFN */ - pages[seg] = virt_to_page(kiov[seg].iov_base); + pages[seg] = kmap_to_page(kiov[seg].iov_base); page_cache_get(pages[seg]); } -- cgit v1.2.3 From a564b8f0398636ba30b07c0eaebdef7ff7837249 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:45:12 -0700 Subject: nfs: enable swap on NFS Implement the new swapfile a_ops for NFS and hook up ->direct_IO. This will set the NFS socket to SOCK_MEMALLOC and run socket reconnect under PF_MEMALLOC as well as reset SOCK_MEMALLOC before engaging the protocol ->connect() method. PF_MEMALLOC should allow the allocation of struct socket and related objects and the early (re)setting of SOCK_MEMALLOC should allow us to receive the packets required for the TCP connection buildup. [jlayton@redhat.com: Restore PF_MEMALLOC task flags in all cases] [dfeng@redhat.com: Fix handling of multiple swap files] [a.p.zijlstra@chello.nl: Original patch] Signed-off-by: Mel Gorman Acked-by: Rik van Riel Cc: Christoph Hellwig Cc: David S. Miller Cc: Eric B Munson Cc: Eric Paris Cc: James Morris Cc: Mel Gorman Cc: Mike Christie Cc: Neil Brown Cc: Sebastian Andrzej Siewior Cc: Trond Myklebust Cc: Xiaotian Feng Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfs/Kconfig | 8 +++++ fs/nfs/direct.c | 82 +++++++++++++++++++++++++++++---------------- fs/nfs/file.c | 22 ++++++++++-- include/linux/nfs_fs.h | 4 +-- include/linux/sunrpc/xprt.h | 3 ++ net/sunrpc/Kconfig | 5 +++ net/sunrpc/clnt.c | 9 +++++ net/sunrpc/sched.c | 7 ++-- net/sunrpc/xprtsock.c | 43 ++++++++++++++++++++++++ 9 files changed, 149 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index 404c6a8ac394..6fd5f2cdcd1e 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -86,6 +86,14 @@ config NFS_V4 If unsure, say Y. +config NFS_SWAP + bool "Provide swap over NFS support" + default n + depends on NFS_FS + select SUNRPC_SWAP + help + This option enables swapon to work on files located on NFS mounts. + config NFS_V4_1 bool "NFS client support for NFSv4.1 (EXPERIMENTAL)" depends on NFS_V4 && EXPERIMENTAL diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 42dce909ec70..bf9c8d0ec16a 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -115,17 +115,28 @@ static inline int put_dreq(struct nfs_direct_req *dreq) * @nr_segs: size of iovec array * * The presence of this routine in the address space ops vector means - * the NFS client supports direct I/O. However, we shunt off direct - * read and write requests before the VFS gets them, so this method - * should never be called. + * the NFS client supports direct I/O. However, for most direct IO, we + * shunt off direct read and write requests before the VFS gets them, + * so this method is only ever called for swap. */ ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t pos, unsigned long nr_segs) { +#ifndef CONFIG_NFS_SWAP dprintk("NFS: nfs_direct_IO (%s) off/no(%Ld/%lu) EINVAL\n", iocb->ki_filp->f_path.dentry->d_name.name, (long long) pos, nr_segs); return -EINVAL; +#else + VM_BUG_ON(iocb->ki_left != PAGE_SIZE); + VM_BUG_ON(iocb->ki_nbytes != PAGE_SIZE); + + if (rw == READ || rw == KERNEL_READ) + return nfs_file_direct_read(iocb, iov, nr_segs, pos, + rw == READ ? true : false); + return nfs_file_direct_write(iocb, iov, nr_segs, pos, + rw == WRITE ? true : false); +#endif /* CONFIG_NFS_SWAP */ } static void nfs_direct_release_pages(struct page **pages, unsigned int npages) @@ -303,7 +314,7 @@ static const struct nfs_pgio_completion_ops nfs_direct_read_completion_ops = { */ static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *desc, const struct iovec *iov, - loff_t pos) + loff_t pos, bool uio) { struct nfs_direct_req *dreq = desc->pg_dreq; struct nfs_open_context *ctx = dreq->ctx; @@ -331,12 +342,20 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *de GFP_KERNEL); if (!pagevec) break; - down_read(¤t->mm->mmap_sem); - result = get_user_pages(current, current->mm, user_addr, + if (uio) { + down_read(¤t->mm->mmap_sem); + result = get_user_pages(current, current->mm, user_addr, npages, 1, 0, pagevec, NULL); - up_read(¤t->mm->mmap_sem); - if (result < 0) - break; + up_read(¤t->mm->mmap_sem); + if (result < 0) + break; + } else { + WARN_ON(npages != 1); + result = get_kernel_page(user_addr, 1, pagevec); + if (WARN_ON(result != 1)) + break; + } + if ((unsigned)result < npages) { bytes = result * PAGE_SIZE; if (bytes <= pgbase) { @@ -386,7 +405,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *de static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, const struct iovec *iov, unsigned long nr_segs, - loff_t pos) + loff_t pos, bool uio) { struct nfs_pageio_descriptor desc; ssize_t result = -EINVAL; @@ -400,7 +419,7 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, for (seg = 0; seg < nr_segs; seg++) { const struct iovec *vec = &iov[seg]; - result = nfs_direct_read_schedule_segment(&desc, vec, pos); + result = nfs_direct_read_schedule_segment(&desc, vec, pos, uio); if (result < 0) break; requested_bytes += result; @@ -426,7 +445,7 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, } static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) + unsigned long nr_segs, loff_t pos, bool uio) { ssize_t result = -ENOMEM; struct inode *inode = iocb->ki_filp->f_mapping->host; @@ -444,7 +463,7 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov, if (!is_sync_kiocb(iocb)) dreq->iocb = iocb; - result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos); + result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos, uio); if (!result) result = nfs_direct_wait(dreq); NFS_I(inode)->read_io += result; @@ -610,7 +629,7 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode */ static ssize_t nfs_direct_write_schedule_segment(struct nfs_pageio_descriptor *desc, const struct iovec *iov, - loff_t pos) + loff_t pos, bool uio) { struct nfs_direct_req *dreq = desc->pg_dreq; struct nfs_open_context *ctx = dreq->ctx; @@ -638,12 +657,19 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_pageio_descriptor *d if (!pagevec) break; - down_read(¤t->mm->mmap_sem); - result = get_user_pages(current, current->mm, user_addr, - npages, 0, 0, pagevec, NULL); - up_read(¤t->mm->mmap_sem); - if (result < 0) - break; + if (uio) { + down_read(¤t->mm->mmap_sem); + result = get_user_pages(current, current->mm, user_addr, + npages, 0, 0, pagevec, NULL); + up_read(¤t->mm->mmap_sem); + if (result < 0) + break; + } else { + WARN_ON(npages != 1); + result = get_kernel_page(user_addr, 0, pagevec); + if (WARN_ON(result != 1)) + break; + } if ((unsigned)result < npages) { bytes = result * PAGE_SIZE; @@ -774,7 +800,7 @@ static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops = { static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, const struct iovec *iov, unsigned long nr_segs, - loff_t pos) + loff_t pos, bool uio) { struct nfs_pageio_descriptor desc; struct inode *inode = dreq->inode; @@ -790,7 +816,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, for (seg = 0; seg < nr_segs; seg++) { const struct iovec *vec = &iov[seg]; - result = nfs_direct_write_schedule_segment(&desc, vec, pos); + result = nfs_direct_write_schedule_segment(&desc, vec, pos, uio); if (result < 0) break; requested_bytes += result; @@ -818,7 +844,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos, - size_t count) + size_t count, bool uio) { ssize_t result = -ENOMEM; struct inode *inode = iocb->ki_filp->f_mapping->host; @@ -836,7 +862,7 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov, if (!is_sync_kiocb(iocb)) dreq->iocb = iocb; - result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos); + result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, uio); if (!result) result = nfs_direct_wait(dreq); out_release: @@ -867,7 +893,7 @@ out: * cache. */ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) + unsigned long nr_segs, loff_t pos, bool uio) { ssize_t retval = -EINVAL; struct file *file = iocb->ki_filp; @@ -892,7 +918,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, task_io_account_read(count); - retval = nfs_direct_read(iocb, iov, nr_segs, pos); + retval = nfs_direct_read(iocb, iov, nr_segs, pos, uio); if (retval > 0) iocb->ki_pos = pos + retval; @@ -923,7 +949,7 @@ out: * is no atomic O_APPEND write facility in the NFS protocol. */ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) + unsigned long nr_segs, loff_t pos, bool uio) { ssize_t retval = -EINVAL; struct file *file = iocb->ki_filp; @@ -955,7 +981,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, task_io_account_write(count); - retval = nfs_direct_write(iocb, iov, nr_segs, pos, count); + retval = nfs_direct_write(iocb, iov, nr_segs, pos, count, uio); if (retval > 0) { struct inode *inode = mapping->host; diff --git a/fs/nfs/file.c b/fs/nfs/file.c index acd4e4cd2906..50fb83a88b1b 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -175,7 +175,7 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov, ssize_t result; if (iocb->ki_filp->f_flags & O_DIRECT) - return nfs_file_direct_read(iocb, iov, nr_segs, pos); + return nfs_file_direct_read(iocb, iov, nr_segs, pos, true); dprintk("NFS: read(%s/%s, %lu@%lu)\n", dentry->d_parent->d_name.name, dentry->d_name.name, @@ -482,6 +482,20 @@ static int nfs_launder_page(struct page *page) return nfs_wb_page(inode, page); } +#ifdef CONFIG_NFS_SWAP +static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file, + sector_t *span) +{ + *span = sis->pages; + return xs_swapper(NFS_CLIENT(file->f_mapping->host)->cl_xprt, 1); +} + +static void nfs_swap_deactivate(struct file *file) +{ + xs_swapper(NFS_CLIENT(file->f_mapping->host)->cl_xprt, 0); +} +#endif + const struct address_space_operations nfs_file_aops = { .readpage = nfs_readpage, .readpages = nfs_readpages, @@ -496,6 +510,10 @@ const struct address_space_operations nfs_file_aops = { .migratepage = nfs_migrate_page, .launder_page = nfs_launder_page, .error_remove_page = generic_error_remove_page, +#ifdef CONFIG_NFS_SWAP + .swap_activate = nfs_swap_activate, + .swap_deactivate = nfs_swap_deactivate, +#endif }; /* @@ -570,7 +588,7 @@ ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov, size_t count = iov_length(iov, nr_segs); if (iocb->ki_filp->f_flags & O_DIRECT) - return nfs_file_direct_write(iocb, iov, nr_segs, pos); + return nfs_file_direct_write(iocb, iov, nr_segs, pos, true); dprintk("NFS: write(%s/%s, %lu@%Ld)\n", dentry->d_parent->d_name.name, dentry->d_name.name, diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 4b6043c20f77..35994f975a7f 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -473,10 +473,10 @@ extern ssize_t nfs_direct_IO(int, struct kiocb *, const struct iovec *, loff_t, unsigned long); extern ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, - loff_t pos); + loff_t pos, bool uio); extern ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, - loff_t pos); + loff_t pos, bool uio); /* * linux/fs/nfs/dir.c diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 77d278defa70..cff40aa7db62 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -174,6 +174,8 @@ struct rpc_xprt { unsigned long state; /* transport state */ unsigned char shutdown : 1, /* being shut down */ resvport : 1; /* use a reserved port */ + unsigned int swapper; /* we're swapping over this + transport */ unsigned int bind_index; /* bind function index */ /* @@ -316,6 +318,7 @@ void xprt_release_rqst_cong(struct rpc_task *task); void xprt_disconnect_done(struct rpc_xprt *xprt); void xprt_force_disconnect(struct rpc_xprt *xprt); void xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie); +int xs_swapper(struct rpc_xprt *xprt, int enable); /* * Reserved bit positions in xprt->state diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig index 9fe8857d8d59..03d03e37a7d5 100644 --- a/net/sunrpc/Kconfig +++ b/net/sunrpc/Kconfig @@ -21,6 +21,11 @@ config SUNRPC_XPRT_RDMA If unsure, say N. +config SUNRPC_SWAP + bool + depends on SUNRPC + select NETVM + config RPCSEC_GSS_KRB5 tristate "Secure RPC: Kerberos V mechanism" depends on SUNRPC && CRYPTO diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index b05df36692ff..fa48c60aef23 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -717,6 +717,15 @@ void rpc_task_set_client(struct rpc_task *task, struct rpc_clnt *clnt) atomic_inc(&clnt->cl_count); if (clnt->cl_softrtry) task->tk_flags |= RPC_TASK_SOFT; + if (sk_memalloc_socks()) { + struct rpc_xprt *xprt; + + rcu_read_lock(); + xprt = rcu_dereference(clnt->cl_xprt); + if (xprt->swapper) + task->tk_flags |= RPC_TASK_SWAPPER; + rcu_read_unlock(); + } /* Add to the client's list of all tasks */ spin_lock(&clnt->cl_lock); list_add_tail(&task->tk_task, &clnt->cl_tasks); diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 994cfea2bad6..83a4c43cee7f 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -812,7 +812,10 @@ static void rpc_async_schedule(struct work_struct *work) void *rpc_malloc(struct rpc_task *task, size_t size) { struct rpc_buffer *buf; - gfp_t gfp = RPC_IS_SWAPPER(task) ? GFP_ATOMIC : GFP_NOWAIT; + gfp_t gfp = GFP_NOWAIT; + + if (RPC_IS_SWAPPER(task)) + gfp |= __GFP_MEMALLOC; size += sizeof(struct rpc_buffer); if (size <= RPC_BUFFER_MAXSIZE) @@ -886,7 +889,7 @@ static void rpc_init_task(struct rpc_task *task, const struct rpc_task_setup *ta static struct rpc_task * rpc_alloc_task(void) { - return (struct rpc_task *)mempool_alloc(rpc_task_mempool, GFP_NOFS); + return (struct rpc_task *)mempool_alloc(rpc_task_mempool, GFP_NOIO); } /* diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 62d0dac8f780..bd59d01f035b 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1927,6 +1927,45 @@ out: xprt_wake_pending_tasks(xprt, status); } +#ifdef CONFIG_SUNRPC_SWAP +static void xs_set_memalloc(struct rpc_xprt *xprt) +{ + struct sock_xprt *transport = container_of(xprt, struct sock_xprt, + xprt); + + if (xprt->swapper) + sk_set_memalloc(transport->inet); +} + +/** + * xs_swapper - Tag this transport as being used for swap. + * @xprt: transport to tag + * @enable: enable/disable + * + */ +int xs_swapper(struct rpc_xprt *xprt, int enable) +{ + struct sock_xprt *transport = container_of(xprt, struct sock_xprt, + xprt); + int err = 0; + + if (enable) { + xprt->swapper++; + xs_set_memalloc(xprt); + } else if (xprt->swapper) { + xprt->swapper--; + sk_clear_memalloc(transport->inet); + } + + return err; +} +EXPORT_SYMBOL_GPL(xs_swapper); +#else +static void xs_set_memalloc(struct rpc_xprt *xprt) +{ +} +#endif + static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) { struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); @@ -1951,6 +1990,8 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) transport->sock = sock; transport->inet = sk; + xs_set_memalloc(xprt); + write_unlock_bh(&sk->sk_callback_lock); } xs_udp_do_set_buffer_size(xprt); @@ -2075,6 +2116,8 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) if (!xprt_bound(xprt)) goto out; + xs_set_memalloc(xprt); + /* Tell the socket layer to start connecting... */ xprt->stat.connect_count++; xprt->stat.connect_start = jiffies; -- cgit v1.2.3 From 0030f535a5cf9b1841d2088c10a0b2f8f2987460 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Tue, 31 Jul 2012 16:45:25 -0700 Subject: mm: memcg: fix compaction/migration failing due to memcg limits Compaction (and page migration in general) can currently be hindered through pages being owned by memory cgroups that are at their limits and unreclaimable. The reason is that the replacement page is being charged against the limit while the page being replaced is also still charged. But this seems unnecessary, given that only one of the two pages will still be in use after migration finishes. This patch changes the memcg migration sequence so that the replacement page is not charged. Whatever page is still in use after successful or failed migration gets to keep the charge of the page that was going to be replaced. The replacement page will still show up temporarily in the rss/cache statistics, this can be fixed in a later patch as it's less urgent. Reported-by: David Rientjes Signed-off-by: Johannes Weiner Acked-by: KAMEZAWA Hiroyuki Acked-by: Michal Hocko Cc: Hugh Dickins Cc: David Rientjes Cc: Wanpeng Li Cc: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 11 ++++---- mm/memcontrol.c | 67 +++++++++++++++++++++++++--------------------- mm/migrate.c | 11 ++------ 3 files changed, 43 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 5a3ee6423634..8d9489fdab2e 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -98,9 +98,9 @@ int mm_match_cgroup(const struct mm_struct *mm, const struct mem_cgroup *cgroup) extern struct cgroup_subsys_state *mem_cgroup_css(struct mem_cgroup *memcg); -extern int -mem_cgroup_prepare_migration(struct page *page, - struct page *newpage, struct mem_cgroup **memcgp, gfp_t gfp_mask); +extern void +mem_cgroup_prepare_migration(struct page *page, struct page *newpage, + struct mem_cgroup **memcgp); extern void mem_cgroup_end_migration(struct mem_cgroup *memcg, struct page *oldpage, struct page *newpage, bool migration_ok); @@ -276,11 +276,10 @@ static inline struct cgroup_subsys_state return NULL; } -static inline int +static inline void mem_cgroup_prepare_migration(struct page *page, struct page *newpage, - struct mem_cgroup **memcgp, gfp_t gfp_mask) + struct mem_cgroup **memcgp) { - return 0; } static inline void mem_cgroup_end_migration(struct mem_cgroup *memcg, diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 0f692a2dbfcb..7eadcdad06f3 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2976,7 +2976,8 @@ direct_uncharge: * uncharge if !page_mapped(page) */ static struct mem_cgroup * -__mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype) +__mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype, + bool end_migration) { struct mem_cgroup *memcg = NULL; unsigned int nr_pages = 1; @@ -3020,7 +3021,16 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype) /* fallthrough */ case MEM_CGROUP_CHARGE_TYPE_DROP: /* See mem_cgroup_prepare_migration() */ - if (page_mapped(page) || PageCgroupMigration(pc)) + if (page_mapped(page)) + goto unlock_out; + /* + * Pages under migration may not be uncharged. But + * end_migration() /must/ be the one uncharging the + * unused post-migration page and so it has to call + * here with the migration bit still set. See the + * res_counter handling below. + */ + if (!end_migration && PageCgroupMigration(pc)) goto unlock_out; break; case MEM_CGROUP_CHARGE_TYPE_SWAPOUT: @@ -3054,7 +3064,12 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype) mem_cgroup_swap_statistics(memcg, true); mem_cgroup_get(memcg); } - if (!mem_cgroup_is_root(memcg)) + /* + * Migration does not charge the res_counter for the + * replacement page, so leave it alone when phasing out the + * page that is unused after the migration. + */ + if (!end_migration && !mem_cgroup_is_root(memcg)) mem_cgroup_do_uncharge(memcg, nr_pages, ctype); return memcg; @@ -3070,14 +3085,14 @@ void mem_cgroup_uncharge_page(struct page *page) if (page_mapped(page)) return; VM_BUG_ON(page->mapping && !PageAnon(page)); - __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_ANON); + __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_ANON, false); } void mem_cgroup_uncharge_cache_page(struct page *page) { VM_BUG_ON(page_mapped(page)); VM_BUG_ON(page->mapping); - __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_CACHE); + __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_CACHE, false); } /* @@ -3141,7 +3156,7 @@ mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout) if (!swapout) /* this was a swap cache but the swap is unused ! */ ctype = MEM_CGROUP_CHARGE_TYPE_DROP; - memcg = __mem_cgroup_uncharge_common(page, ctype); + memcg = __mem_cgroup_uncharge_common(page, ctype, false); /* * record memcg information, if swapout && memcg != NULL, @@ -3231,19 +3246,18 @@ static inline int mem_cgroup_move_swap_account(swp_entry_t entry, * Before starting migration, account PAGE_SIZE to mem_cgroup that the old * page belongs to. */ -int mem_cgroup_prepare_migration(struct page *page, - struct page *newpage, struct mem_cgroup **memcgp, gfp_t gfp_mask) +void mem_cgroup_prepare_migration(struct page *page, struct page *newpage, + struct mem_cgroup **memcgp) { struct mem_cgroup *memcg = NULL; struct page_cgroup *pc; enum charge_type ctype; - int ret = 0; *memcgp = NULL; VM_BUG_ON(PageTransHuge(page)); if (mem_cgroup_disabled()) - return 0; + return; pc = lookup_page_cgroup(page); lock_page_cgroup(pc); @@ -3288,24 +3302,9 @@ int mem_cgroup_prepare_migration(struct page *page, * we return here. */ if (!memcg) - return 0; + return; *memcgp = memcg; - ret = __mem_cgroup_try_charge(NULL, gfp_mask, 1, memcgp, false); - css_put(&memcg->css);/* drop extra refcnt */ - if (ret) { - if (PageAnon(page)) { - lock_page_cgroup(pc); - ClearPageCgroupMigration(pc); - unlock_page_cgroup(pc); - /* - * The old page may be fully unmapped while we kept it. - */ - mem_cgroup_uncharge_page(page); - } - /* we'll need to revisit this error code (we have -EINTR) */ - return -ENOMEM; - } /* * We charge new page before it's used/mapped. So, even if unlock_page() * is called before end_migration, we can catch all events on this new @@ -3318,8 +3317,12 @@ int mem_cgroup_prepare_migration(struct page *page, ctype = MEM_CGROUP_CHARGE_TYPE_CACHE; else ctype = MEM_CGROUP_CHARGE_TYPE_SHMEM; + /* + * The page is committed to the memcg, but it's not actually + * charged to the res_counter since we plan on replacing the + * old one and only one page is going to be left afterwards. + */ __mem_cgroup_commit_charge(memcg, newpage, 1, ctype, false); - return ret; } /* remove redundant charge if migration failed*/ @@ -3341,6 +3344,12 @@ void mem_cgroup_end_migration(struct mem_cgroup *memcg, used = newpage; unused = oldpage; } + anon = PageAnon(used); + __mem_cgroup_uncharge_common(unused, + anon ? MEM_CGROUP_CHARGE_TYPE_ANON + : MEM_CGROUP_CHARGE_TYPE_CACHE, + true); + css_put(&memcg->css); /* * We disallowed uncharge of pages under migration because mapcount * of the page goes down to zero, temporarly. @@ -3350,10 +3359,6 @@ void mem_cgroup_end_migration(struct mem_cgroup *memcg, lock_page_cgroup(pc); ClearPageCgroupMigration(pc); unlock_page_cgroup(pc); - anon = PageAnon(used); - __mem_cgroup_uncharge_common(unused, - anon ? MEM_CGROUP_CHARGE_TYPE_ANON - : MEM_CGROUP_CHARGE_TYPE_CACHE); /* * If a page is a file cache, radix-tree replacement is very atomic diff --git a/mm/migrate.c b/mm/migrate.c index 6c37c51565e5..77ed2d773705 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -683,7 +683,6 @@ static int __unmap_and_move(struct page *page, struct page *newpage, { int rc = -EAGAIN; int remap_swapcache = 1; - int charge = 0; struct mem_cgroup *mem; struct anon_vma *anon_vma = NULL; @@ -725,12 +724,7 @@ static int __unmap_and_move(struct page *page, struct page *newpage, } /* charge against new page */ - charge = mem_cgroup_prepare_migration(page, newpage, &mem, GFP_KERNEL); - if (charge == -ENOMEM) { - rc = -ENOMEM; - goto unlock; - } - BUG_ON(charge); + mem_cgroup_prepare_migration(page, newpage, &mem); if (PageWriteback(page)) { /* @@ -820,8 +814,7 @@ skip_unmap: put_anon_vma(anon_vma); uncharge: - if (!charge) - mem_cgroup_end_migration(mem, page, newpage, rc == 0); + mem_cgroup_end_migration(mem, page, newpage, rc == 0); unlock: unlock_page(page); out: -- cgit v1.2.3 From 6527af5d1bea219d64095a5e30c1b1e0868aae16 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Tue, 31 Jul 2012 16:46:16 -0700 Subject: mm: remove redundant initialization pg_data_t is zeroed before reaching free_area_init_core(), so remove the now unnecessary initializations. Signed-off-by: Minchan Kim Cc: Tejun Heo Cc: Ralf Baechle Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/vmstat.h | 5 ----- mm/page_alloc.c | 9 ++------- 2 files changed, 2 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h index 65efb92da996..ad2cfd53dadc 100644 --- a/include/linux/vmstat.h +++ b/include/linux/vmstat.h @@ -179,11 +179,6 @@ extern void zone_statistics(struct zone *, struct zone *, gfp_t gfp); #define add_zone_page_state(__z, __i, __d) mod_zone_page_state(__z, __i, __d) #define sub_zone_page_state(__z, __i, __d) mod_zone_page_state(__z, __i, -(__d)) -static inline void zap_zone_vm_stats(struct zone *zone) -{ - memset(zone->vm_stat, 0, sizeof(zone->vm_stat)); -} - extern void inc_zone_state(struct zone *, enum zone_stat_item); #ifdef CONFIG_SMP diff --git a/mm/page_alloc.c b/mm/page_alloc.c index a6f7576aecae..889532b8e6c1 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4377,6 +4377,8 @@ void __init set_pageblock_order(void) * - mark all pages reserved * - mark all memory queues empty * - clear the memory bitmaps + * + * NOTE: pgdat should get zeroed by caller. */ static void __paginginit free_area_init_core(struct pglist_data *pgdat, unsigned long *zones_size, unsigned long *zholes_size) @@ -4387,10 +4389,8 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat, int ret; pgdat_resize_init(pgdat); - pgdat->nr_zones = 0; init_waitqueue_head(&pgdat->kswapd_wait); init_waitqueue_head(&pgdat->pfmemalloc_wait); - pgdat->kswapd_max_order = 0; pgdat_page_cgroup_init(pgdat); for (j = 0; j < MAX_NR_ZONES; j++) { @@ -4451,11 +4451,6 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat, zone_pcp_init(zone); lruvec_init(&zone->lruvec, zone); - zap_zone_vm_stats(zone); - zone->flags = 0; -#ifdef CONFIG_MEMORY_ISOLATION - zone->nr_pageblock_isolate = 0; -#endif if (!size) continue; -- cgit v1.2.3 From d833352a4338dc31295ed832a30c9ccff5c7a183 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 31 Jul 2012 16:46:20 -0700 Subject: mm: hugetlbfs: close race during teardown of hugetlbfs shared page tables If a process creates a large hugetlbfs mapping that is eligible for page table sharing and forks heavily with children some of whom fault and others which destroy the mapping then it is possible for page tables to get corrupted. Some teardowns of the mapping encounter a "bad pmd" and output a message to the kernel log. The final teardown will trigger a BUG_ON in mm/filemap.c. This was reproduced in 3.4 but is known to have existed for a long time and goes back at least as far as 2.6.37. It was probably was introduced in 2.6.20 by [39dde65c: shared page table for hugetlb page]. The messages look like this; [ ..........] Lots of bad pmd messages followed by this [ 127.164256] mm/memory.c:391: bad pmd ffff880412e04fe8(80000003de4000e7). [ 127.164257] mm/memory.c:391: bad pmd ffff880412e04ff0(80000003de6000e7). [ 127.164258] mm/memory.c:391: bad pmd ffff880412e04ff8(80000003de0000e7). [ 127.186778] ------------[ cut here ]------------ [ 127.186781] kernel BUG at mm/filemap.c:134! [ 127.186782] invalid opcode: 0000 [#1] SMP [ 127.186783] CPU 7 [ 127.186784] Modules linked in: af_packet cpufreq_conservative cpufreq_userspace cpufreq_powersave acpi_cpufreq mperf ext3 jbd dm_mod coretemp crc32c_intel usb_storage ghash_clmulni_intel aesni_intel i2c_i801 r8169 mii uas sr_mod cdrom sg iTCO_wdt iTCO_vendor_support shpchp serio_raw cryptd aes_x86_64 e1000e pci_hotplug dcdbas aes_generic container microcode ext4 mbcache jbd2 crc16 sd_mod crc_t10dif i915 drm_kms_helper drm i2c_algo_bit ehci_hcd ahci libahci usbcore rtc_cmos usb_common button i2c_core intel_agp video intel_gtt fan processor thermal thermal_sys hwmon ata_generic pata_atiixp libata scsi_mod [ 127.186801] [ 127.186802] Pid: 9017, comm: hugetlbfs-test Not tainted 3.4.0-autobuild #53 Dell Inc. OptiPlex 990/06D7TR [ 127.186804] RIP: 0010:[] [] __delete_from_page_cache+0x15e/0x160 [ 127.186809] RSP: 0000:ffff8804144b5c08 EFLAGS: 00010002 [ 127.186810] RAX: 0000000000000001 RBX: ffffea000a5c9000 RCX: 00000000ffffffc0 [ 127.186811] RDX: 0000000000000000 RSI: 0000000000000009 RDI: ffff88042dfdad00 [ 127.186812] RBP: ffff8804144b5c18 R08: 0000000000000009 R09: 0000000000000003 [ 127.186813] R10: 0000000000000000 R11: 000000000000002d R12: ffff880412ff83d8 [ 127.186814] R13: ffff880412ff83d8 R14: 0000000000000000 R15: ffff880412ff83d8 [ 127.186815] FS: 00007fe18ed2c700(0000) GS:ffff88042dce0000(0000) knlGS:0000000000000000 [ 127.186816] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 127.186817] CR2: 00007fe340000503 CR3: 0000000417a14000 CR4: 00000000000407e0 [ 127.186818] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 127.186819] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 127.186820] Process hugetlbfs-test (pid: 9017, threadinfo ffff8804144b4000, task ffff880417f803c0) [ 127.186821] Stack: [ 127.186822] ffffea000a5c9000 0000000000000000 ffff8804144b5c48 ffffffff810ed83b [ 127.186824] ffff8804144b5c48 000000000000138a 0000000000001387 ffff8804144b5c98 [ 127.186825] ffff8804144b5d48 ffffffff811bc925 ffff8804144b5cb8 0000000000000000 [ 127.186827] Call Trace: [ 127.186829] [] delete_from_page_cache+0x3b/0x80 [ 127.186832] [] truncate_hugepages+0x115/0x220 [ 127.186834] [] hugetlbfs_evict_inode+0x13/0x30 [ 127.186837] [] evict+0xa7/0x1b0 [ 127.186839] [] iput_final+0xd3/0x1f0 [ 127.186840] [] iput+0x39/0x50 [ 127.186842] [] d_kill+0xf8/0x130 [ 127.186843] [] dput+0xd2/0x1a0 [ 127.186845] [] __fput+0x170/0x230 [ 127.186848] [] ? rb_erase+0xce/0x150 [ 127.186849] [] fput+0x1d/0x30 [ 127.186851] [] remove_vma+0x37/0x80 [ 127.186853] [] do_munmap+0x2d2/0x360 [ 127.186855] [] sys_shmdt+0xc9/0x170 [ 127.186857] [] system_call_fastpath+0x16/0x1b [ 127.186858] Code: 0f 1f 44 00 00 48 8b 43 08 48 8b 00 48 8b 40 28 8b b0 40 03 00 00 85 f6 0f 88 df fe ff ff 48 89 df e8 e7 cb 05 00 e9 d2 fe ff ff <0f> 0b 55 83 e2 fd 48 89 e5 48 83 ec 30 48 89 5d d8 4c 89 65 e0 [ 127.186868] RIP [] __delete_from_page_cache+0x15e/0x160 [ 127.186870] RSP [ 127.186871] ---[ end trace 7cbac5d1db69f426 ]--- The bug is a race and not always easy to reproduce. To reproduce it I was doing the following on a single socket I7-based machine with 16G of RAM. $ hugeadm --pool-pages-max DEFAULT:13G $ echo $((18*1048576*1024)) > /proc/sys/kernel/shmmax $ echo $((18*1048576*1024)) > /proc/sys/kernel/shmall $ for i in `seq 1 9000`; do ./hugetlbfs-test; done On my particular machine, it usually triggers within 10 minutes but enabling debug options can change the timing such that it never hits. Once the bug is triggered, the machine is in trouble and needs to be rebooted. The machine will respond but processes accessing proc like "ps aux" will hang due to the BUG_ON. shutdown will also hang and needs a hard reset or a sysrq-b. The basic problem is a race between page table sharing and teardown. For the most part page table sharing depends on i_mmap_mutex. In some cases, it is also taking the mm->page_table_lock for the PTE updates but with shared page tables, it is the i_mmap_mutex that is more important. Unfortunately it appears to be also insufficient. Consider the following situation Process A Process B --------- --------- hugetlb_fault shmdt LockWrite(mmap_sem) do_munmap unmap_region unmap_vmas unmap_single_vma unmap_hugepage_range Lock(i_mmap_mutex) Lock(mm->page_table_lock) huge_pmd_unshare/unmap tables <--- (1) Unlock(mm->page_table_lock) Unlock(i_mmap_mutex) huge_pte_alloc ... Lock(i_mmap_mutex) ... vma_prio_walk, find svma, spte ... Lock(mm->page_table_lock) ... share spte ... Unlock(mm->page_table_lock) ... Unlock(i_mmap_mutex) ... hugetlb_no_page <--- (2) free_pgtables unlink_file_vma hugetlb_free_pgd_range remove_vma_list In this scenario, it is possible for Process A to share page tables with Process B that is trying to tear them down. The i_mmap_mutex on its own does not prevent Process A walking Process B's page tables. At (1) above, the page tables are not shared yet so it unmaps the PMDs. Process A sets up page table sharing and at (2) faults a new entry. Process B then trips up on it in free_pgtables. This patch fixes the problem by adding a new function __unmap_hugepage_range_final that is only called when the VMA is about to be destroyed. This function clears VM_MAYSHARE during unmap_hugepage_range() under the i_mmap_mutex. This makes the VMA ineligible for sharing and avoids the race. Superficially this looks like it would then be vunerable to truncate and madvise issues but hugetlbfs has its own truncate handlers so does not use unmap_mapping_range() and does not support madvise(DONTNEED). This should be treated as a -stable candidate if it is merged. Test program is as follows. The test case was mostly written by Michal Hocko with a few minor changes to reproduce this bug. ==== CUT HERE ==== static size_t huge_page_size = (2UL << 20); static size_t nr_huge_page_A = 512; static size_t nr_huge_page_B = 5632; unsigned int get_random(unsigned int max) { struct timeval tv; gettimeofday(&tv, NULL); srandom(tv.tv_usec); return random() % max; } static void play(void *addr, size_t size) { unsigned char *start = addr, *end = start + size, *a; start += get_random(size/2); /* we could itterate on huge pages but let's give it more time. */ for (a = start; a < end; a += 4096) *a = 0; } int main(int argc, char **argv) { key_t key = IPC_PRIVATE; size_t sizeA = nr_huge_page_A * huge_page_size; size_t sizeB = nr_huge_page_B * huge_page_size; int shmidA, shmidB; void *addrA = NULL, *addrB = NULL; int nr_children = 300, n = 0; if ((shmidA = shmget(key, sizeA, IPC_CREAT|SHM_HUGETLB|0660)) == -1) { perror("shmget:"); return 1; } if ((addrA = shmat(shmidA, addrA, SHM_R|SHM_W)) == (void *)-1UL) { perror("shmat"); return 1; } if ((shmidB = shmget(key, sizeB, IPC_CREAT|SHM_HUGETLB|0660)) == -1) { perror("shmget:"); return 1; } if ((addrB = shmat(shmidB, addrB, SHM_R|SHM_W)) == (void *)-1UL) { perror("shmat"); return 1; } fork_child: switch(fork()) { case 0: switch (n%3) { case 0: play(addrA, sizeA); break; case 1: play(addrB, sizeB); break; case 2: break; } break; case -1: perror("fork:"); break; default: if (++n < nr_children) goto fork_child; play(addrA, sizeA); break; } shmdt(addrA); shmdt(addrB); do { wait(NULL); } while (--n > 0); shmctl(shmidA, IPC_RMID, NULL); shmctl(shmidB, IPC_RMID, NULL); return 0; } [akpm@linux-foundation.org: name the declaration's args, fix CONFIG_HUGETLBFS=n build] Signed-off-by: Hugh Dickins Reviewed-by: Michal Hocko Signed-off-by: Mel Gorman Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 11 +++++++++++ mm/hugetlb.c | 28 ++++++++++++++++++++++++++-- mm/memory.c | 2 +- 3 files changed, 38 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index f9db20bfa9fc..225164842ab6 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -48,6 +48,10 @@ int follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *, unsigned long *, int *, int, unsigned int flags); void unmap_hugepage_range(struct vm_area_struct *, unsigned long, unsigned long, struct page *); +void __unmap_hugepage_range_final(struct mmu_gather *tlb, + struct vm_area_struct *vma, + unsigned long start, unsigned long end, + struct page *ref_page); void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, unsigned long start, unsigned long end, struct page *ref_page); @@ -130,6 +134,13 @@ static inline void copy_huge_page(struct page *dst, struct page *src) #define hugetlb_change_protection(vma, address, end, newprot) +static inline void __unmap_hugepage_range_final(struct mmu_gather *tlb, + struct vm_area_struct *vma, unsigned long start, + unsigned long end, struct page *ref_page) +{ + BUG(); +} + static inline void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, unsigned long start, unsigned long end, struct page *ref_page) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index c39e4beeb63a..bc727122dd44 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -2429,6 +2429,25 @@ again: tlb_end_vma(tlb, vma); } +void __unmap_hugepage_range_final(struct mmu_gather *tlb, + struct vm_area_struct *vma, unsigned long start, + unsigned long end, struct page *ref_page) +{ + __unmap_hugepage_range(tlb, vma, start, end, ref_page); + + /* + * Clear this flag so that x86's huge_pmd_share page_table_shareable + * test will fail on a vma being torn down, and not grab a page table + * on its way out. We're lucky that the flag has such an appropriate + * name, and can in fact be safely cleared here. We could clear it + * before the __unmap_hugepage_range above, but all that's necessary + * is to clear it before releasing the i_mmap_mutex. This works + * because in the context this is called, the VMA is about to be + * destroyed and the i_mmap_mutex is held. + */ + vma->vm_flags &= ~VM_MAYSHARE; +} + void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, unsigned long end, struct page *ref_page) { @@ -3012,9 +3031,14 @@ void hugetlb_change_protection(struct vm_area_struct *vma, } } spin_unlock(&mm->page_table_lock); - mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex); - + /* + * Must flush TLB before releasing i_mmap_mutex: x86's huge_pmd_unshare + * may have cleared our pud entry and done put_page on the page table: + * once we release i_mmap_mutex, another task can do the final put_page + * and that page table be reused and filled with junk. + */ flush_tlb_range(vma, start, end); + mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex); } int hugetlb_reserve_pages(struct inode *inode, diff --git a/mm/memory.c b/mm/memory.c index ec72a616ccd4..482f089765ff 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1345,7 +1345,7 @@ static void unmap_single_vma(struct mmu_gather *tlb, */ if (vma->vm_file) { mutex_lock(&vma->vm_file->f_mapping->i_mmap_mutex); - __unmap_hugepage_range(tlb, vma, start, end, NULL); + __unmap_hugepage_range_final(tlb, vma, start, end, NULL); mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex); } } else -- cgit v1.2.3 From 392a325c4351339cfbf182bb5a1444df1cf65dbb Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 10 Jul 2012 19:31:51 -0700 Subject: Platform: OLPC: add a stub to drivers/platform/ for the OLPC EC driver The OLPC EC driver has outgrown arch/x86/platform/. It's time to both share common code amongst different architectures, as well as move it out of arch/x86/. The XO-1.75 is ARM-based, and the EC driver shares a lot of code with the x86 code. Signed-off-by: Andres Salomon Acked-by: Paul Fox Reviewed-by: Thomas Gleixner --- arch/x86/include/asm/olpc.h | 19 +++---------------- arch/x86/platform/olpc/olpc.c | 4 ++-- drivers/platform/Makefile | 1 + drivers/platform/olpc/Makefile | 4 ++++ drivers/platform/olpc/olpc-ec.c | 16 ++++++++++++++++ include/linux/olpc-ec.h | 29 +++++++++++++++++++++++++++++ 6 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 drivers/platform/olpc/Makefile create mode 100644 drivers/platform/olpc/olpc-ec.c create mode 100644 include/linux/olpc-ec.h (limited to 'include') diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h index 87bdbca72f94..513e9992771d 100644 --- a/arch/x86/include/asm/olpc.h +++ b/arch/x86/include/asm/olpc.h @@ -4,6 +4,7 @@ #define _ASM_X86_OLPC_H #include +#include struct olpc_platform_t { int flags; @@ -102,22 +103,8 @@ extern int pci_olpc_init(void); /* EC related functions */ -extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen, - unsigned char *outbuf, size_t outlen); - -/* EC commands */ - -#define EC_FIRMWARE_REV 0x08 -#define EC_WRITE_SCI_MASK 0x1b -#define EC_WAKE_UP_WLAN 0x24 -#define EC_WLAN_LEAVE_RESET 0x25 -#define EC_READ_EB_MODE 0x2a -#define EC_SET_SCI_INHIBIT 0x32 -#define EC_SET_SCI_INHIBIT_RELEASE 0x34 -#define EC_WLAN_ENTER_RESET 0x35 -#define EC_WRITE_EXT_SCI_MASK 0x38 -#define EC_SCI_QUERY 0x84 -#define EC_EXT_SCI_QUERY 0x85 +extern int olpc_ec_cmd_x86(unsigned char cmd, unsigned char *inbuf, + size_t inlen, unsigned char *outbuf, size_t outlen); /* SCI source values */ diff --git a/arch/x86/platform/olpc/olpc.c b/arch/x86/platform/olpc/olpc.c index a4bee53c2e54..796e199ac77a 100644 --- a/arch/x86/platform/olpc/olpc.c +++ b/arch/x86/platform/olpc/olpc.c @@ -125,7 +125,7 @@ static int __wait_on_obf(unsigned int line, unsigned int port, int desired) * . Unfortunately, while * OpenFirmware's source is available, the EC's is not. */ -int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen, +int olpc_ec_cmd_x86(unsigned char cmd, unsigned char *inbuf, size_t inlen, unsigned char *outbuf, size_t outlen) { unsigned long flags; @@ -201,7 +201,7 @@ err: spin_unlock_irqrestore(&ec_lock, flags); return ret; } -EXPORT_SYMBOL_GPL(olpc_ec_cmd); +EXPORT_SYMBOL_GPL(olpc_ec_cmd_x86); void olpc_ec_wakeup_set(u16 value) { diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile index 782953ae4c03..b17c16ce54ad 100644 --- a/drivers/platform/Makefile +++ b/drivers/platform/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_X86) += x86/ +obj-$(CONFIG_OLPC) += olpc/ diff --git a/drivers/platform/olpc/Makefile b/drivers/platform/olpc/Makefile new file mode 100644 index 000000000000..dc8b26bc7209 --- /dev/null +++ b/drivers/platform/olpc/Makefile @@ -0,0 +1,4 @@ +# +# OLPC XO platform-specific drivers +# +obj-$(CONFIG_OLPC) += olpc-ec.o diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c new file mode 100644 index 000000000000..42026036cd3e --- /dev/null +++ b/drivers/platform/olpc/olpc-ec.c @@ -0,0 +1,16 @@ +/* + * Generic driver for the OLPC Embedded Controller. + * + * Copyright (C) 2011-2012 One Laptop per Child Foundation. + * + * Licensed under the GPL v2 or later. + */ +#include +#include + +int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen) +{ + /* Currently a stub; this will be expanded upon later. */ + return olpc_ec_cmd_x86(cmd, inbuf, inlen, outbuf, outlen); +} +EXPORT_SYMBOL_GPL(olpc_ec_cmd); diff --git a/include/linux/olpc-ec.h b/include/linux/olpc-ec.h new file mode 100644 index 000000000000..6d4e426d9fdc --- /dev/null +++ b/include/linux/olpc-ec.h @@ -0,0 +1,29 @@ +#ifndef _LINUX_OLPC_EC_H +#define _LINUX_OLPC_EC_H + +/* XO-1 EC commands */ +#define EC_FIRMWARE_REV 0x08 +#define EC_WRITE_SCI_MASK 0x1b +#define EC_WAKE_UP_WLAN 0x24 +#define EC_WLAN_LEAVE_RESET 0x25 +#define EC_READ_EB_MODE 0x2a +#define EC_SET_SCI_INHIBIT 0x32 +#define EC_SET_SCI_INHIBIT_RELEASE 0x34 +#define EC_WLAN_ENTER_RESET 0x35 +#define EC_WRITE_EXT_SCI_MASK 0x38 +#define EC_SCI_QUERY 0x84 +#define EC_EXT_SCI_QUERY 0x85 + +#ifdef CONFIG_OLPC + +extern int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, + size_t outlen); + +#else + +static inline int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, + size_t outlen) { return -ENODEV; } + +#endif /* CONFIG_OLPC */ + +#endif /* _LINUX_OLPC_EC_H */ -- cgit v1.2.3 From 3d26c20bae9e97c98f7240184427d3a38515d406 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Wed, 11 Jul 2012 17:40:25 -0700 Subject: Platform: OLPC: allow EC cmd to be overridden, and create a workqueue to call it This provides a new API allows different OLPC architectures to override the EC driver. x86 and ARM OLPC machines use completely different EC backends. The olpc_ec_cmd is synchronous, and waits for the workqueue to send the command to the EC. Multiple callers can run olpc_ec_cmd() at once, and they will by serialized and sleep while only one executes on the EC at a time. We don't provide an unregister function, as that doesn't make sense within the context of OLPC machines - there's only ever 1 EC, it's critical to functionality, and it certainly not hotpluggable. Signed-off-by: Andres Salomon Acked-by: Paul Fox Reviewed-by: Thomas Gleixner --- drivers/platform/olpc/olpc-ec.c | 112 +++++++++++++++++++++++++++++++++++++++- include/linux/olpc-ec.h | 6 +++ 2 files changed, 116 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c index 42026036cd3e..44e6a4fae79b 100644 --- a/drivers/platform/olpc/olpc-ec.c +++ b/drivers/platform/olpc/olpc-ec.c @@ -5,12 +5,120 @@ * * Licensed under the GPL v2 or later. */ +#include +#include +#include +#include #include +#include +#include #include +struct ec_cmd_desc { + u8 cmd; + u8 *inbuf, *outbuf; + size_t inlen, outlen; + + int err; + struct completion finished; + struct list_head node; + + void *priv; +}; + +static void olpc_ec_worker(struct work_struct *w); + +static DECLARE_WORK(ec_worker, olpc_ec_worker); +static LIST_HEAD(ec_cmd_q); +static DEFINE_SPINLOCK(ec_cmd_q_lock); + +static struct olpc_ec_driver *ec_driver; +static void *ec_cb_arg; +static DEFINE_MUTEX(ec_cb_lock); + +void olpc_ec_driver_register(struct olpc_ec_driver *drv, void *arg) +{ + ec_driver = drv; + ec_cb_arg = arg; +} +EXPORT_SYMBOL_GPL(olpc_ec_driver_register); + +static void olpc_ec_worker(struct work_struct *w) +{ + struct ec_cmd_desc *desc = NULL; + unsigned long flags; + + /* Grab the first pending command from the queue */ + spin_lock_irqsave(&ec_cmd_q_lock, flags); + if (!list_empty(&ec_cmd_q)) { + desc = list_first_entry(&ec_cmd_q, struct ec_cmd_desc, node); + list_del(&desc->node); + } + spin_unlock_irqrestore(&ec_cmd_q_lock, flags); + + /* Do we actually have anything to do? */ + if (!desc) + return; + + /* Protect the EC hw with a mutex; only run one cmd at a time */ + mutex_lock(&ec_cb_lock); + desc->err = ec_driver->ec_cmd(desc->cmd, desc->inbuf, desc->inlen, + desc->outbuf, desc->outlen, ec_cb_arg); + mutex_unlock(&ec_cb_lock); + + /* Finished, wake up olpc_ec_cmd() */ + complete(&desc->finished); + + /* Run the worker thread again in case there are more cmds pending */ + schedule_work(&ec_worker); +} + +/* + * Throw a cmd descripter onto the list. We now have SMP OLPC machines, so + * locking is pretty critical. + */ +static void queue_ec_descriptor(struct ec_cmd_desc *desc) +{ + unsigned long flags; + + INIT_LIST_HEAD(&desc->node); + + spin_lock_irqsave(&ec_cmd_q_lock, flags); + list_add_tail(&desc->node, &ec_cmd_q); + spin_unlock_irqrestore(&ec_cmd_q_lock, flags); + + schedule_work(&ec_worker); +} + int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen) { - /* Currently a stub; this will be expanded upon later. */ - return olpc_ec_cmd_x86(cmd, inbuf, inlen, outbuf, outlen); + struct ec_cmd_desc desc; + + /* XXX: this will be removed in later patches */ + /* Are we using old-style callers? */ + if (!ec_driver || !ec_driver->ec_cmd) + return olpc_ec_cmd_x86(cmd, inbuf, inlen, outbuf, outlen); + + /* Ensure a driver and ec hook have been registered */ + if (WARN_ON(!ec_driver || !ec_driver->ec_cmd)) + return -ENODEV; + + might_sleep(); + + desc.cmd = cmd; + desc.inbuf = inbuf; + desc.outbuf = outbuf; + desc.inlen = inlen; + desc.outlen = outlen; + desc.err = 0; + init_completion(&desc.finished); + + queue_ec_descriptor(&desc); + + /* Timeouts must be handled in the platform-specific EC hook */ + wait_for_completion(&desc.finished); + + /* The worker thread dequeues the cmd; no need to do anything here */ + return desc.err; } EXPORT_SYMBOL_GPL(olpc_ec_cmd); diff --git a/include/linux/olpc-ec.h b/include/linux/olpc-ec.h index 6d4e426d9fdc..231e96f5dfe2 100644 --- a/include/linux/olpc-ec.h +++ b/include/linux/olpc-ec.h @@ -14,8 +14,14 @@ #define EC_SCI_QUERY 0x84 #define EC_EXT_SCI_QUERY 0x85 +struct olpc_ec_driver { + int (*ec_cmd)(u8, u8 *, size_t, u8 *, size_t, void *); +}; + #ifdef CONFIG_OLPC +extern void olpc_ec_driver_register(struct olpc_ec_driver *drv, void *arg); + extern int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen); -- cgit v1.2.3 From ac2504151f5af27bbf0c0362b7da5951e05dfc43 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Fri, 13 Jul 2012 05:57:17 -0700 Subject: Platform: OLPC: turn EC driver into a platform_driver The 1.75-based OLPC EC driver already does this; let's do it for all EC drivers. This gives us nice suspend/resume hooks, amongst other things. We want to run the EC's suspend hooks later than other drivers (which may be setting wakeup masks or be running EC commands). We also want to run the EC's resume hooks earlier than other drivers (which may want to run EC commands). Signed-off-by: Andres Salomon Acked-by: Paul Fox Reviewed-by: Thomas Gleixner --- drivers/platform/olpc/olpc-ec.c | 48 +++++++++++++++++++++++++++++++++++++++++ include/linux/olpc-ec.h | 6 ++++++ 2 files changed, 54 insertions(+) (limited to 'include') diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c index 44e6a4fae79b..d00523c65191 100644 --- a/drivers/platform/olpc/olpc-ec.c +++ b/drivers/platform/olpc/olpc-ec.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -122,3 +123,50 @@ int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen) return desc.err; } EXPORT_SYMBOL_GPL(olpc_ec_cmd); + +static int olpc_ec_probe(struct platform_device *pdev) +{ + int err; + + if (!ec_driver) + return -ENODEV; + + err = ec_driver->probe ? ec_driver->probe(pdev) : 0; + + return err; +} + +static int olpc_ec_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + return ec_driver->suspend ? ec_driver->suspend(pdev) : 0; +} + +static int olpc_ec_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + return ec_driver->resume ? ec_driver->resume(pdev) : 0; +} + +static const struct dev_pm_ops olpc_ec_pm_ops = { + .suspend_late = olpc_ec_suspend, + .resume_early = olpc_ec_resume, +}; + +static struct platform_driver olpc_ec_plat_driver = { + .probe = olpc_ec_probe, + .driver = { + .name = "olpc-ec", + .pm = &olpc_ec_pm_ops, + }, +}; + +static int __init olpc_ec_init_module(void) +{ + return platform_driver_register(&olpc_ec_plat_driver); +} + +module_init(olpc_ec_init_module); + +MODULE_AUTHOR("Andres Salomon "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/olpc-ec.h b/include/linux/olpc-ec.h index 231e96f5dfe2..5bb6e760aa61 100644 --- a/include/linux/olpc-ec.h +++ b/include/linux/olpc-ec.h @@ -14,7 +14,13 @@ #define EC_SCI_QUERY 0x84 #define EC_EXT_SCI_QUERY 0x85 +struct platform_device; + struct olpc_ec_driver { + int (*probe)(struct platform_device *); + int (*suspend)(struct platform_device *); + int (*resume)(struct platform_device *); + int (*ec_cmd)(u8, u8 *, size_t, u8 *, size_t, void *); }; -- cgit v1.2.3 From 4f46f8ac80416b0e8fd3aba6a0d842205fb29140 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 30 Jul 2012 21:28:27 +0200 Subject: dmaengine: shdma: restore partial transfer calculation The recent shdma driver split has mistakenly removed support for partial DMA transfer size calculation on forced termination. This patch restores it. Signed-off-by: Guennadi Liakhovetski Acked-by: Vinod Koul Signed-off-by: Paul Mundt --- drivers/dma/sh/shdma-base.c | 9 +++++++++ drivers/dma/sh/shdma.c | 12 ++++++++++++ include/linux/shdma-base.h | 2 ++ 3 files changed, 23 insertions(+) (limited to 'include') diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 27f5c781fd73..f4cd946d259d 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -483,6 +483,7 @@ static struct shdma_desc *shdma_add_desc(struct shdma_chan *schan, new->mark = DESC_PREPARED; new->async_tx.flags = flags; new->direction = direction; + new->partial = 0; *len -= copy_size; if (direction == DMA_MEM_TO_MEM || direction == DMA_MEM_TO_DEV) @@ -644,6 +645,14 @@ static int shdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, case DMA_TERMINATE_ALL: spin_lock_irqsave(&schan->chan_lock, flags); ops->halt_channel(schan); + + if (ops->get_partial && !list_empty(&schan->ld_queue)) { + /* Record partial transfer */ + struct shdma_desc *desc = list_first_entry(&schan->ld_queue, + struct shdma_desc, node); + desc->partial = ops->get_partial(schan, desc); + } + spin_unlock_irqrestore(&schan->chan_lock, flags); shdma_chan_ld_cleanup(schan, true); diff --git a/drivers/dma/sh/shdma.c b/drivers/dma/sh/shdma.c index 027c9be97654..f41bcc5267fd 100644 --- a/drivers/dma/sh/shdma.c +++ b/drivers/dma/sh/shdma.c @@ -381,6 +381,17 @@ static bool sh_dmae_chan_irq(struct shdma_chan *schan, int irq) return true; } +static size_t sh_dmae_get_partial(struct shdma_chan *schan, + struct shdma_desc *sdesc) +{ + struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, + shdma_chan); + struct sh_dmae_desc *sh_desc = container_of(sdesc, + struct sh_dmae_desc, shdma_desc); + return (sh_desc->hw.tcr - sh_dmae_readl(sh_chan, TCR)) << + sh_chan->xmit_shift; +} + /* Called from error IRQ or NMI */ static bool sh_dmae_reset(struct sh_dmae_device *shdev) { @@ -632,6 +643,7 @@ static const struct shdma_ops sh_dmae_shdma_ops = { .start_xfer = sh_dmae_start_xfer, .embedded_desc = sh_dmae_embedded_desc, .chan_irq = sh_dmae_chan_irq, + .get_partial = sh_dmae_get_partial, }; static int __devinit sh_dmae_probe(struct platform_device *pdev) diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h index 93f9821554b6..a3728bf66f0e 100644 --- a/include/linux/shdma-base.h +++ b/include/linux/shdma-base.h @@ -50,6 +50,7 @@ struct shdma_desc { struct list_head node; struct dma_async_tx_descriptor async_tx; enum dma_transfer_direction direction; + size_t partial; dma_cookie_t cookie; int chunks; int mark; @@ -98,6 +99,7 @@ struct shdma_ops { void (*start_xfer)(struct shdma_chan *, struct shdma_desc *); struct shdma_desc *(*embedded_desc)(void *, int); bool (*chan_irq)(struct shdma_chan *, int); + size_t (*get_partial)(struct shdma_chan *, struct shdma_desc *); }; struct shdma_dev { -- cgit v1.2.3 From c83f6bf98dc1f1a194118b3830706cebbebda8c4 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 1 Aug 2012 12:24:18 +0200 Subject: block: add partition resize function to blkpg ioctl Add a new operation code (BLKPG_RESIZE_PARTITION) to the BLKPG ioctl that allows altering the size of an existing partition, even if it is currently in use. This patch converts hd_struct->nr_sects into sequence counter because One might extend a partition while IO is happening to it and update of nr_sects can be non-atomic on 32bit machines with 64bit sector_t. This can lead to issues like reading inconsistent size of a partition. Sequence counter have been used so that readers don't have to take bdev mutex lock as we call sector_in_part() very frequently. Now all the access to hd_struct->nr_sects should happen using sequence counter read/update helper functions part_nr_sects_read/part_nr_sects_write. There is one exception though, set_capacity()/get_capacity(). I think theoritically race should exist there too but this patch does not modify set_capacity()/get_capacity() due to sheer number of call sites and I am afraid that change might break something. I have left that as a TODO item. We can handle it later if need be. This patch does not introduce any new races as such w.r.t set_capacity()/get_capacity(). v2: Add CONFIG_LBDAF test to UP preempt case as suggested by Phillip. Signed-off-by: Vivek Goyal Signed-off-by: Phillip Susi Signed-off-by: Jens Axboe --- block/genhd.c | 20 ++++++++++++---- block/ioctl.c | 59 ++++++++++++++++++++++++++++++++++++++++++++--- block/partition-generic.c | 4 +++- include/linux/blkpg.h | 1 + include/linux/genhd.h | 57 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 132 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/block/genhd.c b/block/genhd.c index 9cf5583c90ff..cac7366957c3 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -154,7 +154,7 @@ struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter) part = rcu_dereference(ptbl->part[piter->idx]); if (!part) continue; - if (!part->nr_sects && + if (!part_nr_sects_read(part) && !(piter->flags & DISK_PITER_INCL_EMPTY) && !(piter->flags & DISK_PITER_INCL_EMPTY_PART0 && piter->idx == 0)) @@ -191,7 +191,7 @@ EXPORT_SYMBOL_GPL(disk_part_iter_exit); static inline int sector_in_part(struct hd_struct *part, sector_t sector) { return part->start_sect <= sector && - sector < part->start_sect + part->nr_sects; + sector < part->start_sect + part_nr_sects_read(part); } /** @@ -769,8 +769,8 @@ void __init printk_all_partitions(void) printk("%s%s %10llu %s %s", is_part0 ? "" : " ", bdevt_str(part_devt(part), devt_buf), - (unsigned long long)part->nr_sects >> 1, - disk_name(disk, part->partno, name_buf), + (unsigned long long)part_nr_sects_read(part) >> 1 + , disk_name(disk, part->partno, name_buf), uuid_buf); if (is_part0) { if (disk->driverfs_dev != NULL && @@ -862,7 +862,7 @@ static int show_partition(struct seq_file *seqf, void *v) while ((part = disk_part_iter_next(&piter))) seq_printf(seqf, "%4d %7d %10llu %s\n", MAJOR(part_devt(part)), MINOR(part_devt(part)), - (unsigned long long)part->nr_sects >> 1, + (unsigned long long)part_nr_sects_read(part) >> 1, disk_name(sgp, part->partno, buf)); disk_part_iter_exit(&piter); @@ -1268,6 +1268,16 @@ struct gendisk *alloc_disk_node(int minors, int node_id) } disk->part_tbl->part[0] = &disk->part0; + /* + * set_capacity() and get_capacity() currently don't use + * seqcounter to read/update the part0->nr_sects. Still init + * the counter as we can read the sectors in IO submission + * patch using seqence counters. + * + * TODO: Ideally set_capacity() and get_capacity() should be + * converted to make use of bd_mutex and sequence counters. + */ + seqcount_init(&disk->part0.nr_sects_seq); hd_ref_init(&disk->part0); disk->minors = minors; diff --git a/block/ioctl.c b/block/ioctl.c index ba15b2dbfb98..4476e0e85d16 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -13,7 +13,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user { struct block_device *bdevp; struct gendisk *disk; - struct hd_struct *part; + struct hd_struct *part, *lpart; struct blkpg_ioctl_arg a; struct blkpg_partition p; struct disk_part_iter piter; @@ -36,8 +36,8 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user case BLKPG_ADD_PARTITION: start = p.start >> 9; length = p.length >> 9; - /* check for fit in a hd_struct */ - if (sizeof(sector_t) == sizeof(long) && + /* check for fit in a hd_struct */ + if (sizeof(sector_t) == sizeof(long) && sizeof(long long) > sizeof(long)) { long pstart = start, plength = length; if (pstart != start || plength != length @@ -91,6 +91,59 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user mutex_unlock(&bdevp->bd_mutex); bdput(bdevp); + return 0; + case BLKPG_RESIZE_PARTITION: + start = p.start >> 9; + /* new length of partition in bytes */ + length = p.length >> 9; + /* check for fit in a hd_struct */ + if (sizeof(sector_t) == sizeof(long) && + sizeof(long long) > sizeof(long)) { + long pstart = start, plength = length; + if (pstart != start || plength != length + || pstart < 0 || plength < 0) + return -EINVAL; + } + part = disk_get_part(disk, partno); + if (!part) + return -ENXIO; + bdevp = bdget(part_devt(part)); + if (!bdevp) { + disk_put_part(part); + return -ENOMEM; + } + mutex_lock(&bdevp->bd_mutex); + mutex_lock_nested(&bdev->bd_mutex, 1); + if (start != part->start_sect) { + mutex_unlock(&bdevp->bd_mutex); + mutex_unlock(&bdev->bd_mutex); + bdput(bdevp); + disk_put_part(part); + return -EINVAL; + } + /* overlap? */ + disk_part_iter_init(&piter, disk, + DISK_PITER_INCL_EMPTY); + while ((lpart = disk_part_iter_next(&piter))) { + if (lpart->partno != partno && + !(start + length <= lpart->start_sect || + start >= lpart->start_sect + lpart->nr_sects) + ) { + disk_part_iter_exit(&piter); + mutex_unlock(&bdevp->bd_mutex); + mutex_unlock(&bdev->bd_mutex); + bdput(bdevp); + disk_put_part(part); + return -EBUSY; + } + } + disk_part_iter_exit(&piter); + part_nr_sects_write(part, (sector_t)length); + i_size_write(bdevp->bd_inode, p.length); + mutex_unlock(&bdevp->bd_mutex); + mutex_unlock(&bdev->bd_mutex); + bdput(bdevp); + disk_put_part(part); return 0; default: return -EINVAL; diff --git a/block/partition-generic.c b/block/partition-generic.c index 6df5d6928a44..f1d14519cc04 100644 --- a/block/partition-generic.c +++ b/block/partition-generic.c @@ -84,7 +84,7 @@ ssize_t part_size_show(struct device *dev, struct device_attribute *attr, char *buf) { struct hd_struct *p = dev_to_part(dev); - return sprintf(buf, "%llu\n",(unsigned long long)p->nr_sects); + return sprintf(buf, "%llu\n",(unsigned long long)part_nr_sects_read(p)); } static ssize_t part_ro_show(struct device *dev, @@ -294,6 +294,8 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno, err = -ENOMEM; goto out_free; } + + seqcount_init(&p->nr_sects_seq); pdev = part_to_dev(p); p->start_sect = start; diff --git a/include/linux/blkpg.h b/include/linux/blkpg.h index faf8a45af210..a8519446c111 100644 --- a/include/linux/blkpg.h +++ b/include/linux/blkpg.h @@ -40,6 +40,7 @@ struct blkpg_ioctl_arg { /* The subfunctions (for the op field) */ #define BLKPG_ADD_PARTITION 1 #define BLKPG_DEL_PARTITION 2 +#define BLKPG_RESIZE_PARTITION 3 /* Sizes of name fields. Unused at present. */ #define BLKPG_DEVNAMELTH 64 diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 017a7fb5a1fc..b88723b81b3d 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -98,7 +98,13 @@ struct partition_meta_info { struct hd_struct { sector_t start_sect; + /* + * nr_sects is protected by sequence counter. One might extend a + * partition while IO is happening to it and update of nr_sects + * can be non-atomic on 32bit machines with 64bit sector_t. + */ sector_t nr_sects; + seqcount_t nr_sects_seq; sector_t alignment_offset; unsigned int discard_alignment; struct device __dev; @@ -648,6 +654,57 @@ static inline void hd_struct_put(struct hd_struct *part) __delete_partition(part); } +/* + * Any access of part->nr_sects which is not protected by partition + * bd_mutex or gendisk bdev bd_mutex, should be done using this + * accessor function. + * + * Code written along the lines of i_size_read() and i_size_write(). + * CONFIG_PREEMPT case optimizes the case of UP kernel with preemption + * on. + */ +static inline sector_t part_nr_sects_read(struct hd_struct *part) +{ +#if BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_SMP) + sector_t nr_sects; + unsigned seq; + do { + seq = read_seqcount_begin(&part->nr_sects_seq); + nr_sects = part->nr_sects; + } while (read_seqcount_retry(&part->nr_sects_seq, seq)); + return nr_sects; +#elif BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_PREEMPT) + sector_t nr_sects; + + preempt_disable(); + nr_sects = part->nr_sects; + preempt_enable(); + return nr_sects; +#else + return part->nr_sects; +#endif +} + +/* + * Should be called with mutex lock held (typically bd_mutex) of partition + * to provide mutual exlusion among writers otherwise seqcount might be + * left in wrong state leaving the readers spinning infinitely. + */ +static inline void part_nr_sects_write(struct hd_struct *part, sector_t size) +{ +#if BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_SMP) + write_seqcount_begin(&part->nr_sects_seq); + part->nr_sects = size; + write_seqcount_end(&part->nr_sects_seq); +#elif BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_PREEMPT) + preempt_disable(); + part->nr_sects = size; + preempt_enable(); +#else + part->nr_sects = size; +#endif +} + #else /* CONFIG_BLOCK */ static inline void printk_all_partitions(void) { } -- cgit v1.2.3 From 068535f1fef4c90aee23eb7b9b9a71c5b72d7cd0 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 1 Aug 2012 07:56:16 -0400 Subject: locks: remove unused lm_release_private In commit 3b6e2723f32d ("locks: prevent side-effects of locks_release_private before file_lock is initialized") we removed the last user of lm_release_private without removing the field itself. Signed-off-by: J. Bruce Fields Signed-off-by: Linus Torvalds --- Documentation/filesystems/Locking | 2 -- fs/locks.c | 6 +----- include/linux/fs.h | 1 - 3 files changed, 1 insertion(+), 8 deletions(-) (limited to 'include') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 2db1900d7538..7f647e17830c 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -359,7 +359,6 @@ prototypes: int (*lm_compare_owner)(struct file_lock *, struct file_lock *); void (*lm_notify)(struct file_lock *); /* unblock callback */ int (*lm_grant)(struct file_lock *, struct file_lock *, int); - void (*lm_release_private)(struct file_lock *); void (*lm_break)(struct file_lock *); /* break_lease callback */ int (*lm_change)(struct file_lock **, int); @@ -368,7 +367,6 @@ locking rules: lm_compare_owner: yes no lm_notify: yes no lm_grant: no no -lm_release_private: maybe no lm_break: yes no lm_change yes no diff --git a/fs/locks.c b/fs/locks.c index cdcf219a7391..7e81bfc75164 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -200,11 +200,7 @@ void locks_release_private(struct file_lock *fl) fl->fl_ops->fl_release_private(fl); fl->fl_ops = NULL; } - if (fl->fl_lmops) { - if (fl->fl_lmops->lm_release_private) - fl->fl_lmops->lm_release_private(fl); - fl->fl_lmops = NULL; - } + fl->fl_lmops = NULL; } EXPORT_SYMBOL_GPL(locks_release_private); diff --git a/include/linux/fs.h b/include/linux/fs.h index d7eed5b98ae2..4ba5c8715523 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1162,7 +1162,6 @@ struct lock_manager_operations { int (*lm_compare_owner)(struct file_lock *, struct file_lock *); void (*lm_notify)(struct file_lock *); /* unblock callback */ int (*lm_grant)(struct file_lock *, struct file_lock *, int); - void (*lm_release_private)(struct file_lock *); void (*lm_break)(struct file_lock *); int (*lm_change)(struct file_lock **, int); }; -- cgit v1.2.3 From 30b678d844af3305cda5953467005cebb5d7b687 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 30 Jul 2012 15:57:00 +0000 Subject: net: Allow driver to limit number of GSO segments per skb A peer (or local user) may cause TCP to use a nominal MSS of as little as 88 (actual MSS of 76 with timestamps). Given that we have a sufficiently prodigious local sender and the peer ACKs quickly enough, it is nevertheless possible to grow the window for such a connection to the point that we will try to send just under 64K at once. This results in a single skb that expands to 861 segments. In some drivers with TSO support, such an skb will require hundreds of DMA descriptors; a substantial fraction of a TX ring or even more than a full ring. The TX queue selected for the skb may stall and trigger the TX watchdog repeatedly (since the problem skb will be retried after the TX reset). This particularly affects sfc, for which the issue is designated as CVE-2012-3412. Therefore: 1. Add the field net_device::gso_max_segs holding the device-specific limit. 2. In netif_skb_features(), if the number of segments is too high then mask out GSO features to force fall back to software GSO. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 4 ++++ 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index eb06e58bed0b..a9db4f33407f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1300,6 +1300,8 @@ struct net_device { /* for setting kernel sock attribute on TCP connection setup */ #define GSO_MAX_SIZE 65536 unsigned int gso_max_size; +#define GSO_MAX_SEGS 65535 + u16 gso_max_segs; #ifdef CONFIG_DCB /* Data Center Bridging netlink ops */ diff --git a/net/core/dev.c b/net/core/dev.c index 0cb3fe8d8e72..f91abf800161 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2134,6 +2134,9 @@ netdev_features_t netif_skb_features(struct sk_buff *skb) __be16 protocol = skb->protocol; netdev_features_t features = skb->dev->features; + if (skb_shinfo(skb)->gso_segs > skb->dev->gso_max_segs) + features &= ~NETIF_F_GSO_MASK; + if (protocol == htons(ETH_P_8021Q)) { struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; protocol = veh->h_vlan_encapsulated_proto; @@ -5986,6 +5989,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, dev_net_set(dev, &init_net); dev->gso_max_size = GSO_MAX_SIZE; + dev->gso_max_segs = GSO_MAX_SEGS; INIT_LIST_HEAD(&dev->napi_list); INIT_LIST_HEAD(&dev->unreg_list); -- cgit v1.2.3 From 1485348d2424e1131ea42efc033cbd9366462b01 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 30 Jul 2012 16:11:42 +0000 Subject: tcp: Apply device TSO segment limit earlier Cache the device gso_max_segs in sock::sk_gso_max_segs and use it to limit the size of TSO skbs. This avoids the need to fall back to software GSO for local TCP senders. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/net/sock.h | 2 ++ net/core/sock.c | 1 + net/ipv4/tcp.c | 4 +++- net/ipv4/tcp_cong.c | 3 ++- net/ipv4/tcp_output.c | 21 ++++++++++++--------- 5 files changed, 20 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index b3730239bf18..72132aef53fc 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -218,6 +218,7 @@ struct cg_proto; * @sk_route_nocaps: forbidden route capabilities (e.g NETIF_F_GSO_MASK) * @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4) * @sk_gso_max_size: Maximum GSO segment size to build + * @sk_gso_max_segs: Maximum number of GSO segments * @sk_lingertime: %SO_LINGER l_linger setting * @sk_backlog: always used with the per-socket spinlock held * @sk_callback_lock: used with the callbacks in the end of this struct @@ -338,6 +339,7 @@ struct sock { netdev_features_t sk_route_nocaps; int sk_gso_type; unsigned int sk_gso_max_size; + u16 sk_gso_max_segs; int sk_rcvlowat; unsigned long sk_lingertime; struct sk_buff_head sk_error_queue; diff --git a/net/core/sock.c b/net/core/sock.c index 6b654b3ddfda..8f67ced8d6a8 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1458,6 +1458,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst) } else { sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM; sk->sk_gso_max_size = dst->dev->gso_max_size; + sk->sk_gso_max_segs = dst->dev->gso_max_segs; } } } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index e7e6eeae49c0..2109ff4a1daf 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -811,7 +811,9 @@ static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, old_size_goal + mss_now > xmit_size_goal)) { xmit_size_goal = old_size_goal; } else { - tp->xmit_size_goal_segs = xmit_size_goal / mss_now; + tp->xmit_size_goal_segs = + min_t(u16, xmit_size_goal / mss_now, + sk->sk_gso_max_segs); xmit_size_goal = tp->xmit_size_goal_segs * mss_now; } } diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 4d4db16e336e..1432cdb0644c 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -291,7 +291,8 @@ bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight) left = tp->snd_cwnd - in_flight; if (sk_can_gso(sk) && left * sysctl_tcp_tso_win_divisor < tp->snd_cwnd && - left * tp->mss_cache < sk->sk_gso_max_size) + left * tp->mss_cache < sk->sk_gso_max_size && + left < sk->sk_gso_max_segs) return true; return left <= tcp_max_tso_deferred_mss(tp); } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 3f1bcff0b10b..a7b3ec9b6c3e 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1522,21 +1522,21 @@ static void tcp_cwnd_validate(struct sock *sk) * when we would be allowed to send the split-due-to-Nagle skb fully. */ static unsigned int tcp_mss_split_point(const struct sock *sk, const struct sk_buff *skb, - unsigned int mss_now, unsigned int cwnd) + unsigned int mss_now, unsigned int max_segs) { const struct tcp_sock *tp = tcp_sk(sk); - u32 needed, window, cwnd_len; + u32 needed, window, max_len; window = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq; - cwnd_len = mss_now * cwnd; + max_len = mss_now * max_segs; - if (likely(cwnd_len <= window && skb != tcp_write_queue_tail(sk))) - return cwnd_len; + if (likely(max_len <= window && skb != tcp_write_queue_tail(sk))) + return max_len; needed = min(skb->len, window); - if (cwnd_len <= needed) - return cwnd_len; + if (max_len <= needed) + return max_len; return needed - needed % mss_now; } @@ -1765,7 +1765,8 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb) limit = min(send_win, cong_win); /* If a full-sized TSO skb can be sent, do it. */ - if (limit >= sk->sk_gso_max_size) + if (limit >= min_t(unsigned int, sk->sk_gso_max_size, + sk->sk_gso_max_segs * tp->mss_cache)) goto send_now; /* Middle in queue won't get any more data, full sendable already? */ @@ -1999,7 +2000,9 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, limit = mss_now; if (tso_segs > 1 && !tcp_urg_mode(tp)) limit = tcp_mss_split_point(sk, skb, mss_now, - cwnd_quota); + min_t(unsigned int, + cwnd_quota, + sk->sk_gso_max_segs)); if (skb->len > limit && unlikely(tso_fragment(sk, skb, limit, mss_now, gfp))) -- cgit v1.2.3 From e3c0d04750751389d5116267f8cf4687444d9a50 Mon Sep 17 00:00:00 2001 From: Fan Du Date: Mon, 30 Jul 2012 21:43:54 +0000 Subject: Fix unexpected SA hard expiration after changing date After SA is setup, one timer is armed to detect soft/hard expiration, however the timer handler uses xtime to do the math. This makes hard expiration occurs first before soft expiration after setting new date with big interval. As a result new child SA is deleted before rekeying the new one. Signed-off-by: Fan Du Signed-off-by: David S. Miller --- include/net/xfrm.h | 4 ++++ net/xfrm/xfrm_state.c | 21 +++++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index d9509eb29b80..62b619e82a90 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -213,6 +213,9 @@ struct xfrm_state { struct xfrm_lifetime_cur curlft; struct tasklet_hrtimer mtimer; + /* used to fix curlft->add_time when changing date */ + long saved_tmo; + /* Last used time */ unsigned long lastused; @@ -238,6 +241,7 @@ static inline struct net *xs_net(struct xfrm_state *x) /* xflags - make enum if more show up */ #define XFRM_TIME_DEFER 1 +#define XFRM_SOFT_EXPIRE 2 enum { XFRM_STATE_VOID, diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 5b228f97d4b3..87cd0e4d4282 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -415,8 +415,17 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me) if (x->lft.hard_add_expires_seconds) { long tmo = x->lft.hard_add_expires_seconds + x->curlft.add_time - now; - if (tmo <= 0) - goto expired; + if (tmo <= 0) { + if (x->xflags & XFRM_SOFT_EXPIRE) { + /* enter hard expire without soft expire first?! + * setting a new date could trigger this. + * workarbound: fix x->curflt.add_time by below: + */ + x->curlft.add_time = now - x->saved_tmo - 1; + tmo = x->lft.hard_add_expires_seconds - x->saved_tmo; + } else + goto expired; + } if (tmo < next) next = tmo; } @@ -433,10 +442,14 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me) if (x->lft.soft_add_expires_seconds) { long tmo = x->lft.soft_add_expires_seconds + x->curlft.add_time - now; - if (tmo <= 0) + if (tmo <= 0) { warn = 1; - else if (tmo < next) + x->xflags &= ~XFRM_SOFT_EXPIRE; + } else if (tmo < next) { next = tmo; + x->xflags |= XFRM_SOFT_EXPIRE; + x->saved_tmo = tmo; + } } if (x->lft.soft_use_expires_seconds) { long tmo = x->lft.soft_use_expires_seconds + -- cgit v1.2.3 From c6e666345e1b79c62ba82339cc7d55a89cb73f88 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 2 Aug 2012 09:48:50 +0200 Subject: block: split discard into aligned requests When a disk has large discard_granularity and small max_discard_sectors, discards are not split with optimal alignment. In the limit case of discard_granularity == max_discard_sectors, no request could be aligned correctly, so in fact you might end up with no discarded logical blocks at all. Another example that helps showing the condition in the patch is with discard_granularity == 64, max_discard_sectors == 128. A request that is submitted for 256 sectors 2..257 will be split in two: 2..129, 130..257. However, only 2 aligned blocks out of 3 are included in the request; 128..191 may be left intact and not discarded. With this patch, the first request will be truncated to ensure good alignment of what's left, and the split will be 2..127, 128..255, 256..257. The patch will also take into account the discard_alignment. At most one extra request will be introduced, because the first request will be reduced by at most granularity-1 sectors, and granularity must be less than max_discard_sectors. Subsequent requests will run on round_down(max_discard_sectors, granularity) sectors, as in the current code. Signed-off-by: Paolo Bonzini Acked-by: Vivek Goyal Tested-by: Mike Snitzer Signed-off-by: Jens Axboe --- block/blk-lib.c | 34 ++++++++++++++++++++++++---------- include/linux/blkdev.h | 10 ++++++++++ 2 files changed, 34 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/block/blk-lib.c b/block/blk-lib.c index 16b06f62e68c..19cc761cacb2 100644 --- a/block/blk-lib.c +++ b/block/blk-lib.c @@ -44,7 +44,7 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector, struct request_queue *q = bdev_get_queue(bdev); int type = REQ_WRITE | REQ_DISCARD; unsigned int max_discard_sectors; - unsigned int granularity; + unsigned int granularity, alignment, mask; struct bio_batch bb; struct bio *bio; int ret = 0; @@ -57,10 +57,12 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector, /* Zero-sector (unknown) and one-sector granularities are the same. */ granularity = max(q->limits.discard_granularity >> 9, 1U); + mask = granularity - 1; + alignment = (bdev_discard_alignment(bdev) >> 9) & mask; /* * Ensure that max_discard_sectors is of the proper - * granularity + * granularity, so that requests stay aligned after a split. */ max_discard_sectors = min(q->limits.max_discard_sectors, UINT_MAX >> 9); max_discard_sectors = round_down(max_discard_sectors, granularity); @@ -80,25 +82,37 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector, bb.wait = &wait; while (nr_sects) { + unsigned int req_sects; + sector_t end_sect; + bio = bio_alloc(gfp_mask, 1); if (!bio) { ret = -ENOMEM; break; } + req_sects = min_t(sector_t, nr_sects, max_discard_sectors); + + /* + * If splitting a request, and the next starting sector would be + * misaligned, stop the discard at the previous aligned sector. + */ + end_sect = sector + req_sects; + if (req_sects < nr_sects && (end_sect & mask) != alignment) { + end_sect = + round_down(end_sect - alignment, granularity) + + alignment; + req_sects = end_sect - sector; + } + bio->bi_sector = sector; bio->bi_end_io = bio_batch_end_io; bio->bi_bdev = bdev; bio->bi_private = &bb; - if (nr_sects > max_discard_sectors) { - bio->bi_size = max_discard_sectors << 9; - nr_sects -= max_discard_sectors; - sector += max_discard_sectors; - } else { - bio->bi_size = nr_sects << 9; - nr_sects = 0; - } + bio->bi_size = req_sects << 9; + nr_sects -= req_sects; + sector = end_sect; atomic_inc(&bb.done); submit_bio(type, bio); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 4e72a9d48232..281516ae8b4e 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1139,6 +1139,16 @@ static inline int queue_limit_discard_alignment(struct queue_limits *lim, sector & (lim->discard_granularity - 1); } +static inline int bdev_discard_alignment(struct block_device *bdev) +{ + struct request_queue *q = bdev_get_queue(bdev); + + if (bdev != bdev->bd_contains) + return bdev->bd_part->discard_alignment; + + return q->limits.discard_alignment; +} + static inline unsigned int queue_discard_zeroes_data(struct request_queue *q) { if (q->limits.max_discard_sectors && q->limits.discard_zeroes_data == 1) -- cgit v1.2.3 From 03f6b0843ad6512f27bc2e48f04c21065311e03e Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Wed, 1 Aug 2012 15:58:42 -0500 Subject: cfg80211: add channel flag to prohibit OFDM operation Currently the only way for wireless drivers to tell whether or not OFDM is allowed on the current channel is to check the regulatory information. However, this requires hodling cfg80211_mutex, which is not visible to the drivers. Other regulatory restrictions are provided as flags in the channel definition, so let's do similarly with OFDM. This patch adds a new flag, IEEE80211_CHAN_NO_OFDM, to tell drivers that OFDM on a channel is not allowed. This flag is set on any channels for which regulatory indicates that OFDM is prohibited. Signed-off-by: Seth Forshee Tested-by: Arend van Spriel Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ net/wireless/reg.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 493fa0c79005..3d254e10ff30 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -96,6 +96,7 @@ enum ieee80211_band { * is not permitted. * @IEEE80211_CHAN_NO_HT40MINUS: extension channel below this channel * is not permitted. + * @IEEE80211_CHAN_NO_OFDM: OFDM is not allowed on this channel. */ enum ieee80211_channel_flags { IEEE80211_CHAN_DISABLED = 1<<0, @@ -104,6 +105,7 @@ enum ieee80211_channel_flags { IEEE80211_CHAN_RADAR = 1<<3, IEEE80211_CHAN_NO_HT40PLUS = 1<<4, IEEE80211_CHAN_NO_HT40MINUS = 1<<5, + IEEE80211_CHAN_NO_OFDM = 1<<6, }; #define IEEE80211_CHAN_NO_HT40 \ diff --git a/net/wireless/reg.c b/net/wireless/reg.c index a9175fedeb59..cbf30de79c69 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -680,6 +680,8 @@ static u32 map_regdom_flags(u32 rd_flags) channel_flags |= IEEE80211_CHAN_NO_IBSS; if (rd_flags & NL80211_RRF_DFS) channel_flags |= IEEE80211_CHAN_RADAR; + if (rd_flags & NL80211_RRF_NO_OFDM) + channel_flags |= IEEE80211_CHAN_NO_OFDM; return channel_flags; } -- cgit v1.2.3 From c263c2c1ad615e935d563cd7be11d417f94895d9 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Mon, 23 Jul 2012 18:20:12 +0200 Subject: bcma: BCM43228 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/host_pci.c | 1 + drivers/bcma/sprom.c | 4 +++- include/linux/bcma/bcma_driver_chipcommon.h | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index 11b32d2642df..a6e5672c67e7 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -272,6 +272,7 @@ static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = { { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) }, { 0, }, }; diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index 26823d97fd9f..9ea4627dc0c2 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -507,7 +507,9 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus) /* for these chips OTP is always available */ present = true; break; - + case BCMA_CHIP_ID_BCM43228: + present = chip_status & BCMA_CC_CHIPST_43228_OTP_PRESENT; + break; default: present = false; break; diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index 3c80885fa829..d323a4b4143c 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -89,6 +89,12 @@ #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_43228_ILP_DIV_EN 0x00000001 +#define BCMA_CC_CHIPST_43228_OTP_PRESENT 0x00000002 +#define BCMA_CC_CHIPST_43228_SERDES_REFCLK_PADSEL 0x00000004 +#define BCMA_CC_CHIPST_43228_SDIO_MODE 0x00000008 +#define BCMA_CC_CHIPST_43228_SDIO_OTP_PRESENT 0x00000010 +#define BCMA_CC_CHIPST_43228_SDIO_RESET 0x00000020 #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 */ -- cgit v1.2.3 From f6166384095b7ecf77752b5e9096e6d03d75f7ae Mon Sep 17 00:00:00 2001 From: Peng Tao Date: Thu, 2 Aug 2012 15:36:09 +0300 Subject: NFS41: add pg_layout_private to nfs_pageio_descriptor To allow layout driver to pass private information around pg_init/pg_doio. Signed-off-by: Peng Tao Signed-off-by: Boaz Harrosh Signed-off-by: Trond Myklebust --- fs/nfs/pagelist.c | 2 ++ include/linux/nfs_page.h | 1 + include/linux/nfs_xdr.h | 1 + 3 files changed, 4 insertions(+) (limited to 'include') diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 1a6732ed04a4..311a79681e2b 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -49,6 +49,7 @@ void nfs_pgheader_init(struct nfs_pageio_descriptor *desc, hdr->io_start = req_offset(hdr->req); hdr->good_bytes = desc->pg_count; hdr->dreq = desc->pg_dreq; + hdr->layout_private = desc->pg_layout_private; hdr->release = release; hdr->completion_ops = desc->pg_completion_ops; if (hdr->completion_ops->init_hdr) @@ -268,6 +269,7 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc, desc->pg_error = 0; desc->pg_lseg = NULL; desc->pg_dreq = NULL; + desc->pg_layout_private = NULL; } EXPORT_SYMBOL_GPL(nfs_pageio_init); diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 880805774f9f..92ce5783b707 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -69,6 +69,7 @@ struct nfs_pageio_descriptor { const struct nfs_pgio_completion_ops *pg_completion_ops; struct pnfs_layout_segment *pg_lseg; struct nfs_direct_req *pg_dreq; + void *pg_layout_private; }; #define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags)) diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 00485e084394..ac7c8ae254f2 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1248,6 +1248,7 @@ struct nfs_pgio_header { void (*release) (struct nfs_pgio_header *hdr); const struct nfs_pgio_completion_ops *completion_ops; struct nfs_direct_req *dreq; + void *layout_private; spinlock_t lock; /* fields protected by lock */ int pnfs_error; -- cgit v1.2.3 From 85b9f66a41eb8ee3f1dfc95707412705463cdd97 Mon Sep 17 00:00:00 2001 From: Asias He Date: Thu, 2 Aug 2012 23:42:04 +0200 Subject: block: Add blk_bio_map_sg() helper Add a helper to map a bio to a scatterlist, modelled after blk_rq_map_sg. This helper is useful for any driver that wants to create a scatterlist from its ->make_request_fn method. Changes in v2: - Use __blk_segment_map_sg to avoid duplicated code - Add cocbook style function comment Cc: Rusty Russell Cc: Christoph Hellwig Cc: Tejun Heo Cc: Shaohua Li Cc: "Michael S. Tsirkin" Cc: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: virtualization@lists.linux-foundation.org Signed-off-by: Christoph Hellwig Signed-off-by: Minchan Kim Signed-off-by: Asias He Signed-off-by: Jens Axboe --- block/blk-merge.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/blkdev.h | 2 ++ 2 files changed, 39 insertions(+) (limited to 'include') diff --git a/block/blk-merge.c b/block/blk-merge.c index 576b68e79248..e76279e41162 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -209,6 +209,43 @@ int blk_rq_map_sg(struct request_queue *q, struct request *rq, } EXPORT_SYMBOL(blk_rq_map_sg); +/** + * blk_bio_map_sg - map a bio to a scatterlist + * @q: request_queue in question + * @bio: bio being mapped + * @sglist: scatterlist being mapped + * + * Note: + * Caller must make sure sg can hold bio->bi_phys_segments entries + * + * Will return the number of sg entries setup + */ +int blk_bio_map_sg(struct request_queue *q, struct bio *bio, + struct scatterlist *sglist) +{ + struct bio_vec *bvec, *bvprv; + struct scatterlist *sg; + int nsegs, cluster; + unsigned long i; + + nsegs = 0; + cluster = blk_queue_cluster(q); + + bvprv = NULL; + sg = NULL; + bio_for_each_segment(bvec, bio, i) { + __blk_segment_map_sg(q, bvec, sglist, &bvprv, &sg, + &nsegs, &cluster); + } /* segments in bio */ + + if (sg) + sg_mark_end(sg); + + BUG_ON(bio->bi_phys_segments && nsegs > bio->bi_phys_segments); + return nsegs; +} +EXPORT_SYMBOL(blk_bio_map_sg); + static inline int ll_new_hw_segment(struct request_queue *q, struct request *req, struct bio *bio) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 281516ae8b4e..dc632975d54f 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -894,6 +894,8 @@ extern void blk_queue_flush_queueable(struct request_queue *q, bool queueable); extern struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev); extern int blk_rq_map_sg(struct request_queue *, struct request *, struct scatterlist *); +extern int blk_bio_map_sg(struct request_queue *q, struct bio *bio, + struct scatterlist *sglist); extern void blk_dump_rq_flags(struct request *, char *); extern long nr_blockdev_pages(void); -- cgit v1.2.3 From 095adbb6441172985f5ddc3b9e88cb3191bdeac4 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Tue, 31 Jul 2012 17:41:09 +0200 Subject: ACPI: Only count valid srat memory structures Otherwise you could run into: WARN_ON in numa_register_memblks(), because node_possible_map is zero References: https://bugzilla.novell.com/show_bug.cgi?id=757888 On this machine (ProLiant ML570 G3) the SRAT table contains: - No processor affinities - One memory affinity structure (which is set disabled) CC: Per Jessen CC: Andi Kleen Signed-off-by: Thomas Renninger Signed-off-by: Len Brown --- arch/ia64/kernel/acpi.c | 5 +++-- arch/x86/mm/srat.c | 15 ++++++++------- drivers/acpi/numa.c | 8 +++++--- include/linux/acpi.h | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index 6f38b6120d96..440578850ae5 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -497,7 +497,7 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) srat_num_cpus++; } -void __init +int __init acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) { unsigned long paddr, size; @@ -512,7 +512,7 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) /* Ignore disabled entries */ if (!(ma->flags & ACPI_SRAT_MEM_ENABLED)) - return; + return -1; /* record this node in proximity bitmap */ pxm_bit_set(pxm); @@ -531,6 +531,7 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) p->size = size; p->nid = pxm; num_node_memblks++; + return 0; } void __init acpi_numa_arch_fixup(void) diff --git a/arch/x86/mm/srat.c b/arch/x86/mm/srat.c index 4599c3e8bcb6..4ddf497ca65b 100644 --- a/arch/x86/mm/srat.c +++ b/arch/x86/mm/srat.c @@ -142,23 +142,23 @@ static inline int save_add_info(void) {return 0;} #endif /* Callback for parsing of the Proximity Domain <-> Memory Area mappings */ -void __init +int __init acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) { u64 start, end; int node, pxm; if (srat_disabled()) - return; + return -1; if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) { bad_srat(); - return; + return -1; } if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0) - return; + return -1; if ((ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && !save_add_info()) - return; + return -1; start = ma->base_address; end = start + ma->length; pxm = ma->proximity_domain; @@ -168,12 +168,12 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) if (node < 0) { printk(KERN_ERR "SRAT: Too many proximity domains.\n"); bad_srat(); - return; + return -1; } if (numa_add_memblk(node, start, end) < 0) { bad_srat(); - return; + return -1; } node_set(node, numa_nodes_parsed); @@ -181,6 +181,7 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) printk(KERN_INFO "SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]\n", node, pxm, (unsigned long long) start, (unsigned long long) end - 1); + return 0; } void __init acpi_numa_arch_fixup(void) {} diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 2a6399345c85..cb31298ca684 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -237,6 +237,8 @@ acpi_parse_processor_affinity(struct acpi_subtable_header *header, return 0; } +static int __initdata parsed_numa_memblks; + static int __init acpi_parse_memory_affinity(struct acpi_subtable_header * header, const unsigned long end) @@ -250,8 +252,8 @@ acpi_parse_memory_affinity(struct acpi_subtable_header * header, acpi_table_print_srat_entry(header); /* let architecture-dependent part to do it */ - acpi_numa_memory_affinity_init(memory_affinity); - + if (!acpi_numa_memory_affinity_init(memory_affinity)) + parsed_numa_memblks++; return 0; } @@ -306,7 +308,7 @@ int __init acpi_numa_init(void) if (cnt < 0) return cnt; - else if (cnt == 0) + else if (!parsed_numa_memblks) return -ENOENT; return 0; } diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 3ad510b25283..4f2a76224509 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -96,7 +96,7 @@ void acpi_table_print_madt_entry (struct acpi_subtable_header *madt); void acpi_numa_slit_init (struct acpi_table_slit *slit); void acpi_numa_processor_affinity_init (struct acpi_srat_cpu_affinity *pa); void acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa); -void acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma); +int acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma); void acpi_numa_arch_fixup(void); #ifdef CONFIG_ACPI_HOTPLUG_CPU -- cgit v1.2.3 From 76582d0a68ab6c46987dd678165c26ad81c44cdb Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 25 Jul 2012 16:24:49 +0200 Subject: iommu: Include linux/types.h The linux/iommu.h header uses types defined in linux/types.h but doesn't include it. Signed-off-by: Thierry Reding Signed-off-by: Joerg Roedel --- include/linux/iommu.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 54d6d690073c..0a85199191b9 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -20,6 +20,7 @@ #define __LINUX_IOMMU_H #include +#include #define IOMMU_READ (1) #define IOMMU_WRITE (2) -- cgit v1.2.3 From ba1eabfade3272596000bf3c62b68ca375e48b08 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Fri, 3 Aug 2012 15:55:41 +0200 Subject: iommu: Add missing forward declaration in include file The 'struct notifier_block' is not used in linux/iommu.h but not declared anywhere. Add a forward declaration for it. Reported-by: Thierry Reding Signed-off-by: Joerg Roedel --- include/linux/iommu.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 0a85199191b9..7e83370e6fd2 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -31,6 +31,7 @@ struct iommu_group; struct bus_type; struct device; struct iommu_domain; +struct notifier_block; /* iommu fault flags */ #define IOMMU_FAULT_READ 0x0 -- cgit v1.2.3 From f0cd2dbb6cf387c11f87265462e370bb5469299e Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 25 Jul 2012 18:11:59 +0300 Subject: vfs: kill write_super and sync_supers Finally we can kill the 'sync_supers' kernel thread along with the '->write_super()' superblock operation because all the users are gone. Now every file-system is supposed to self-manage own superblock and its dirty state. The nice thing about killing this thread is that it improves power management. Indeed, 'sync_supers' is a source of monotonic system wake-ups - it woke up every 5 seconds no matter what - even if there were no dirty superblocks and even if there were no file-systems using this service (e.g., btrfs and journalled ext4 do not need it). So it was wasting power most of the time. And because the thread was in the core of the kernel, all systems had to have it. So I am quite happy to make it go away. Interestingly, this thread is a left-over from the pdflush kernel thread which was a self-forking kernel thread responsible for all the write-back in old Linux kernels. It was turned into per-block device BDI threads, and 'sync_supers' was a left-over. Thus, R.I.P, pdflush as well. Signed-off-by: Artem Bityutskiy Signed-off-by: Al Viro --- fs/super.c | 40 ---------------------------------- include/linux/backing-dev.h | 1 - include/linux/fs.h | 3 --- mm/backing-dev.c | 52 --------------------------------------------- mm/page-writeback.c | 1 - 5 files changed, 97 deletions(-) (limited to 'include') diff --git a/fs/super.c b/fs/super.c index b05cf47463d0..0902cfa6a12e 100644 --- a/fs/super.c +++ b/fs/super.c @@ -536,46 +536,6 @@ void drop_super(struct super_block *sb) EXPORT_SYMBOL(drop_super); -/** - * sync_supers - helper for periodic superblock writeback - * - * Call the write_super method if present on all dirty superblocks in - * the system. This is for the periodic writeback used by most older - * filesystems. For data integrity superblock writeback use - * sync_filesystems() instead. - * - * Note: check the dirty flag before waiting, so we don't - * hold up the sync while mounting a device. (The newly - * mounted device won't need syncing.) - */ -void sync_supers(void) -{ - struct super_block *sb, *p = NULL; - - spin_lock(&sb_lock); - list_for_each_entry(sb, &super_blocks, s_list) { - if (hlist_unhashed(&sb->s_instances)) - continue; - if (sb->s_op->write_super && sb->s_dirt) { - sb->s_count++; - spin_unlock(&sb_lock); - - down_read(&sb->s_umount); - if (sb->s_root && sb->s_dirt && (sb->s_flags & MS_BORN)) - sb->s_op->write_super(sb); - up_read(&sb->s_umount); - - spin_lock(&sb_lock); - if (p) - __put_super(p); - p = sb; - } - } - if (p) - __put_super(p); - spin_unlock(&sb_lock); -} - /** * iterate_supers - call function for all active superblocks * @f: function to call diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index c97c6b9cd38e..2a9a9abc9126 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -124,7 +124,6 @@ void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages, void bdi_start_background_writeback(struct backing_dev_info *bdi); int bdi_writeback_thread(void *data); int bdi_has_dirty_io(struct backing_dev_info *bdi); -void bdi_arm_supers_timer(void); void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi); void bdi_lock_two(struct bdi_writeback *wb1, struct bdi_writeback *wb2); diff --git a/include/linux/fs.h b/include/linux/fs.h index 38dba16c4176..aa110476a95b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1491,7 +1491,6 @@ struct sb_writers { struct super_block { struct list_head s_list; /* Keep this first */ dev_t s_dev; /* search index; _not_ kdev_t */ - unsigned char s_dirt; unsigned char s_blocksize_bits; unsigned long s_blocksize; loff_t s_maxbytes; /* Max file size */ @@ -1861,7 +1860,6 @@ struct super_operations { int (*drop_inode) (struct inode *); void (*evict_inode) (struct inode *); void (*put_super) (struct super_block *); - void (*write_super) (struct super_block *); int (*sync_fs)(struct super_block *sb, int wait); int (*freeze_fs) (struct super_block *); int (*unfreeze_fs) (struct super_block *); @@ -2397,7 +2395,6 @@ extern int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync); extern int vfs_fsync(struct file *file, int datasync); extern int generic_write_sync(struct file *file, loff_t pos, loff_t count); -extern void sync_supers(void); extern void emergency_sync(void); extern void emergency_remount(void); #ifdef CONFIG_BLOCK diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 6b4718e2ee34..b41823cc05e6 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -39,12 +39,6 @@ DEFINE_SPINLOCK(bdi_lock); LIST_HEAD(bdi_list); LIST_HEAD(bdi_pending_list); -static struct task_struct *sync_supers_tsk; -static struct timer_list sync_supers_timer; - -static int bdi_sync_supers(void *); -static void sync_supers_timer_fn(unsigned long); - void bdi_lock_two(struct bdi_writeback *wb1, struct bdi_writeback *wb2) { if (wb1 < wb2) { @@ -250,12 +244,6 @@ static int __init default_bdi_init(void) { int err; - sync_supers_tsk = kthread_run(bdi_sync_supers, NULL, "sync_supers"); - BUG_ON(IS_ERR(sync_supers_tsk)); - - setup_timer(&sync_supers_timer, sync_supers_timer_fn, 0); - bdi_arm_supers_timer(); - err = bdi_init(&default_backing_dev_info); if (!err) bdi_register(&default_backing_dev_info, NULL, "default"); @@ -270,46 +258,6 @@ int bdi_has_dirty_io(struct backing_dev_info *bdi) return wb_has_dirty_io(&bdi->wb); } -/* - * kupdated() used to do this. We cannot do it from the bdi_forker_thread() - * or we risk deadlocking on ->s_umount. The longer term solution would be - * to implement sync_supers_bdi() or similar and simply do it from the - * bdi writeback thread individually. - */ -static int bdi_sync_supers(void *unused) -{ - set_user_nice(current, 0); - - while (!kthread_should_stop()) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - - /* - * Do this periodically, like kupdated() did before. - */ - sync_supers(); - } - - return 0; -} - -void bdi_arm_supers_timer(void) -{ - unsigned long next; - - if (!dirty_writeback_interval) - return; - - next = msecs_to_jiffies(dirty_writeback_interval * 10) + jiffies; - mod_timer(&sync_supers_timer, round_jiffies_up(next)); -} - -static void sync_supers_timer_fn(unsigned long unused) -{ - wake_up_process(sync_supers_tsk); - bdi_arm_supers_timer(); -} - static void wakeup_timer_fn(unsigned long data) { struct backing_dev_info *bdi = (struct backing_dev_info *)data; diff --git a/mm/page-writeback.c b/mm/page-writeback.c index e5363f34e025..5ad5ce23c1e0 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -1532,7 +1532,6 @@ int dirty_writeback_centisecs_handler(ctl_table *table, int write, void __user *buffer, size_t *length, loff_t *ppos) { proc_dointvec(table, write, buffer, length, ppos); - bdi_arm_supers_timer(); return 0; } -- cgit v1.2.3 From 0d5c3eba2e1e5aa74e097f49bc90b58f607e101c Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 25 Jul 2012 18:12:08 +0300 Subject: vfs: nuke pdflush from comments The pdflush thread is long gone, so this patch removes references to pdflush from vfs comments. Signed-off-by: Artem Bityutskiy Signed-off-by: Al Viro --- fs/bio.c | 2 +- include/linux/writeback.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/fs/bio.c b/fs/bio.c index 73922abba832..5eaa70c9d96e 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -1312,7 +1312,7 @@ EXPORT_SYMBOL(bio_copy_kern); * Note that this code is very hard to test under normal circumstances because * direct-io pins the pages with get_user_pages(). This makes * is_page_cache_freeable return false, and the VM will not clean the pages. - * But other code (eg, pdflush) could clean the pages if they are mapped + * But other code (eg, flusher threads) could clean the pages if they are mapped * pagecache. * * Simply disabling the call to bio_set_pages_dirty() is a good way to test the diff --git a/include/linux/writeback.h b/include/linux/writeback.h index c66fe3332d83..50c3e8fa06a8 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -104,7 +104,6 @@ static inline void wait_on_inode(struct inode *inode) wait_on_bit(&inode->i_state, __I_NEW, inode_wait, TASK_UNINTERRUPTIBLE); } - /* * mm/page-writeback.c */ -- cgit v1.2.3 From d796c52ef0b71a988364f6109aeb63d79c5b116b Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sun, 5 Aug 2012 19:04:57 -0400 Subject: ext4: make sure the journal sb is written in ext4_clear_journal_err() After we transfer set the EXT4_ERROR_FS bit in the file system superblock, it's not enough to call jbd2_journal_clear_err() to clear the error indication from journal superblock --- we need to call jbd2_journal_update_sb_errno() as well. Otherwise, when the root file system is mounted read-only, the journal is replayed, and the error indicator is transferred to the superblock --- but the s_errno field in the jbd2 superblock is left set (since although we cleared it in memory, we never flushed it out to disk). This can end up confusing e2fsck. We should make e2fsck more robust in this case, but the kernel shouldn't be leaving things in this confused state, either. Signed-off-by: "Theodore Ts'o" Cc: stable@kernel.org --- fs/ext4/super.c | 1 + fs/jbd2/journal.c | 3 ++- include/linux/jbd2.h | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/ext4/super.c b/fs/ext4/super.c index d76ec8277d3f..ccc4bcad5616 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -4430,6 +4430,7 @@ static void ext4_clear_journal_err(struct super_block *sb, ext4_commit_super(sb, 1); jbd2_journal_clear_err(journal); + jbd2_journal_update_sb_errno(journal); } } diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index e9a3c4c85594..bd23f2ebaa67 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1377,7 +1377,7 @@ static void jbd2_mark_journal_empty(journal_t *journal) * Update a journal's errno. Write updated superblock to disk waiting for IO * to complete. */ -static void jbd2_journal_update_sb_errno(journal_t *journal) +void jbd2_journal_update_sb_errno(journal_t *journal) { journal_superblock_t *sb = journal->j_superblock; @@ -1390,6 +1390,7 @@ static void jbd2_journal_update_sb_errno(journal_t *journal) jbd2_write_superblock(journal, WRITE_SYNC); } +EXPORT_SYMBOL(jbd2_journal_update_sb_errno); /* * Read the superblock for a given journal, performing initial diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index f334c7fab967..3efc43f3f162 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -1125,6 +1125,7 @@ extern int jbd2_journal_destroy (journal_t *); extern int jbd2_journal_recover (journal_t *journal); extern int jbd2_journal_wipe (journal_t *, int); extern int jbd2_journal_skip_recovery (journal_t *); +extern void jbd2_journal_update_sb_errno(journal_t *); extern void jbd2_journal_update_sb_log_tail (journal_t *, tid_t, unsigned long, int); extern void __jbd2_journal_abort_hard (journal_t *); -- cgit v1.2.3 From 5d299f3d3c8a2fbc732b1bf03af36333ccec3130 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 6 Aug 2012 05:09:33 +0000 Subject: net: ipv6: fix TCP early demux IPv6 needs a cookie in dst_check() call. We need to add rx_dst_cookie and provide a family independent sk_rx_dst_set(sk, skb) method to properly support IPv6 TCP early demux. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/ipv6.h | 1 + include/net/inet_connection_sock.h | 1 + include/net/inet_sock.h | 9 --------- net/ipv4/tcp_input.c | 4 +++- net/ipv4/tcp_ipv4.c | 13 ++++++++++--- net/ipv4/tcp_minisocks.c | 2 +- net/ipv6/tcp_ipv6.c | 27 +++++++++++++++++++++++++-- 7 files changed, 41 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 379e433e15e0..879db26ec401 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -369,6 +369,7 @@ struct ipv6_pinfo { __u8 rcv_tclass; __u32 dst_cookie; + __u32 rx_dst_cookie; struct ipv6_mc_socklist __rcu *ipv6_mc_list; struct ipv6_ac_socklist *ipv6_ac_list; diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 5ee66f517b4f..ba1d3615acbb 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -39,6 +39,7 @@ struct inet_connection_sock_af_ops { int (*queue_xmit)(struct sk_buff *skb, struct flowi *fl); void (*send_check)(struct sock *sk, struct sk_buff *skb); int (*rebuild_header)(struct sock *sk); + void (*sk_rx_dst_set)(struct sock *sk, const struct sk_buff *skb); int (*conn_request)(struct sock *sk, struct sk_buff *skb); struct sock *(*syn_recv_sock)(struct sock *sk, struct sk_buff *skb, struct request_sock *req, diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 83b567fe1941..613cfa401672 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -249,13 +249,4 @@ static inline __u8 inet_sk_flowi_flags(const struct sock *sk) return flags; } -static inline void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) -{ - struct dst_entry *dst = skb_dst(skb); - - dst_hold(dst); - sk->sk_rx_dst = dst; - inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; -} - #endif /* _INET_SOCK_H */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 2fd2bc9e3c64..85308b90df80 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5392,6 +5392,8 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb, { struct tcp_sock *tp = tcp_sk(sk); + if (unlikely(sk->sk_rx_dst == NULL)) + inet_csk(sk)->icsk_af_ops->sk_rx_dst_set(sk, skb); /* * Header prediction. * The code loosely follows the one in the famous @@ -5605,7 +5607,7 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb) tcp_set_state(sk, TCP_ESTABLISHED); if (skb != NULL) { - inet_sk_rx_dst_set(sk, skb); + icsk->icsk_af_ops->sk_rx_dst_set(sk, skb); security_inet_conn_established(sk, skb); } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 42b2a6a73092..272241f16fcb 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1627,9 +1627,6 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) sk->sk_rx_dst = NULL; } } - if (unlikely(sk->sk_rx_dst == NULL)) - inet_sk_rx_dst_set(sk, skb); - if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) { rsk = sk; goto reset; @@ -1872,10 +1869,20 @@ static struct timewait_sock_ops tcp_timewait_sock_ops = { .twsk_destructor= tcp_twsk_destructor, }; +static void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + + dst_hold(dst); + sk->sk_rx_dst = dst; + inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; +} + const struct inet_connection_sock_af_ops ipv4_specific = { .queue_xmit = ip_queue_xmit, .send_check = tcp_v4_send_check, .rebuild_header = inet_sk_rebuild_header, + .sk_rx_dst_set = inet_sk_rx_dst_set, .conn_request = tcp_v4_conn_request, .syn_recv_sock = tcp_v4_syn_recv_sock, .net_header_len = sizeof(struct iphdr), diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 232a90c3ec86..d9c9dcef2de3 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -387,7 +387,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, struct tcp_sock *oldtp = tcp_sk(sk); struct tcp_cookie_values *oldcvp = oldtp->cookie_values; - inet_sk_rx_dst_set(newsk, skb); + newicsk->icsk_af_ops->sk_rx_dst_set(newsk, skb); /* TCP Cookie Transactions require space for the cookie pair, * as it differs for each connection. There is no need to diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index c66b90f71c9b..5a439e9a4c01 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1447,7 +1447,17 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) opt_skb = skb_clone(skb, sk_gfp_atomic(sk, GFP_ATOMIC)); if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ + struct dst_entry *dst = sk->sk_rx_dst; + sock_rps_save_rxhash(sk, skb); + if (dst) { + if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif || + dst->ops->check(dst, np->rx_dst_cookie) == NULL) { + dst_release(dst); + sk->sk_rx_dst = NULL; + } + } + if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) goto reset; if (opt_skb) @@ -1705,9 +1715,9 @@ static void tcp_v6_early_demux(struct sk_buff *skb) struct dst_entry *dst = sk->sk_rx_dst; struct inet_sock *icsk = inet_sk(sk); if (dst) - dst = dst_check(dst, 0); + dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie); if (dst && - icsk->rx_dst_ifindex == inet6_iif(skb)) + icsk->rx_dst_ifindex == skb->skb_iif) skb_dst_set_noref(skb, dst); } } @@ -1719,10 +1729,23 @@ static struct timewait_sock_ops tcp6_timewait_sock_ops = { .twsk_destructor= tcp_twsk_destructor, }; +static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + const struct rt6_info *rt = (const struct rt6_info *)dst; + + dst_hold(dst); + sk->sk_rx_dst = dst; + inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; + if (rt->rt6i_node) + inet6_sk(sk)->rx_dst_cookie = rt->rt6i_node->fn_sernum; +} + static const struct inet_connection_sock_af_ops ipv6_specific = { .queue_xmit = inet6_csk_xmit, .send_check = tcp_v6_send_check, .rebuild_header = inet6_sk_rebuild_header, + .sk_rx_dst_set = inet6_sk_rx_dst_set, .conn_request = tcp_v6_conn_request, .syn_recv_sock = tcp_v6_syn_recv_sock, .net_header_len = sizeof(struct ipv6hdr), -- cgit v1.2.3 From 035534ed3377d9def2c17717899fd64a111a785b Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Mon, 6 Aug 2012 18:02:29 +0200 Subject: canfd: remove redundant CAN FD flag The first idea of the CAN FD implementation started with a new struct canfd_frame to be used for both CAN FD frames and legacy CAN frames. The now mainlined implementation supports both CAN frame types simultaneously and distinguishes them only by their required sizes: CAN_MTU and CANFD_MTU. Only the struct canfd_frame contains a flags element which is needed for the additional CAN FD information. As CAN FD implicitly means that the 'Extened Data Length' mode is enabled the formerly defined CANFD_EDL bit became redundant and also confusing as an unset bit would be an error and would always need to be tested. This patch removes the obsolete CANFD_EDL bit and clarifies the documentation for the use of struct canfd_frame and the CAN FD relevant flags. Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/linux/can.h | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/can.h b/include/linux/can.h index 018055efc034..e52958d7c2d1 100644 --- a/include/linux/can.h +++ b/include/linux/can.h @@ -74,20 +74,21 @@ struct can_frame { /* * 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. + * The use of struct canfd_frame implies the Extended Data Length (EDL) bit to + * be set in the CAN frame bitstream on the wire. The EDL bit switch turns + * the CAN controllers bitstream processor into the CAN FD mode which creates + * two new options within the CAN FD frame specification: * - * 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 + * Bit Rate Switch - to indicate a second bitrate is/was used for the payload + * Error State Indicator - represents the error state of the transmitting node + * + * As the CANFD_ESI bit is internally generated by the transmitting CAN + * controller only the CANFD_BRS bit is relevant for real CAN controllers when + * building a CAN FD frame for transmission. Setting the CANFD_ESI bit can make + * sense for virtual CAN interfaces to test applications with echoed frames. */ -#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 */ +#define CANFD_BRS 0x01 /* bit rate switch (second bitrate for payload data) */ +#define CANFD_ESI 0x02 /* error state indicator of the transmitting node */ /** * struct canfd_frame - CAN flexible data rate frame structure -- cgit v1.2.3 From 817fea2df3c24b22f6123dc0106eb063b7132883 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 7 Aug 2012 11:48:33 -0600 Subject: vfio: Include vfio.h in installed headers Signed-off-by: Alex Williamson --- include/linux/Kbuild | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index d9a754474878..fa217607c582 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -391,6 +391,7 @@ header-y += v4l2-dv-timings.h header-y += v4l2-mediabus.h header-y += v4l2-subdev.h header-y += veth.h +header-y += vfio.h header-y += vhost.h header-y += videodev2.h header-y += virtio_9p.h -- cgit v1.2.3 From 300d3739e873d50d4c6e3656f89007a217fb1d29 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 7 Aug 2012 13:50:22 +0200 Subject: Revert "NMI watchdog: fix for lockup detector breakage on resume" Revert commit 45226e9 (NMI watchdog: fix for lockup detector breakage on resume) which breaks resume from system suspend on my SH7372 Mackerel board (by causing a NULL pointer dereference to happen) and is generally wrong, because it abuses the CPU hotplug functionality in a shamelessly blatant way. The original issue should be addressed through appropriate syscore resume callback instead. Signed-off-by: Rafael J. Wysocki --- include/linux/sched.h | 8 -------- kernel/power/suspend.c | 3 --- kernel/watchdog.c | 21 ++------------------- 3 files changed, 2 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index c147e7024f11..b8c86648a2f9 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -334,14 +334,6 @@ static inline void lockup_detector_init(void) } #endif -#if defined(CONFIG_LOCKUP_DETECTOR) && defined(CONFIG_SUSPEND) -void lockup_detector_bootcpu_resume(void); -#else -static inline void lockup_detector_bootcpu_resume(void) -{ -} -#endif - #ifdef CONFIG_DETECT_HUNG_TASK extern unsigned int sysctl_hung_task_panic; extern unsigned long sysctl_hung_task_check_count; diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 1da39ea248fd..c8b7446b27df 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -178,9 +178,6 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) arch_suspend_enable_irqs(); BUG_ON(irqs_disabled()); - /* Kick the lockup detector */ - lockup_detector_bootcpu_resume(); - Enable_cpus: enable_nonboot_cpus(); diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 69add8a9da68..4b1dfba70f7c 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -575,7 +575,7 @@ out: /* * Create/destroy watchdog threads as CPUs come and go: */ -static int +static int __cpuinit cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { int hotcpu = (unsigned long)hcpu; @@ -610,27 +610,10 @@ cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) return NOTIFY_OK; } -static struct notifier_block cpu_nfb = { +static struct notifier_block __cpuinitdata cpu_nfb = { .notifier_call = cpu_callback }; -#ifdef CONFIG_SUSPEND -/* - * On exit from suspend we force an offline->online transition on the boot CPU - * so that the PMU state that was lost while in suspended state gets set up - * properly for the boot CPU. This information is required for restarting the - * NMI watchdog. - */ -void lockup_detector_bootcpu_resume(void) -{ - void *cpu = (void *)(long)smp_processor_id(); - - cpu_callback(&cpu_nfb, CPU_DEAD_FROZEN, cpu); - cpu_callback(&cpu_nfb, CPU_UP_PREPARE_FROZEN, cpu); - cpu_callback(&cpu_nfb, CPU_ONLINE_FROZEN, cpu); -} -#endif - void __init lockup_detector_init(void) { void *cpu = (void *)(long)smp_processor_id(); -- cgit v1.2.3 From a37e6e344910a43b9ebc2bbf29a029f5ea942598 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 7 Aug 2012 10:55:45 +0000 Subject: net: force dst_default_metrics to const section While investigating on network performance problems, I found this little gem : $ nm -v vmlinux | grep -1 dst_default_metrics ffffffff82736540 b busy.46605 ffffffff82736560 B dst_default_metrics ffffffff82736598 b dst_busy_list Apparently, declaring a const array without initializer put it in (writeable) bss section, in middle of possibly often dirtied cache lines. Since we really want dst_default_metrics be const to avoid any possible false sharing and catch any buggy writes, I force a null initializer. ffffffff818a4c20 R dst_default_metrics Signed-off-by: Eric Dumazet Cc: Ben Hutchings Signed-off-by: David S. Miller --- include/net/dst.h | 2 +- net/core/dst.c | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index baf597890064..621e3513ef5e 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -110,7 +110,7 @@ struct dst_entry { }; extern u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old); -extern const u32 dst_default_metrics[RTAX_MAX]; +extern const u32 dst_default_metrics[]; #define DST_METRICS_READ_ONLY 0x1UL #define __DST_METRICS_PTR(Y) \ diff --git a/net/core/dst.c b/net/core/dst.c index 069d51d29414..56d63612e1e4 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -149,7 +149,15 @@ int dst_discard(struct sk_buff *skb) } EXPORT_SYMBOL(dst_discard); -const u32 dst_default_metrics[RTAX_MAX]; +const u32 dst_default_metrics[RTAX_MAX + 1] = { + /* This initializer is needed to force linker to place this variable + * into const section. Otherwise it might end into bss section. + * We really want to avoid false sharing on this variable, and catch + * any writes on it. + */ + [RTAX_MAX] = 0xdeadbeef, +}; + void *dst_alloc(struct dst_ops *ops, struct net_device *dev, int initial_ref, int initial_obsolete, unsigned short flags) -- cgit v1.2.3 From 59ee93a528b94ef4e81a08db252b0326feff171f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sun, 5 Aug 2012 14:58:37 +0000 Subject: ARM: pxa: remove irq_to_gpio from ezx-pcap driver The irq_to_gpio function was removed from the pxa platform in linux-3.2, and this driver has been broken since. There is actually no in-tree user of this driver that adds this platform device, but the driver can and does get enabled on some platforms. Without this patch, building ezx_defconfig results in: drivers/mfd/ezx-pcap.c: In function 'pcap_isr_work': drivers/mfd/ezx-pcap.c:205:2: error: implicit declaration of function 'irq_to_gpio' [-Werror=implicit-function-declaration] Signed-off-by: Arnd Bergmann Acked-by: Haojian Zhuang Cc: stable@vger.kernel.org (v3.2+) Cc: Samuel Ortiz Cc: Daniel Ribeiro --- drivers/mfd/ezx-pcap.c | 2 +- include/linux/mfd/ezx-pcap.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c index 43a76c41cfcc..db662e2dcfa5 100644 --- a/drivers/mfd/ezx-pcap.c +++ b/drivers/mfd/ezx-pcap.c @@ -202,7 +202,7 @@ static void pcap_isr_work(struct work_struct *work) } local_irq_enable(); ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr); - } while (gpio_get_value(irq_to_gpio(pcap->spi->irq))); + } while (gpio_get_value(pdata->gpio)); } static void pcap_irq_handler(unsigned int irq, struct irq_desc *desc) diff --git a/include/linux/mfd/ezx-pcap.h b/include/linux/mfd/ezx-pcap.h index 40c372165f3e..32a1b5cfeba1 100644 --- a/include/linux/mfd/ezx-pcap.h +++ b/include/linux/mfd/ezx-pcap.h @@ -16,6 +16,7 @@ struct pcap_subdev { struct pcap_platform_data { unsigned int irq_base; unsigned int config; + int gpio; void (*init) (void *); /* board specific init */ int num_subdevs; struct pcap_subdev *subdevs; -- cgit v1.2.3 From 4eef6cbfcc03b294d9d334368a851b35b496ce53 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 30 Apr 2012 16:21:37 +0000 Subject: Input: eeti_ts: pass gpio value instead of IRQ The EETI touchscreen asserts its IRQ line as soon as it has data in its internal buffers. The line is automatically deasserted once all data has been read via I2C. Hence, the driver has to monitor the GPIO line and cannot simply rely on the interrupt handler reception. In the current implementation of the driver, irq_to_gpio() is used to determine the GPIO number from the i2c_client's IRQ value. As irq_to_gpio() is not available on all platforms, this patch changes this and makes the driver ignore the passed in IRQ. Instead, a GPIO is added to the platform_data struct and gpio_to_irq is used to derive the IRQ from that GPIO. If this fails, bail out. The driver is only able to work in environments where the touchscreen GPIO can be mapped to an IRQ. Without this patch, building raumfeld_defconfig results in: drivers/input/touchscreen/eeti_ts.c: In function 'eeti_ts_irq_active': drivers/input/touchscreen/eeti_ts.c:65:2: error: implicit declaration of function 'irq_to_gpio' [-Werror=implicit-function-declaration] Signed-off-by: Daniel Mack Signed-off-by: Arnd Bergmann Cc: stable@vger.kernel.org (v3.2+) Cc: Dmitry Torokhov Cc: Sven Neumann Cc: linux-input@vger.kernel.org Cc: Haojian Zhuang --- arch/arm/mach-pxa/raumfeld.c | 2 +- drivers/input/touchscreen/eeti_ts.c | 21 +++++++++++++-------- include/linux/input/eeti_ts.h | 1 + 3 files changed, 15 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c index 5905ed130e94..d89d87ae144c 100644 --- a/arch/arm/mach-pxa/raumfeld.c +++ b/arch/arm/mach-pxa/raumfeld.c @@ -953,12 +953,12 @@ static struct i2c_board_info raumfeld_connector_i2c_board_info __initdata = { static struct eeti_ts_platform_data eeti_ts_pdata = { .irq_active_high = 1, + .irq_gpio = GPIO_TOUCH_IRQ, }; static struct i2c_board_info raumfeld_controller_i2c_board_info __initdata = { .type = "eeti_ts", .addr = 0x0a, - .irq = PXA_GPIO_TO_IRQ(GPIO_TOUCH_IRQ), .platform_data = &eeti_ts_pdata, }; diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c index 503c7096ed36..908407efc672 100644 --- a/drivers/input/touchscreen/eeti_ts.c +++ b/drivers/input/touchscreen/eeti_ts.c @@ -48,7 +48,7 @@ struct eeti_ts_priv { struct input_dev *input; struct work_struct work; struct mutex mutex; - int irq, irq_active_high; + int irq_gpio, irq, irq_active_high; }; #define EETI_TS_BITDEPTH (11) @@ -62,7 +62,7 @@ struct eeti_ts_priv { static inline int eeti_ts_irq_active(struct eeti_ts_priv *priv) { - return gpio_get_value(irq_to_gpio(priv->irq)) == priv->irq_active_high; + return gpio_get_value(priv->irq_gpio) == priv->irq_active_high; } static void eeti_ts_read(struct work_struct *work) @@ -157,7 +157,7 @@ static void eeti_ts_close(struct input_dev *dev) static int __devinit eeti_ts_probe(struct i2c_client *client, const struct i2c_device_id *idp) { - struct eeti_ts_platform_data *pdata; + struct eeti_ts_platform_data *pdata = client->dev.platform_data; struct eeti_ts_priv *priv; struct input_dev *input; unsigned int irq_flags; @@ -199,9 +199,12 @@ static int __devinit eeti_ts_probe(struct i2c_client *client, priv->client = client; priv->input = input; - priv->irq = client->irq; + priv->irq_gpio = pdata->irq_gpio; + priv->irq = gpio_to_irq(pdata->irq_gpio); - pdata = client->dev.platform_data; + err = gpio_request_one(pdata->irq_gpio, GPIOF_IN, client->name); + if (err < 0) + goto err1; if (pdata) priv->irq_active_high = pdata->irq_active_high; @@ -215,13 +218,13 @@ static int __devinit eeti_ts_probe(struct i2c_client *client, err = input_register_device(input); if (err) - goto err1; + goto err2; err = request_irq(priv->irq, eeti_ts_isr, irq_flags, client->name, priv); if (err) { dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); - goto err2; + goto err3; } /* @@ -233,9 +236,11 @@ static int __devinit eeti_ts_probe(struct i2c_client *client, device_init_wakeup(&client->dev, 0); return 0; -err2: +err3: input_unregister_device(input); input = NULL; /* so we dont try to free it below */ +err2: + gpio_free(pdata->irq_gpio); err1: input_free_device(input); kfree(priv); diff --git a/include/linux/input/eeti_ts.h b/include/linux/input/eeti_ts.h index f875b316249d..16625d799b6f 100644 --- a/include/linux/input/eeti_ts.h +++ b/include/linux/input/eeti_ts.h @@ -2,6 +2,7 @@ #define LINUX_INPUT_EETI_TS_H struct eeti_ts_platform_data { + int irq_gpio; unsigned int irq_active_high; }; -- cgit v1.2.3 From 276f0f5d157bb4a816053f4f3a941dbcd4f76556 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Thu, 9 Aug 2012 15:20:23 +0200 Subject: block: disable discard request merge temporarily The SCSI discard request merge never worked, and looks no solution for in future, let's disable it temporarily. Signed-off-by: Shaohua Li Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- include/linux/blkdev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index dc632975d54f..4a2ab7c85393 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -601,7 +601,7 @@ static inline void blk_clear_rl_full(struct request_list *rl, bool sync) * it already be started by driver. */ #define RQ_NOMERGE_FLAGS \ - (REQ_NOMERGE | REQ_STARTED | REQ_SOFTBARRIER | REQ_FLUSH | REQ_FUA) + (REQ_NOMERGE | REQ_STARTED | REQ_SOFTBARRIER | REQ_FLUSH | REQ_FUA | REQ_DISCARD) #define rq_mergeable(rq) \ (!((rq)->cmd_flags & RQ_NOMERGE_FLAGS) && \ (((rq)->cmd_flags & REQ_DISCARD) || \ -- cgit v1.2.3 From 63d02d157ec4124990258d66517b6c11fd6df0cf Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 9 Aug 2012 14:11:00 +0000 Subject: net: tcp: ipv6_mapped needs sk_rx_dst_set method commit 5d299f3d3c8a2fb (net: ipv6: fix TCP early demux) added a regression for ipv6_mapped case. [ 67.422369] SELinux: initialized (dev autofs, type autofs), uses genfs_contexts [ 67.449678] SELinux: initialized (dev autofs, type autofs), uses genfs_contexts [ 92.631060] BUG: unable to handle kernel NULL pointer dereference at (null) [ 92.631435] IP: [< (null)>] (null) [ 92.631645] PGD 0 [ 92.631846] Oops: 0010 [#1] SMP [ 92.632095] Modules linked in: autofs4 sunrpc ipv6 dm_mirror dm_region_hash dm_log dm_multipath dm_mod video sbs sbshc battery ac lp parport sg snd_hda_intel snd_hda_codec snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device pcspkr snd_pcm_oss snd_mixer_oss snd_pcm snd_timer serio_raw button floppy snd i2c_i801 i2c_core soundcore snd_page_alloc shpchp ide_cd_mod cdrom microcode ehci_hcd ohci_hcd uhci_hcd [ 92.634294] CPU 0 [ 92.634294] Pid: 4469, comm: sendmail Not tainted 3.6.0-rc1 #3 [ 92.634294] RIP: 0010:[<0000000000000000>] [< (null)>] (null) [ 92.634294] RSP: 0018:ffff880245fc7cb0 EFLAGS: 00010282 [ 92.634294] RAX: ffffffffa01985f0 RBX: ffff88024827ad00 RCX: 0000000000000000 [ 92.634294] RDX: 0000000000000218 RSI: ffff880254735380 RDI: ffff88024827ad00 [ 92.634294] RBP: ffff880245fc7cc8 R08: 0000000000000001 R09: 0000000000000000 [ 92.634294] R10: 0000000000000000 R11: ffff880245fc7bf8 R12: ffff880254735380 [ 92.634294] R13: ffff880254735380 R14: 0000000000000000 R15: 7fffffffffff0218 [ 92.634294] FS: 00007f4516ccd6f0(0000) GS:ffff880256600000(0000) knlGS:0000000000000000 [ 92.634294] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 92.634294] CR2: 0000000000000000 CR3: 0000000245ed1000 CR4: 00000000000007f0 [ 92.634294] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 92.634294] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 92.634294] Process sendmail (pid: 4469, threadinfo ffff880245fc6000, task ffff880254b8cac0) [ 92.634294] Stack: [ 92.634294] ffffffff813837a7 ffff88024827ad00 ffff880254b6b0e8 ffff880245fc7d68 [ 92.634294] ffffffff81385083 00000000001d2680 ffff8802547353a8 ffff880245fc7d18 [ 92.634294] ffffffff8105903a ffff88024827ad60 0000000000000002 00000000000000ff [ 92.634294] Call Trace: [ 92.634294] [] ? tcp_finish_connect+0x2c/0xfa [ 92.634294] [] tcp_rcv_state_process+0x2b6/0x9c6 [ 92.634294] [] ? sched_clock_cpu+0xc3/0xd1 [ 92.634294] [] ? local_clock+0x2b/0x3c [ 92.634294] [] tcp_v4_do_rcv+0x63a/0x670 [ 92.634294] [] release_sock+0x128/0x1bd [ 92.634294] [] __inet_stream_connect+0x1b1/0x352 [ 92.634294] [] ? lock_sock_nested+0x74/0x7f [ 92.634294] [] ? wake_up_bit+0x25/0x25 [ 92.634294] [] ? lock_sock_nested+0x74/0x7f [ 92.634294] [] ? inet_stream_connect+0x22/0x4b [ 92.634294] [] inet_stream_connect+0x33/0x4b [ 92.634294] [] sys_connect+0x78/0x9e [ 92.634294] [] ? sysret_check+0x1b/0x56 [ 92.634294] [] ? __audit_syscall_entry+0x195/0x1c8 [ 92.634294] [] ? trace_hardirqs_on_thunk+0x3a/0x3f [ 92.634294] [] system_call_fastpath+0x16/0x1b [ 92.634294] Code: Bad RIP value. [ 92.634294] RIP [< (null)>] (null) [ 92.634294] RSP [ 92.634294] CR2: 0000000000000000 [ 92.648982] ---[ end trace 24e2bed94314c8d9 ]--- [ 92.649146] Kernel panic - not syncing: Fatal exception in interrupt Fix this using inet_sk_rx_dst_set(), and export this function in case IPv6 is modular. Reported-by: Andrew Morton Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 1 + net/ipv4/tcp_ipv4.c | 3 ++- net/ipv6/tcp_ipv6.c | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index e19124b84cd2..1f000ffe7075 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -464,6 +464,7 @@ extern int tcp_disconnect(struct sock *sk, int flags); void tcp_connect_init(struct sock *sk); void tcp_finish_connect(struct sock *sk, struct sk_buff *skb); int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size); +void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb); /* From syncookies.c */ extern __u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS]; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 272241f16fcb..767823764016 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1869,7 +1869,7 @@ static struct timewait_sock_ops tcp_timewait_sock_ops = { .twsk_destructor= tcp_twsk_destructor, }; -static void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) +void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); @@ -1877,6 +1877,7 @@ static void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) sk->sk_rx_dst = dst; inet_sk(sk)->rx_dst_ifindex = skb->skb_iif; } +EXPORT_SYMBOL(inet_sk_rx_dst_set); const struct inet_connection_sock_af_ops ipv4_specific = { .queue_xmit = ip_queue_xmit, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 5a439e9a4c01..bb9ce2b2f377 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1777,6 +1777,7 @@ static const struct inet_connection_sock_af_ops ipv6_mapped = { .queue_xmit = ip_queue_xmit, .send_check = tcp_v4_send_check, .rebuild_header = inet_sk_rebuild_header, + .sk_rx_dst_set = inet_sk_rx_dst_set, .conn_request = tcp_v6_conn_request, .syn_recv_sock = tcp_v6_syn_recv_sock, .net_header_len = sizeof(struct iphdr), -- cgit v1.2.3 From 02b69cbdc2fb2e1bfbfd9ac0c246d7be1b08d3cd Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 9 Aug 2012 10:08:46 +0000 Subject: netfilter: nf_ct_sip: fix IPv6 address parsing Within SIP messages IPv6 addresses are enclosed in square brackets in most cases, with the exception of the "received=" header parameter. Currently the helper fails to parse enclosed addresses. This patch: - changes the SIP address parsing function to enforce square brackets when required, and accept them when not required but present, as recommended by RFC 5118. - adds a new SDP address parsing function that never accepts square brackets since SDP doesn't use them. With these changes, the SIP helper correctly parses all test messages from RFC 5118 (Session Initiation Protocol (SIP) Torture Test Messages for Internet Protocol Version 6 (IPv6)). Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_conntrack_sip.h | 2 +- net/ipv4/netfilter/nf_nat_sip.c | 4 +- net/netfilter/nf_conntrack_sip.c | 87 ++++++++++++++++++++++++------ 3 files changed, 73 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h index 0dfc8b7210a3..89f2a627f3f0 100644 --- a/include/linux/netfilter/nf_conntrack_sip.h +++ b/include/linux/netfilter/nf_conntrack_sip.h @@ -164,7 +164,7 @@ extern int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr unsigned int dataoff, unsigned int datalen, const char *name, unsigned int *matchoff, unsigned int *matchlen, - union nf_inet_addr *addr); + union nf_inet_addr *addr, bool delim); extern int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr, unsigned int off, unsigned int datalen, const char *name, diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c index ea4a23813d26..eef8f29e8bf8 100644 --- a/net/ipv4/netfilter/nf_nat_sip.c +++ b/net/ipv4/netfilter/nf_nat_sip.c @@ -173,7 +173,7 @@ static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff, * the reply. */ if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, "maddr=", &poff, &plen, - &addr) > 0 && + &addr, true) > 0 && addr.ip == ct->tuplehash[dir].tuple.src.u3.ip && addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) { buflen = sprintf(buffer, "%pI4", @@ -187,7 +187,7 @@ static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff, * from which the server received the request. */ if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, "received=", &poff, &plen, - &addr) > 0 && + &addr, false) > 0 && addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip && addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) { buflen = sprintf(buffer, "%pI4", diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 2fb666920cc9..5c0a112aeee6 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -183,12 +183,12 @@ static int media_len(const struct nf_conn *ct, const char *dptr, return len + digits_len(ct, dptr, limit, shift); } -static int parse_addr(const struct nf_conn *ct, const char *cp, - const char **endp, union nf_inet_addr *addr, - const char *limit) +static int sip_parse_addr(const struct nf_conn *ct, const char *cp, + const char **endp, union nf_inet_addr *addr, + const char *limit, bool delim) { const char *end; - int ret = 0; + int ret; if (!ct) return 0; @@ -197,16 +197,28 @@ static int parse_addr(const struct nf_conn *ct, const char *cp, switch (nf_ct_l3num(ct)) { case AF_INET: ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end); + if (ret == 0) + return 0; break; case AF_INET6: + if (cp < limit && *cp == '[') + cp++; + else if (delim) + return 0; + ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end); + if (ret == 0) + return 0; + + if (end < limit && *end == ']') + end++; + else if (delim) + return 0; break; default: BUG(); } - if (ret == 0 || end == cp) - return 0; if (endp) *endp = end; return 1; @@ -219,7 +231,7 @@ static int epaddr_len(const struct nf_conn *ct, const char *dptr, union nf_inet_addr addr; const char *aux = dptr; - if (!parse_addr(ct, dptr, &dptr, &addr, limit)) { + if (!sip_parse_addr(ct, dptr, &dptr, &addr, limit, true)) { pr_debug("ip: %s parse failed.!\n", dptr); return 0; } @@ -296,7 +308,7 @@ int ct_sip_parse_request(const struct nf_conn *ct, return 0; dptr += shift; - if (!parse_addr(ct, dptr, &end, addr, limit)) + if (!sip_parse_addr(ct, dptr, &end, addr, limit, true)) return -1; if (end < limit && *end == ':') { end++; @@ -550,7 +562,7 @@ int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr, if (ret == 0) return ret; - if (!parse_addr(ct, dptr + *matchoff, &c, addr, limit)) + if (!sip_parse_addr(ct, dptr + *matchoff, &c, addr, limit, true)) return -1; if (*c == ':') { c++; @@ -599,7 +611,7 @@ int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr, unsigned int dataoff, unsigned int datalen, const char *name, unsigned int *matchoff, unsigned int *matchlen, - union nf_inet_addr *addr) + union nf_inet_addr *addr, bool delim) { const char *limit = dptr + datalen; const char *start, *end; @@ -613,7 +625,7 @@ int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr, return 0; start += strlen(name); - if (!parse_addr(ct, start, &end, addr, limit)) + if (!sip_parse_addr(ct, start, &end, addr, limit, delim)) return 0; *matchoff = start - dptr; *matchlen = end - start; @@ -675,6 +687,47 @@ static int ct_sip_parse_transport(struct nf_conn *ct, const char *dptr, return 1; } +static int sdp_parse_addr(const struct nf_conn *ct, const char *cp, + const char **endp, union nf_inet_addr *addr, + const char *limit) +{ + const char *end; + int ret; + + memset(addr, 0, sizeof(*addr)); + switch (nf_ct_l3num(ct)) { + case AF_INET: + ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end); + break; + case AF_INET6: + ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end); + break; + default: + BUG(); + } + + if (ret == 0) + return 0; + if (endp) + *endp = end; + return 1; +} + +/* skip ip address. returns its length. */ +static int sdp_addr_len(const struct nf_conn *ct, const char *dptr, + const char *limit, int *shift) +{ + union nf_inet_addr addr; + const char *aux = dptr; + + if (!sdp_parse_addr(ct, dptr, &dptr, &addr, limit)) { + pr_debug("ip: %s parse failed.!\n", dptr); + return 0; + } + + return dptr - aux; +} + /* SDP header parsing: a SDP session description contains an ordered set of * headers, starting with a section containing general session parameters, * optionally followed by multiple media descriptions. @@ -686,10 +739,10 @@ static int ct_sip_parse_transport(struct nf_conn *ct, const char *dptr, */ static const struct sip_header ct_sdp_hdrs[] = { [SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len), - [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", epaddr_len), - [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len), - [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len), - [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len), + [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", sdp_addr_len), + [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", sdp_addr_len), + [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", sdp_addr_len), + [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", sdp_addr_len), [SDP_HDR_MEDIA] = SDP_HDR("m=", NULL, media_len), }; @@ -775,8 +828,8 @@ static int ct_sip_parse_sdp_addr(const struct nf_conn *ct, const char *dptr, if (ret <= 0) return ret; - if (!parse_addr(ct, dptr + *matchoff, NULL, addr, - dptr + *matchoff + *matchlen)) + if (!sdp_parse_addr(ct, dptr + *matchoff, NULL, addr, + dptr + *matchoff + *matchlen)) return -1; return 1; } -- cgit v1.2.3 From 9d8dad742ad1c74d7e7210ee05d0b44961d5ea16 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 9 Aug 2012 19:01:26 -0700 Subject: Yama: higher restrictions should block PTRACE_TRACEME The higher ptrace restriction levels should be blocking even PTRACE_TRACEME requests. The comments in the LSM documentation are misleading about when the checks happen (the parent does not go through security_ptrace_access_check() on a PTRACE_TRACEME call). Signed-off-by: Kees Cook Cc: stable@vger.kernel.org # 3.5.x and later Signed-off-by: James Morris --- Documentation/security/Yama.txt | 14 +++++++------- include/linux/security.h | 2 -- security/yama/yama_lsm.c | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/Documentation/security/Yama.txt b/Documentation/security/Yama.txt index e369de2d48cd..dd908cf64ecf 100644 --- a/Documentation/security/Yama.txt +++ b/Documentation/security/Yama.txt @@ -46,14 +46,13 @@ restrictions, it can call prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, ...) so that any otherwise allowed process (even those in external pid namespaces) may attach. -These restrictions do not change how ptrace via PTRACE_TRACEME operates. - -The sysctl settings are: +The sysctl settings (writable only with CAP_SYS_PTRACE) are: 0 - classic ptrace permissions: a process can PTRACE_ATTACH to any other process running under the same uid, as long as it is dumpable (i.e. did not transition uids, start privileged, or have called - prctl(PR_SET_DUMPABLE...) already). + prctl(PR_SET_DUMPABLE...) already). Similarly, PTRACE_TRACEME is + unchanged. 1 - restricted ptrace: a process must have a predefined relationship with the inferior it wants to call PTRACE_ATTACH on. By default, @@ -61,12 +60,13 @@ The sysctl settings are: classic criteria is also met. To change the relationship, an inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare an allowed debugger PID to call PTRACE_ATTACH on the inferior. + Using PTRACE_TRACEME is unchanged. 2 - admin-only attach: only processes with CAP_SYS_PTRACE may use ptrace - with PTRACE_ATTACH. + with PTRACE_ATTACH, or through children calling PTRACE_TRACEME. -3 - no attach: no processes may use ptrace with PTRACE_ATTACH. Once set, - this sysctl cannot be changed to a lower value. +3 - no attach: no processes may use ptrace with PTRACE_ATTACH nor via + PTRACE_TRACEME. Once set, this sysctl value cannot be changed. The original children-only logic was based on the restrictions in grsecurity. diff --git a/include/linux/security.h b/include/linux/security.h index 4e5a73cdbbef..3dea6a9d568f 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1242,8 +1242,6 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * Check that the @parent process has sufficient permission to trace the * current process before allowing the current process to present itself * to the @parent process for tracing. - * The parent process will still have to undergo the ptrace_access_check - * checks before it is allowed to trace this one. * @parent contains the task_struct structure for debugger process. * Return 0 if permission is granted. * @capget: diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 83554ee8a587..d51b7c76c37d 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -290,10 +290,51 @@ static int yama_ptrace_access_check(struct task_struct *child, return rc; } +/** + * yama_ptrace_traceme - validate PTRACE_TRACEME calls + * @parent: task that will become the ptracer of the current task + * + * Returns 0 if following the ptrace is allowed, -ve on error. + */ +static int yama_ptrace_traceme(struct task_struct *parent) +{ + int rc; + + /* If standard caps disallows it, so does Yama. We should + * only tighten restrictions further. + */ + rc = cap_ptrace_traceme(parent); + if (rc) + return rc; + + /* Only disallow PTRACE_TRACEME on more aggressive settings. */ + switch (ptrace_scope) { + case YAMA_SCOPE_CAPABILITY: + if (!ns_capable(task_user_ns(parent), CAP_SYS_PTRACE)) + rc = -EPERM; + break; + case YAMA_SCOPE_NO_ATTACH: + rc = -EPERM; + break; + } + + if (rc) { + char name[sizeof(current->comm)]; + printk_ratelimited(KERN_NOTICE + "ptraceme of pid %d was attempted by: %s (pid %d)\n", + current->pid, + get_task_comm(name, parent), + parent->pid); + } + + return rc; +} + static struct security_operations yama_ops = { .name = "yama", .ptrace_access_check = yama_ptrace_access_check, + .ptrace_traceme = yama_ptrace_traceme, .task_prctl = yama_task_prctl, .task_free = yama_task_free, }; -- cgit v1.2.3 From b5ec8eeac46a99004c26791f70b15d001e970acf Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 10 Aug 2012 02:22:47 +0000 Subject: ipv4: fix ip_send_skb() ip_send_skb() can send orphaned skb, so we must pass the net pointer to avoid possible NULL dereference in error path. Bug added by commit 3a7c384ffd57 (ipv4: tcp: unicast_sock should not land outside of TCP stack) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/ip.h | 2 +- net/ipv4/ip_output.c | 5 ++--- net/ipv4/udp.c | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/ip.h b/include/net/ip.h index bd5e444a19ce..5a5d84d3d2c6 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -120,7 +120,7 @@ extern struct sk_buff *__ip_make_skb(struct sock *sk, struct flowi4 *fl4, struct sk_buff_head *queue, struct inet_cork *cork); -extern int ip_send_skb(struct sk_buff *skb); +extern int ip_send_skb(struct net *net, struct sk_buff *skb); extern int ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4); extern void ip_flush_pending_frames(struct sock *sk); extern struct sk_buff *ip_make_skb(struct sock *sk, diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index ec410e08b4b9..147ccc3e93db 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1366,9 +1366,8 @@ out: return skb; } -int ip_send_skb(struct sk_buff *skb) +int ip_send_skb(struct net *net, struct sk_buff *skb) { - struct net *net = sock_net(skb->sk); int err; err = ip_local_out(skb); @@ -1391,7 +1390,7 @@ int ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4) return 0; /* Netfilter gets whole the not fragmented skb. */ - return ip_send_skb(skb); + return ip_send_skb(sock_net(sk), skb); } /* diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index b4c3582a991f..6f6d1aca3c3d 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -758,7 +758,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4) uh->check = CSUM_MANGLED_0; send: - err = ip_send_skb(skb); + err = ip_send_skb(sock_net(sk), skb); if (err) { if (err == -ENOBUFS && !inet->recverr) { UDP_INC_STATS_USER(sock_net(sk), -- cgit v1.2.3 From 2359a47671fc4fb0fe5e9945f76c2cb10792c0f8 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 29 Jul 2012 20:52:21 +0000 Subject: codel: refine one condition to avoid a nul rec_inv_sqrt One condition before codel_Newton_step() was not good if we never left the dropping state for a flow. As a result rec_inv_sqrt was 0, instead of the ~0 initial value. codel control law was then set to a very aggressive mode, dropping many packets before reaching 'target' and recovering from this problem. To keep codel_vars_init() as efficient as possible, refine the condition to make sure rec_inv_sqrt initial value is correct Many thanks to Anton Mich for discovering the issue and suggesting a fix. Reported-by: Anton Mich Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/codel.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/codel.h b/include/net/codel.h index 550debfc2403..389cf621161d 100644 --- a/include/net/codel.h +++ b/include/net/codel.h @@ -305,6 +305,8 @@ static struct sk_buff *codel_dequeue(struct Qdisc *sch, } } } else if (drop) { + u32 delta; + if (params->ecn && INET_ECN_set_ce(skb)) { stats->ecn_mark++; } else { @@ -320,9 +322,11 @@ static struct sk_buff *codel_dequeue(struct Qdisc *sch, * assume that the drop rate that controlled the queue on the * last cycle is a good starting point to control it now. */ - if (codel_time_before(now - vars->drop_next, + delta = vars->count - vars->lastcount; + if (delta > 1 && + codel_time_before(now - vars->drop_next, 16 * params->interval)) { - vars->count = (vars->count - vars->lastcount) | 1; + vars->count = delta; /* we dont care if rec_inv_sqrt approximation * is not very precise : * Next Newton steps will correct it quadratically. -- cgit v1.2.3 From 2f292004dd1fb005788dc0a9cdd5559812ed866e Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 6 Aug 2012 10:03:59 -0400 Subject: drm/radeon: add some new SI pci ids Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- include/drm/drm_pciids.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index 7ff5c99b1638..c78bb997e2c6 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -213,9 +213,12 @@ {0x1002, 0x6800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6802, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6810, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6816, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6817, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6818, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6819, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ -- cgit v1.2.3 From 6759a0a7a0496dbbd4fb062c6a76d61c55d0fbd9 Mon Sep 17 00:00:00 2001 From: Marek Olšák Date: Thu, 9 Aug 2012 16:34:17 +0200 Subject: drm/radeon/kms: implement timestamp userspace query (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Returns a snapshot of the GPU clock counter. Needed for certain OpenGL extensions. v2: agd5f - address Jerome's comments - add function documentation Signed-off-by: Marek Olšák Reviewed-by: Jerome Glisse Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/r600.c | 20 +++++++++++++++++++ drivers/gpu/drm/radeon/r600d.h | 3 +++ drivers/gpu/drm/radeon/radeon.h | 1 + drivers/gpu/drm/radeon/radeon_asic.h | 2 ++ drivers/gpu/drm/radeon/radeon_device.c | 1 + drivers/gpu/drm/radeon/radeon_drv.c | 3 ++- drivers/gpu/drm/radeon/radeon_kms.c | 35 ++++++++++++++++++++++++++++------ drivers/gpu/drm/radeon/si.c | 19 ++++++++++++++++++ drivers/gpu/drm/radeon/sid.h | 3 +++ include/drm/radeon_drm.h | 2 ++ 10 files changed, 82 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 637280f541a3..d79c639ae739 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -3789,3 +3789,23 @@ static void r600_pcie_gen2_enable(struct radeon_device *rdev) WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); } } + +/** + * r600_get_gpu_clock - return GPU clock counter snapshot + * + * @rdev: radeon_device pointer + * + * Fetches a GPU clock counter snapshot (R6xx-cayman). + * Returns the 64 bit clock counter snapshot. + */ +uint64_t r600_get_gpu_clock(struct radeon_device *rdev) +{ + uint64_t clock; + + mutex_lock(&rdev->gpu_clock_mutex); + WREG32(RLC_CAPTURE_GPU_CLOCK_COUNT, 1); + clock = (uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_LSB) | + ((uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_MSB) << 32ULL); + mutex_unlock(&rdev->gpu_clock_mutex); + return clock; +} diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index 4b116ae75fc2..fd328f4c3ea8 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -602,6 +602,9 @@ #define RLC_HB_WPTR 0x3f1c #define RLC_HB_WPTR_LSB_ADDR 0x3f14 #define RLC_HB_WPTR_MSB_ADDR 0x3f18 +#define RLC_GPU_CLOCK_COUNT_LSB 0x3f38 +#define RLC_GPU_CLOCK_COUNT_MSB 0x3f3c +#define RLC_CAPTURE_GPU_CLOCK_COUNT 0x3f40 #define RLC_MC_CNTL 0x3f44 #define RLC_UCODE_CNTL 0x3f48 #define RLC_UCODE_ADDR 0x3f2c diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index b237a29142d1..99304194a65c 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1534,6 +1534,7 @@ struct radeon_device { unsigned debugfs_count; /* virtual memory */ struct radeon_vm_manager vm_manager; + struct mutex gpu_clock_mutex; }; int radeon_device_init(struct radeon_device *rdev, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 0d445e7d00d1..18c38d14c8cd 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -368,6 +368,7 @@ void r600_kms_blit_copy(struct radeon_device *rdev, unsigned num_gpu_pages, struct radeon_sa_bo *vb); int r600_mc_wait_for_idle(struct radeon_device *rdev); +uint64_t r600_get_gpu_clock(struct radeon_device *rdev); /* * rv770,rv730,rv710,rv740 @@ -468,5 +469,6 @@ int si_vm_bind(struct radeon_device *rdev, struct radeon_vm *vm, int id); void si_vm_unbind(struct radeon_device *rdev, struct radeon_vm *vm); void si_vm_tlb_flush(struct radeon_device *rdev, struct radeon_vm *vm); int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib); +uint64_t si_get_gpu_clock(struct radeon_device *rdev); #endif diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 742af8244e89..d2e243867ac6 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1009,6 +1009,7 @@ int radeon_device_init(struct radeon_device *rdev, atomic_set(&rdev->ih.lock, 0); mutex_init(&rdev->gem.mutex); mutex_init(&rdev->pm.mutex); + mutex_init(&rdev->gpu_clock_mutex); init_rwsem(&rdev->pm.mclk_lock); init_rwsem(&rdev->exclusive_lock); init_waitqueue_head(&rdev->irq.vblank_queue); diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index a7f8ac0d8f03..d7269f48d37c 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -61,9 +61,10 @@ * 2.17.0 - add STRMOUT_BASE_UPDATE for r7xx * 2.18.0 - r600-eg: allow "invalid" DB formats * 2.19.0 - r600-eg: MSAA textures + * 2.20.0 - r600-si: RADEON_INFO_TIMESTAMP query */ #define KMS_DRIVER_MAJOR 2 -#define KMS_DRIVER_MINOR 19 +#define KMS_DRIVER_MINOR 20 #define KMS_DRIVER_PATCHLEVEL 0 int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags); int radeon_driver_unload_kms(struct drm_device *dev); diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 1d73f16b5d97..414b4acf6947 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -29,6 +29,7 @@ #include "drm_sarea.h" #include "radeon.h" #include "radeon_drm.h" +#include "radeon_asic.h" #include #include @@ -167,17 +168,39 @@ static void radeon_set_filp_rights(struct drm_device *dev, int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { struct radeon_device *rdev = dev->dev_private; - struct drm_radeon_info *info; + struct drm_radeon_info *info = data; struct radeon_mode_info *minfo = &rdev->mode_info; - uint32_t *value_ptr; - uint32_t value; + uint32_t value, *value_ptr; + uint64_t value64, *value_ptr64; struct drm_crtc *crtc; int i, found; - info = data; + /* TIMESTAMP is a 64-bit value, needs special handling. */ + if (info->request == RADEON_INFO_TIMESTAMP) { + if (rdev->family >= CHIP_R600) { + value_ptr64 = (uint64_t*)((unsigned long)info->value); + if (rdev->family >= CHIP_TAHITI) { + value64 = si_get_gpu_clock(rdev); + } else { + value64 = r600_get_gpu_clock(rdev); + } + + if (DRM_COPY_TO_USER(value_ptr64, &value64, sizeof(value64))) { + DRM_ERROR("copy_to_user %s:%u\n", __func__, __LINE__); + return -EFAULT; + } + return 0; + } else { + DRM_DEBUG_KMS("timestamp is r6xx+ only!\n"); + return -EINVAL; + } + } + value_ptr = (uint32_t *)((unsigned long)info->value); - if (DRM_COPY_FROM_USER(&value, value_ptr, sizeof(value))) + if (DRM_COPY_FROM_USER(&value, value_ptr, sizeof(value))) { + DRM_ERROR("copy_from_user %s:%u\n", __func__, __LINE__); return -EFAULT; + } switch (info->request) { case RADEON_INFO_DEVICE_ID: @@ -337,7 +360,7 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) return -EINVAL; } if (DRM_COPY_TO_USER(value_ptr, &value, sizeof(uint32_t))) { - DRM_ERROR("copy_to_user\n"); + DRM_ERROR("copy_to_user %s:%u\n", __func__, __LINE__); return -EFAULT; } return 0; diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index c153a7f359c8..0139e227e3c7 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -3968,3 +3968,22 @@ void si_fini(struct radeon_device *rdev) rdev->bios = NULL; } +/** + * si_get_gpu_clock - return GPU clock counter snapshot + * + * @rdev: radeon_device pointer + * + * Fetches a GPU clock counter snapshot (SI). + * Returns the 64 bit clock counter snapshot. + */ +uint64_t si_get_gpu_clock(struct radeon_device *rdev) +{ + uint64_t clock; + + mutex_lock(&rdev->gpu_clock_mutex); + WREG32(RLC_CAPTURE_GPU_CLOCK_COUNT, 1); + clock = (uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_LSB) | + ((uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_MSB) << 32ULL); + mutex_unlock(&rdev->gpu_clock_mutex); + return clock; +} diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h index 7869089e8761..ef4815c27b1c 100644 --- a/drivers/gpu/drm/radeon/sid.h +++ b/drivers/gpu/drm/radeon/sid.h @@ -698,6 +698,9 @@ #define RLC_UCODE_ADDR 0xC32C #define RLC_UCODE_DATA 0xC330 +#define RLC_GPU_CLOCK_COUNT_LSB 0xC338 +#define RLC_GPU_CLOCK_COUNT_MSB 0xC33C +#define RLC_CAPTURE_GPU_CLOCK_COUNT 0xC340 #define RLC_MC_CNTL 0xC344 #define RLC_UCODE_CNTL 0xC348 diff --git a/include/drm/radeon_drm.h b/include/drm/radeon_drm.h index 58056865b8e9..dc3a8cd7db8a 100644 --- a/include/drm/radeon_drm.h +++ b/include/drm/radeon_drm.h @@ -964,6 +964,8 @@ struct drm_radeon_cs { #define RADEON_INFO_IB_VM_MAX_SIZE 0x0f /* max pipes - needed for compute shaders */ #define RADEON_INFO_MAX_PIPES 0x10 +/* timestamp for GL_ARB_timer_query (OpenGL), returns the current GPU clock */ +#define RADEON_INFO_TIMESTAMP 0x11 struct drm_radeon_info { uint32_t request; -- cgit v1.2.3 From 0bce9c46bf3b15f485d82d7e81dabed6ebcc24b1 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 10 Aug 2012 15:22:09 +0100 Subject: mutex: Place lock in contended state after fastpath_lock failure ARM recently moved to asm-generic/mutex-xchg.h for its mutex implementation after the previous implementation was found to be missing some crucial memory barriers. However, this has revealed some problems running hackbench on SMP platforms due to the way in which the MUTEX_SPIN_ON_OWNER code operates. The symptoms are that a bunch of hackbench tasks are left waiting on an unlocked mutex and therefore never get woken up to claim it. This boils down to the following sequence of events: Task A Task B Task C Lock value 0 1 1 lock() 0 2 lock() 0 3 spin(A) 0 4 unlock() 1 5 lock() 0 6 cmpxchg(1,0) 0 7 contended() -1 8 lock() 0 9 spin(C) 0 10 unlock() 1 11 cmpxchg(1,0) 0 12 unlock() 1 At this point, the lock is unlocked, but Task B is in an uninterruptible sleep with nobody to wake it up. This patch fixes the problem by ensuring we put the lock into the contended state if we fail to acquire it on the fastpath, ensuring that any blocked waiters are woken up when the mutex is released. Signed-off-by: Will Deacon Cc: Arnd Bergmann Cc: Chris Mason Cc: Ingo Molnar Cc: Reviewed-by: Nicolas Pitre Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-6e9lrw2avczr0617fzl5vqb8@git.kernel.org Signed-off-by: Thomas Gleixner --- include/asm-generic/mutex-xchg.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-generic/mutex-xchg.h b/include/asm-generic/mutex-xchg.h index 580a6d35c700..c04e0db8a2d6 100644 --- a/include/asm-generic/mutex-xchg.h +++ b/include/asm-generic/mutex-xchg.h @@ -26,7 +26,13 @@ static inline void __mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *)) { if (unlikely(atomic_xchg(count, 0) != 1)) - fail_fn(count); + /* + * We failed to acquire the lock, so mark it contended + * to ensure that any waiting tasks are woken up by the + * unlock slow path. + */ + if (likely(atomic_xchg(count, -1) != 1)) + fail_fn(count); } /** @@ -43,7 +49,8 @@ static inline int __mutex_fastpath_lock_retval(atomic_t *count, int (*fail_fn)(atomic_t *)) { if (unlikely(atomic_xchg(count, 0) != 1)) - return fail_fn(count); + if (likely(atomic_xchg(count, -1) != 1)) + return fail_fn(count); return 0; } -- cgit v1.2.3 From f026cfa82f628db24b8cea41b9d6202af104cecb Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 14 Aug 2012 09:53:38 -0700 Subject: Revert "x86-64/efi: Use EFI to deal with platform wall clock" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit bacef661acdb634170a8faddbc1cf28e8f8b9eee. This commit has been found to cause serious regressions on a number of ASUS machines at the least. We probably need to provide a 1:1 map in addition to the EFI virtual memory map in order for this to work. Signed-off-by: H. Peter Anvin Reported-and-bisected-by: Jérôme Carretero Cc: Jan Beulich Cc: Matt Fleming Cc: Matthew Garrett Cc: Linus Torvalds Cc: Andrew Morton Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120805172903.5f8bb24c@zougloub.eu --- arch/x86/mm/pageattr.c | 10 ++++------ arch/x86/platform/efi/efi.c | 30 ++++++++++++++++++++++++++---- include/linux/efi.h | 2 ++ init/main.c | 8 ++++---- 4 files changed, 36 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 931930a96160..a718e0d23503 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -919,13 +919,11 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages, /* * On success we use clflush, when the CPU supports it to - * avoid the wbindv. If the CPU does not support it, in the - * error case, and during early boot (for EFI) we fall back - * to cpa_flush_all (which uses wbinvd): + * avoid the wbindv. If the CPU does not support it and in the + * error case we fall back to cpa_flush_all (which uses + * wbindv): */ - if (early_boot_irqs_disabled) - __cpa_flush_all((void *)(long)cache); - else if (!ret && cpu_has_clflush) { + if (!ret && cpu_has_clflush) { if (cpa.flags & (CPA_PAGES_ARRAY | CPA_ARRAY)) { cpa_flush_array(addr, numpages, cache, cpa.flags, pages); diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 2dc29f51e75a..92660edaa1e7 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -234,7 +234,22 @@ static efi_status_t __init phys_efi_set_virtual_address_map( return status; } -static int efi_set_rtc_mmss(unsigned long nowtime) +static efi_status_t __init phys_efi_get_time(efi_time_t *tm, + efi_time_cap_t *tc) +{ + unsigned long flags; + efi_status_t status; + + spin_lock_irqsave(&rtc_lock, flags); + efi_call_phys_prelog(); + status = efi_call_phys2(efi_phys.get_time, virt_to_phys(tm), + virt_to_phys(tc)); + efi_call_phys_epilog(); + spin_unlock_irqrestore(&rtc_lock, flags); + return status; +} + +int efi_set_rtc_mmss(unsigned long nowtime) { int real_seconds, real_minutes; efi_status_t status; @@ -263,7 +278,7 @@ static int efi_set_rtc_mmss(unsigned long nowtime) return 0; } -static unsigned long efi_get_time(void) +unsigned long efi_get_time(void) { efi_status_t status; efi_time_t eft; @@ -606,13 +621,18 @@ static int __init efi_runtime_init(void) } /* * We will only need *early* access to the following - * EFI runtime service before set_virtual_address_map + * two EFI runtime services before set_virtual_address_map * is invoked. */ + efi_phys.get_time = (efi_get_time_t *)runtime->get_time; efi_phys.set_virtual_address_map = (efi_set_virtual_address_map_t *) runtime->set_virtual_address_map; - + /* + * Make efi_get_time can be called before entering + * virtual mode. + */ + efi.get_time = phys_efi_get_time; early_iounmap(runtime, sizeof(efi_runtime_services_t)); return 0; @@ -700,10 +720,12 @@ void __init efi_init(void) efi_enabled = 0; return; } +#ifdef CONFIG_X86_32 if (efi_native) { x86_platform.get_wallclock = efi_get_time; x86_platform.set_wallclock = efi_set_rtc_mmss; } +#endif #if EFI_DEBUG print_efi_memmap(); diff --git a/include/linux/efi.h b/include/linux/efi.h index 103adc6d7e3a..ec45ccd8708a 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -503,6 +503,8 @@ extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size); extern int __init efi_uart_console_only (void); extern void efi_initialize_iomem_resources(struct resource *code_resource, struct resource *data_resource, struct resource *bss_resource); +extern unsigned long efi_get_time(void); +extern int efi_set_rtc_mmss(unsigned long nowtime); extern void efi_reserve_boot_services(void); extern struct efi_memory_map memmap; diff --git a/init/main.c b/init/main.c index e60679de61c3..b28673087ac0 100644 --- a/init/main.c +++ b/init/main.c @@ -461,10 +461,6 @@ static void __init mm_init(void) percpu_init_late(); pgtable_cache_init(); vmalloc_init(); -#ifdef CONFIG_X86 - if (efi_enabled) - efi_enter_virtual_mode(); -#endif } asmlinkage void __init start_kernel(void) @@ -606,6 +602,10 @@ asmlinkage void __init start_kernel(void) calibrate_delay(); pidmap_init(); anon_vma_init(); +#ifdef CONFIG_X86 + if (efi_enabled) + efi_enter_virtual_mode(); +#endif thread_info_cache_init(); cred_init(); fork_init(totalram_pages); -- cgit v1.2.3 From 47be03a28cc6c80e3aa2b3e8ed6d960ff0c5c0af Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Fri, 10 Aug 2012 01:24:37 +0000 Subject: netpoll: use GFP_ATOMIC in slave_enable_netpoll() and __netpoll_setup() slave_enable_netpoll() and __netpoll_setup() may be called with read_lock() held, so should use GFP_ATOMIC to allocate memory. Eric suggested to pass gfp flags to __netpoll_setup(). Cc: Eric Dumazet Cc: "David S. Miller" Reported-by: Dan Carpenter Signed-off-by: Eric Dumazet Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 6 +++--- drivers/net/team/team.c | 16 +++++++++------- include/linux/netdevice.h | 3 ++- include/linux/netpoll.h | 2 +- net/8021q/vlan_dev.c | 7 ++++--- net/bridge/br_device.c | 12 ++++++------ net/bridge/br_if.c | 2 +- net/bridge/br_private.h | 4 ++-- net/core/netpoll.c | 8 ++++---- 9 files changed, 32 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 6fae5f3ec7f6..8697136e27c0 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1235,12 +1235,12 @@ static inline int slave_enable_netpoll(struct slave *slave) struct netpoll *np; int err = 0; - np = kzalloc(sizeof(*np), GFP_KERNEL); + np = kzalloc(sizeof(*np), GFP_ATOMIC); err = -ENOMEM; if (!np) goto out; - err = __netpoll_setup(np, slave->dev); + err = __netpoll_setup(np, slave->dev, GFP_ATOMIC); if (err) { kfree(np); goto out; @@ -1292,7 +1292,7 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev) read_unlock(&bond->lock); } -static int bond_netpoll_setup(struct net_device *dev, struct netpoll_info *ni) +static int bond_netpoll_setup(struct net_device *dev, struct netpoll_info *ni, gfp_t gfp) { struct bonding *bond = netdev_priv(dev); struct slave *slave; diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 87707ab39430..341b65dbbcd3 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -795,16 +795,17 @@ static void team_port_leave(struct team *team, struct team_port *port) } #ifdef CONFIG_NET_POLL_CONTROLLER -static int team_port_enable_netpoll(struct team *team, struct team_port *port) +static int team_port_enable_netpoll(struct team *team, struct team_port *port, + gfp_t gfp) { struct netpoll *np; int err; - np = kzalloc(sizeof(*np), GFP_KERNEL); + np = kzalloc(sizeof(*np), gfp); if (!np) return -ENOMEM; - err = __netpoll_setup(np, port->dev); + err = __netpoll_setup(np, port->dev, gfp); if (err) { kfree(np); return err; @@ -833,7 +834,8 @@ static struct netpoll_info *team_netpoll_info(struct team *team) } #else -static int team_port_enable_netpoll(struct team *team, struct team_port *port) +static int team_port_enable_netpoll(struct team *team, struct team_port *port, + gfp_t gfp) { return 0; } @@ -913,7 +915,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev) } if (team_netpoll_info(team)) { - err = team_port_enable_netpoll(team, port); + err = team_port_enable_netpoll(team, port, GFP_KERNEL); if (err) { netdev_err(dev, "Failed to enable netpoll on device %s\n", portname); @@ -1443,7 +1445,7 @@ static void team_netpoll_cleanup(struct net_device *dev) } static int team_netpoll_setup(struct net_device *dev, - struct netpoll_info *npifo) + struct netpoll_info *npifo, gfp_t gfp) { struct team *team = netdev_priv(dev); struct team_port *port; @@ -1451,7 +1453,7 @@ static int team_netpoll_setup(struct net_device *dev, mutex_lock(&team->lock); list_for_each_entry(port, &team->port_list, list) { - err = team_port_enable_netpoll(team, port); + err = team_port_enable_netpoll(team, port, gfp); if (err) { __team_netpoll_cleanup(team); break; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a9db4f33407f..3560d688161e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -953,7 +953,8 @@ struct net_device_ops { #ifdef CONFIG_NET_POLL_CONTROLLER void (*ndo_poll_controller)(struct net_device *dev); int (*ndo_netpoll_setup)(struct net_device *dev, - struct netpoll_info *info); + struct netpoll_info *info, + gfp_t gfp); void (*ndo_netpoll_cleanup)(struct net_device *dev); #endif int (*ndo_set_vf_mac)(struct net_device *dev, diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 28f5389c924b..bf2d51eec0f3 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -43,7 +43,7 @@ struct netpoll_info { void netpoll_send_udp(struct netpoll *np, const char *msg, int len); void netpoll_print_options(struct netpoll *np); int netpoll_parse_options(struct netpoll *np, char *opt); -int __netpoll_setup(struct netpoll *np, struct net_device *ndev); +int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp); int netpoll_setup(struct netpoll *np); int netpoll_trap(void); void netpoll_set_trap(int trap); diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 73a2a83ee2da..ee4ae0944cef 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -669,19 +669,20 @@ static void vlan_dev_poll_controller(struct net_device *dev) return; } -static int vlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo) +static int vlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo, + gfp_t gfp) { struct vlan_dev_priv *info = vlan_dev_priv(dev); struct net_device *real_dev = info->real_dev; struct netpoll *netpoll; int err = 0; - netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL); + netpoll = kzalloc(sizeof(*netpoll), gfp); err = -ENOMEM; if (!netpoll) goto out; - err = __netpoll_setup(netpoll, real_dev); + err = __netpoll_setup(netpoll, real_dev, gfp); if (err) { kfree(netpoll); goto out; diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 333484537600..ed0e0f9dc788 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -213,7 +213,8 @@ static void br_netpoll_cleanup(struct net_device *dev) } } -static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni) +static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni, + gfp_t gfp) { struct net_bridge *br = netdev_priv(dev); struct net_bridge_port *p, *n; @@ -222,8 +223,7 @@ static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni) list_for_each_entry_safe(p, n, &br->port_list, list) { if (!p->dev) continue; - - err = br_netpoll_enable(p); + err = br_netpoll_enable(p, gfp); if (err) goto fail; } @@ -236,17 +236,17 @@ fail: goto out; } -int br_netpoll_enable(struct net_bridge_port *p) +int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp) { struct netpoll *np; int err = 0; - np = kzalloc(sizeof(*p->np), GFP_KERNEL); + np = kzalloc(sizeof(*p->np), gfp); err = -ENOMEM; if (!np) goto out; - err = __netpoll_setup(np, p->dev); + err = __netpoll_setup(np, p->dev, gfp); if (err) { kfree(np); goto out; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index e1144e1617be..171fd6b9bfe6 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -361,7 +361,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) if (err) goto err2; - if (br_netpoll_info(br) && ((err = br_netpoll_enable(p)))) + if (br_netpoll_info(br) && ((err = br_netpoll_enable(p, GFP_KERNEL)))) goto err3; err = netdev_set_master(dev, br->dev); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index a768b2408edf..f507d2af9646 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -316,7 +316,7 @@ static inline void br_netpoll_send_skb(const struct net_bridge_port *p, netpoll_send_skb(np, skb); } -extern int br_netpoll_enable(struct net_bridge_port *p); +extern int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp); extern void br_netpoll_disable(struct net_bridge_port *p); #else static inline struct netpoll_info *br_netpoll_info(struct net_bridge *br) @@ -329,7 +329,7 @@ static inline void br_netpoll_send_skb(const struct net_bridge_port *p, { } -static inline int br_netpoll_enable(struct net_bridge_port *p) +static inline int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp) { return 0; } diff --git a/net/core/netpoll.c b/net/core/netpoll.c index b4c90e42b443..37cc854774a4 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -715,7 +715,7 @@ int netpoll_parse_options(struct netpoll *np, char *opt) } EXPORT_SYMBOL(netpoll_parse_options); -int __netpoll_setup(struct netpoll *np, struct net_device *ndev) +int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp) { struct netpoll_info *npinfo; const struct net_device_ops *ops; @@ -734,7 +734,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev) } if (!ndev->npinfo) { - npinfo = kmalloc(sizeof(*npinfo), GFP_KERNEL); + npinfo = kmalloc(sizeof(*npinfo), gfp); if (!npinfo) { err = -ENOMEM; goto out; @@ -752,7 +752,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev) ops = np->dev->netdev_ops; if (ops->ndo_netpoll_setup) { - err = ops->ndo_netpoll_setup(ndev, npinfo); + err = ops->ndo_netpoll_setup(ndev, npinfo, gfp); if (err) goto free_npinfo; } @@ -857,7 +857,7 @@ int netpoll_setup(struct netpoll *np) refill_skbs(); rtnl_lock(); - err = __netpoll_setup(np, ndev); + err = __netpoll_setup(np, ndev, GFP_KERNEL); rtnl_unlock(); if (err) -- cgit v1.2.3 From 38e6bc185d9544dfad1774b3f8902a0b061aea25 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Fri, 10 Aug 2012 01:24:38 +0000 Subject: netpoll: make __netpoll_cleanup non-block Like the previous patch, slave_disable_netpoll() and __netpoll_cleanup() may be called with read_lock() held too, so we should make them non-block, by moving the cleanup and kfree() to call_rcu_bh() callbacks. Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 4 +--- include/linux/netpoll.h | 3 +++ net/8021q/vlan_dev.c | 6 +----- net/bridge/br_device.c | 6 +----- net/core/netpoll.c | 42 +++++++++++++++++++++++++++++++---------- 5 files changed, 38 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 8697136e27c0..e42891683e3b 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1257,9 +1257,7 @@ static inline void slave_disable_netpoll(struct slave *slave) return; slave->np = NULL; - synchronize_rcu_bh(); - __netpoll_cleanup(np); - kfree(np); + __netpoll_free_rcu(np); } static inline bool slave_dev_support_netpoll(struct net_device *slave_dev) { diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index bf2d51eec0f3..907812efb4d9 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -23,6 +23,7 @@ struct netpoll { u8 remote_mac[ETH_ALEN]; struct list_head rx; /* rx_np list element */ + struct rcu_head rcu; }; struct netpoll_info { @@ -38,6 +39,7 @@ struct netpoll_info { struct delayed_work tx_work; struct netpoll *netpoll; + struct rcu_head rcu; }; void netpoll_send_udp(struct netpoll *np, const char *msg, int len); @@ -48,6 +50,7 @@ int netpoll_setup(struct netpoll *np); int netpoll_trap(void); void netpoll_set_trap(int trap); void __netpoll_cleanup(struct netpoll *np); +void __netpoll_free_rcu(struct netpoll *np); void netpoll_cleanup(struct netpoll *np); int __netpoll_rx(struct sk_buff *skb); void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index ee4ae0944cef..b65623f90660 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -704,11 +704,7 @@ static void vlan_dev_netpoll_cleanup(struct net_device *dev) info->netpoll = NULL; - /* Wait for transmitting packets to finish before freeing. */ - synchronize_rcu_bh(); - - __netpoll_cleanup(netpoll); - kfree(netpoll); + __netpoll_free_rcu(netpoll); } #endif /* CONFIG_NET_POLL_CONTROLLER */ diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index ed0e0f9dc788..f41ba4048c9a 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -267,11 +267,7 @@ void br_netpoll_disable(struct net_bridge_port *p) p->np = NULL; - /* Wait for transmitting packets to finish before freeing. */ - synchronize_rcu_bh(); - - __netpoll_cleanup(np); - kfree(np); + __netpoll_free_rcu(np); } #endif diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 37cc854774a4..dc17f1db1479 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -878,6 +878,24 @@ static int __init netpoll_init(void) } core_initcall(netpoll_init); +static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head) +{ + struct netpoll_info *npinfo = + container_of(rcu_head, struct netpoll_info, rcu); + + skb_queue_purge(&npinfo->arp_tx); + skb_queue_purge(&npinfo->txq); + + /* we can't call cancel_delayed_work_sync here, as we are in softirq */ + cancel_delayed_work(&npinfo->tx_work); + + /* clean after last, unfinished work */ + __skb_queue_purge(&npinfo->txq); + /* now cancel it again */ + cancel_delayed_work(&npinfo->tx_work); + kfree(npinfo); +} + void __netpoll_cleanup(struct netpoll *np) { struct netpoll_info *npinfo; @@ -903,20 +921,24 @@ void __netpoll_cleanup(struct netpoll *np) ops->ndo_netpoll_cleanup(np->dev); RCU_INIT_POINTER(np->dev->npinfo, NULL); + call_rcu_bh(&npinfo->rcu, rcu_cleanup_netpoll_info); + } +} +EXPORT_SYMBOL_GPL(__netpoll_cleanup); - /* avoid racing with NAPI reading npinfo */ - synchronize_rcu_bh(); +static void rcu_cleanup_netpoll(struct rcu_head *rcu_head) +{ + struct netpoll *np = container_of(rcu_head, struct netpoll, rcu); - skb_queue_purge(&npinfo->arp_tx); - skb_queue_purge(&npinfo->txq); - cancel_delayed_work_sync(&npinfo->tx_work); + __netpoll_cleanup(np); + kfree(np); +} - /* clean after last, unfinished work */ - __skb_queue_purge(&npinfo->txq); - kfree(npinfo); - } +void __netpoll_free_rcu(struct netpoll *np) +{ + call_rcu_bh(&np->rcu, rcu_cleanup_netpoll); } -EXPORT_SYMBOL_GPL(__netpoll_cleanup); +EXPORT_SYMBOL_GPL(__netpoll_free_rcu); void netpoll_cleanup(struct netpoll *np) { -- cgit v1.2.3 From 57c5d46191e75312934c00eba65b13a31ca95120 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Fri, 10 Aug 2012 01:24:40 +0000 Subject: netpoll: take rcu_read_lock_bh() in netpoll_rx() In __netpoll_rx(), it dereferences ->npinfo without rcu_dereference_bh(), this patch fixes it by using the 'npinfo' passed from netpoll_rx() where it is already dereferenced with rcu_dereference_bh(). Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/netpoll.h | 4 ++-- net/core/netpoll.c | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 907812efb4d9..5d881c388273 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -52,7 +52,7 @@ void netpoll_set_trap(int trap); void __netpoll_cleanup(struct netpoll *np); void __netpoll_free_rcu(struct netpoll *np); void netpoll_cleanup(struct netpoll *np); -int __netpoll_rx(struct sk_buff *skb); +int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo); void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, struct net_device *dev); static inline void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) @@ -77,7 +77,7 @@ static inline bool netpoll_rx(struct sk_buff *skb) spin_lock(&npinfo->rx_lock); /* check rx_flags again with the lock held */ - if (npinfo->rx_flags && __netpoll_rx(skb)) + if (npinfo->rx_flags && __netpoll_rx(skb, npinfo)) ret = true; spin_unlock(&npinfo->rx_lock); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index dc17f1db1479..d055bb01328b 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -543,13 +543,12 @@ static void arp_reply(struct sk_buff *skb) spin_unlock_irqrestore(&npinfo->rx_lock, flags); } -int __netpoll_rx(struct sk_buff *skb) +int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo) { int proto, len, ulen; int hits = 0; const struct iphdr *iph; struct udphdr *uh; - struct netpoll_info *npinfo = skb->dev->npinfo; struct netpoll *np, *tmp; if (list_empty(&npinfo->rx_np)) -- cgit v1.2.3 From 91fe4a4b9e490a24f6702dd8afe72d8afab6fcdb Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Fri, 10 Aug 2012 01:24:41 +0000 Subject: netpoll: use netpoll_rx_on() in netpoll_rx() The logic of the code is same, just call netpoll_rx_on(). Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/netpoll.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 5d881c388273..2d178baa49df 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -63,6 +63,13 @@ static inline void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) #ifdef CONFIG_NETPOLL +static inline int netpoll_rx_on(struct sk_buff *skb) +{ + struct netpoll_info *npinfo = rcu_dereference_bh(skb->dev->npinfo); + + return npinfo && (!list_empty(&npinfo->rx_np) || npinfo->rx_flags); +} + static inline bool netpoll_rx(struct sk_buff *skb) { struct netpoll_info *npinfo; @@ -70,11 +77,11 @@ static inline bool netpoll_rx(struct sk_buff *skb) bool ret = false; local_irq_save(flags); - npinfo = rcu_dereference_bh(skb->dev->npinfo); - if (!npinfo || (list_empty(&npinfo->rx_np) && !npinfo->rx_flags)) + if (!netpoll_rx_on(skb)) goto out; + npinfo = rcu_dereference_bh(skb->dev->npinfo); spin_lock(&npinfo->rx_lock); /* check rx_flags again with the lock held */ if (npinfo->rx_flags && __netpoll_rx(skb, npinfo)) @@ -86,13 +93,6 @@ out: return ret; } -static inline int netpoll_rx_on(struct sk_buff *skb) -{ - struct netpoll_info *npinfo = rcu_dereference_bh(skb->dev->npinfo); - - return npinfo && (!list_empty(&npinfo->rx_np) || npinfo->rx_flags); -} - static inline int netpoll_receive_skb(struct sk_buff *skb) { if (!list_empty(&skb->dev->napi_list)) -- cgit v1.2.3 From 2899656b494dcd118123af1126826b115c8ea6f9 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Fri, 10 Aug 2012 01:24:42 +0000 Subject: netpoll: take rcu_read_lock_bh() in netpoll_send_skb_on_dev() This patch fixes several problems in the call path of netpoll_send_skb_on_dev(): 1. Disable IRQ's before calling netpoll_send_skb_on_dev(). 2. All the callees of netpoll_send_skb_on_dev() should use rcu_dereference_bh() to dereference ->npinfo. 3. Rename arp_reply() to netpoll_arp_reply(), the former is too generic. Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/netpoll.h | 3 +++ net/core/netpoll.c | 31 +++++++++++++++++-------------- 2 files changed, 20 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 2d178baa49df..61aee86cf21d 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -57,7 +57,10 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, struct net_device *dev); static inline void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) { + unsigned long flags; + local_irq_save(flags); netpoll_send_skb_on_dev(np, skb, np->dev); + local_irq_restore(flags); } diff --git a/net/core/netpoll.c b/net/core/netpoll.c index d055bb01328b..174346ac15a0 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -54,7 +54,7 @@ static atomic_t trapped; MAX_UDP_CHUNK) static void zap_completion_queue(void); -static void arp_reply(struct sk_buff *skb); +static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo); static unsigned int carrier_timeout = 4; module_param(carrier_timeout, uint, 0644); @@ -170,7 +170,8 @@ static void poll_napi(struct net_device *dev) list_for_each_entry(napi, &dev->napi_list, dev_list) { if (napi->poll_owner != smp_processor_id() && spin_trylock(&napi->poll_lock)) { - budget = poll_one_napi(dev->npinfo, napi, budget); + budget = poll_one_napi(rcu_dereference_bh(dev->npinfo), + napi, budget); spin_unlock(&napi->poll_lock); if (!budget) @@ -185,13 +186,14 @@ static void service_arp_queue(struct netpoll_info *npi) struct sk_buff *skb; while ((skb = skb_dequeue(&npi->arp_tx))) - arp_reply(skb); + netpoll_arp_reply(skb, npi); } } static void netpoll_poll_dev(struct net_device *dev) { const struct net_device_ops *ops; + struct netpoll_info *ni = rcu_dereference_bh(dev->npinfo); if (!dev || !netif_running(dev)) return; @@ -206,17 +208,18 @@ static void netpoll_poll_dev(struct net_device *dev) poll_napi(dev); if (dev->flags & IFF_SLAVE) { - if (dev->npinfo) { + if (ni) { struct net_device *bond_dev = dev->master; struct sk_buff *skb; - while ((skb = skb_dequeue(&dev->npinfo->arp_tx))) { + struct netpoll_info *bond_ni = rcu_dereference_bh(bond_dev->npinfo); + while ((skb = skb_dequeue(&ni->arp_tx))) { skb->dev = bond_dev; - skb_queue_tail(&bond_dev->npinfo->arp_tx, skb); + skb_queue_tail(&bond_ni->arp_tx, skb); } } } - service_arp_queue(dev->npinfo); + service_arp_queue(ni); zap_completion_queue(); } @@ -302,6 +305,7 @@ static int netpoll_owner_active(struct net_device *dev) return 0; } +/* call with IRQ disabled */ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, struct net_device *dev) { @@ -309,8 +313,11 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, unsigned long tries; const struct net_device_ops *ops = dev->netdev_ops; /* It is up to the caller to keep npinfo alive. */ - struct netpoll_info *npinfo = np->dev->npinfo; + struct netpoll_info *npinfo; + + WARN_ON_ONCE(!irqs_disabled()); + npinfo = rcu_dereference_bh(np->dev->npinfo); if (!npinfo || !netif_running(dev) || !netif_device_present(dev)) { __kfree_skb(skb); return; @@ -319,11 +326,9 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, /* don't get messages out of order, and no recursion */ if (skb_queue_len(&npinfo->txq) == 0 && !netpoll_owner_active(dev)) { struct netdev_queue *txq; - unsigned long flags; txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); - local_irq_save(flags); /* try until next clock tick */ for (tries = jiffies_to_usecs(1)/USEC_PER_POLL; tries > 0; --tries) { @@ -347,10 +352,9 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb, } WARN_ONCE(!irqs_disabled(), - "netpoll_send_skb(): %s enabled interrupts in poll (%pF)\n", + "netpoll_send_skb_on_dev(): %s enabled interrupts in poll (%pF)\n", dev->name, ops->ndo_start_xmit); - local_irq_restore(flags); } if (status != NETDEV_TX_OK) { @@ -423,9 +427,8 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len) } EXPORT_SYMBOL(netpoll_send_udp); -static void arp_reply(struct sk_buff *skb) +static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo) { - struct netpoll_info *npinfo = skb->dev->npinfo; struct arphdr *arp; unsigned char *arp_ptr; int size, type = ARPOP_REPLY, ptype = ETH_P_ARP; -- cgit v1.2.3 From e15c3c2294605f09f9b336b2f3b97086ab4b8145 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Fri, 10 Aug 2012 01:24:45 +0000 Subject: netpoll: check netpoll tx status on the right device Although this doesn't matter actually, because netpoll_tx_running() doesn't use the parameter, the code will be more readable. For team_dev_queue_xmit() we have to move it down to avoid compile errors. Cc: David Miller Signed-off-by: Jiri Pirko Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 2 +- include/linux/if_team.h | 30 +++++++++++++++--------------- net/bridge/br_forward.c | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index e42891683e3b..d688a8af432c 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -398,7 +398,7 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, sizeof(qdisc_skb_cb(skb)->slave_dev_queue_mapping)); skb->queue_mapping = qdisc_skb_cb(skb)->slave_dev_queue_mapping; - if (unlikely(netpoll_tx_running(slave_dev))) + if (unlikely(netpoll_tx_running(bond->dev))) bond_netpoll_send_skb(bond_get_slave_by_dev(bond, slave_dev), skb); else dev_queue_xmit(skb); diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 6960fc1841a7..aa2e167e1ef4 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -96,21 +96,6 @@ static inline void team_netpoll_send_skb(struct team_port *port, } #endif -static inline int team_dev_queue_xmit(struct team *team, struct team_port *port, - struct sk_buff *skb) -{ - BUILD_BUG_ON(sizeof(skb->queue_mapping) != - sizeof(qdisc_skb_cb(skb)->slave_dev_queue_mapping)); - skb_set_queue_mapping(skb, qdisc_skb_cb(skb)->slave_dev_queue_mapping); - - skb->dev = port->dev; - if (unlikely(netpoll_tx_running(port->dev))) { - team_netpoll_send_skb(port, skb); - return 0; - } - return dev_queue_xmit(skb); -} - struct team_mode_ops { int (*init)(struct team *team); void (*exit)(struct team *team); @@ -200,6 +185,21 @@ struct team { long mode_priv[TEAM_MODE_PRIV_LONGS]; }; +static inline int team_dev_queue_xmit(struct team *team, struct team_port *port, + struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(skb->queue_mapping) != + sizeof(qdisc_skb_cb(skb)->slave_dev_queue_mapping)); + skb_set_queue_mapping(skb, qdisc_skb_cb(skb)->slave_dev_queue_mapping); + + skb->dev = port->dev; + if (unlikely(netpoll_tx_running(team->dev))) { + team_netpoll_send_skb(port, skb); + return 0; + } + return dev_queue_xmit(skb); +} + static inline struct hlist_head *team_port_index_hash(struct team *team, int port_index) { diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index e9466d412707..02015a505d2a 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -65,7 +65,7 @@ static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) { skb->dev = to->dev; - if (unlikely(netpoll_tx_running(to->dev))) { + if (unlikely(netpoll_tx_running(to->br->dev))) { if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb)) kfree_skb(skb); else { -- cgit v1.2.3 From 77ab8a54d9a8dcc4a46484a04133314f33f2aba6 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Fri, 10 Aug 2012 01:24:46 +0000 Subject: netpoll: convert several functions to bool These functions are just boolean, let them return bool instead of int. Cc: David Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/netpoll.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index 61aee86cf21d..66d5379c305e 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -66,7 +66,7 @@ static inline void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) #ifdef CONFIG_NETPOLL -static inline int netpoll_rx_on(struct sk_buff *skb) +static inline bool netpoll_rx_on(struct sk_buff *skb) { struct netpoll_info *npinfo = rcu_dereference_bh(skb->dev->npinfo); @@ -125,7 +125,7 @@ static inline void netpoll_poll_unlock(void *have) } } -static inline int netpoll_tx_running(struct net_device *dev) +static inline bool netpoll_tx_running(struct net_device *dev) { return irqs_disabled(); } @@ -133,11 +133,11 @@ static inline int netpoll_tx_running(struct net_device *dev) #else static inline bool netpoll_rx(struct sk_buff *skb) { - return 0; + return false; } -static inline int netpoll_rx_on(struct sk_buff *skb) +static inline bool netpoll_rx_on(struct sk_buff *skb) { - return 0; + return false; } static inline int netpoll_receive_skb(struct sk_buff *skb) { @@ -153,9 +153,9 @@ static inline void netpoll_poll_unlock(void *have) static inline void netpoll_netdev_init(struct net_device *dev) { } -static inline int netpoll_tx_running(struct net_device *dev) +static inline bool netpoll_tx_running(struct net_device *dev) { - return 0; + return false; } #endif -- cgit v1.2.3 From 6024935f5ff5f1646bce8404416318e5fd4a0c4a Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 13 Aug 2012 02:49:59 +0000 Subject: llc2: Fix silent failure of llc_station_init() llc_station_init() creates and processes an event skb with no effect other than to change the state from DOWN to UP. Allocation failure is reported, but then ignored by its caller, llc2_init(). Remove this possibility by simply initialising the state as UP. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/net/llc.h | 2 +- net/llc/llc_station.c | 19 ++----------------- 2 files changed, 3 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/llc.h b/include/net/llc.h index 226c846cab08..f2d0fc570527 100644 --- a/include/net/llc.h +++ b/include/net/llc.h @@ -133,7 +133,7 @@ extern int llc_build_and_send_ui_pkt(struct llc_sap *sap, struct sk_buff *skb, extern void llc_sap_handler(struct llc_sap *sap, struct sk_buff *skb); extern void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_station_init(void); +extern void llc_station_init(void); extern void llc_station_exit(void); #ifdef CONFIG_PROC_FS diff --git a/net/llc/llc_station.c b/net/llc/llc_station.c index 6828e39ec2ec..45ddbb93c5d0 100644 --- a/net/llc/llc_station.c +++ b/net/llc/llc_station.c @@ -687,12 +687,8 @@ static void llc_station_rcv(struct sk_buff *skb) llc_station_state_process(skb); } -int __init llc_station_init(void) +void __init llc_station_init(void) { - int rc = -ENOBUFS; - struct sk_buff *skb; - struct llc_station_state_ev *ev; - skb_queue_head_init(&llc_main_station.mac_pdu_q); skb_queue_head_init(&llc_main_station.ev_q.list); spin_lock_init(&llc_main_station.ev_q.lock); @@ -700,20 +696,9 @@ int __init llc_station_init(void) (unsigned long)&llc_main_station); llc_main_station.ack_timer.expires = jiffies + sysctl_llc_station_ack_timeout; - skb = alloc_skb(0, GFP_ATOMIC); - if (!skb) - goto out; - rc = 0; llc_set_station_handler(llc_station_rcv); - ev = llc_station_ev(skb); - memset(ev, 0, sizeof(*ev)); llc_main_station.maximum_retry = 1; - llc_main_station.state = LLC_STATION_STATE_DOWN; - ev->type = LLC_STATION_EV_TYPE_SIMPLE; - ev->prim_type = LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK; - rc = llc_station_next_state(skb); -out: - return rc; + llc_main_station.state = LLC_STATION_STATE_UP; } void __exit llc_station_exit(void) -- cgit v1.2.3 From 4e8b14526ca7fb046a81c94002c1c43b6fdf0e9b Mon Sep 17 00:00:00 2001 From: John Stultz Date: Wed, 8 Aug 2012 15:36:20 -0400 Subject: time: Improve sanity checking of timekeeping inputs Unexpected behavior could occur if the time is set to a value large enough to overflow a 64bit ktime_t (which is something larger then the year 2262). Also unexpected behavior could occur if large negative offsets are injected via adjtimex. So this patch improves the sanity check timekeeping inputs by improving the timespec_valid() check, and then makes better use of timespec_valid() to make sure we don't set the time to an invalid negative value or one that overflows ktime_t. Note: This does not protect from setting the time close to overflowing ktime_t and then letting natural accumulation cause the overflow. Reported-by: CAI Qian Reported-by: Sasha Levin Signed-off-by: John Stultz Cc: Peter Zijlstra Cc: Prarit Bhargava Cc: Zhouping Liu Cc: Ingo Molnar Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/1344454580-17031-1-git-send-email-john.stultz@linaro.org Signed-off-by: Thomas Gleixner --- include/linux/ktime.h | 7 ------- include/linux/time.h | 22 ++++++++++++++++++++-- kernel/time/timekeeping.c | 26 ++++++++++++++++++++++++-- 3 files changed, 44 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/ktime.h b/include/linux/ktime.h index 603bec2913b0..06177ba10a16 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -58,13 +58,6 @@ union ktime { typedef union ktime ktime_t; /* Kill this */ -#define KTIME_MAX ((s64)~((u64)1 << 63)) -#if (BITS_PER_LONG == 64) -# define KTIME_SEC_MAX (KTIME_MAX / NSEC_PER_SEC) -#else -# define KTIME_SEC_MAX LONG_MAX -#endif - /* * ktime_t definitions when using the 64-bit scalar representation: */ diff --git a/include/linux/time.h b/include/linux/time.h index c81c5e40fcb5..b0bbd8f0130d 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -107,11 +107,29 @@ static inline struct timespec timespec_sub(struct timespec lhs, return ts_delta; } +#define KTIME_MAX ((s64)~((u64)1 << 63)) +#if (BITS_PER_LONG == 64) +# define KTIME_SEC_MAX (KTIME_MAX / NSEC_PER_SEC) +#else +# define KTIME_SEC_MAX LONG_MAX +#endif + /* * Returns true if the timespec is norm, false if denorm: */ -#define timespec_valid(ts) \ - (((ts)->tv_sec >= 0) && (((unsigned long) (ts)->tv_nsec) < NSEC_PER_SEC)) +static inline bool timespec_valid(const struct timespec *ts) +{ + /* Dates before 1970 are bogus */ + if (ts->tv_sec < 0) + return false; + /* Can't have more nanoseconds then a second */ + if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) + return false; + /* Disallow values that could overflow ktime_t */ + if ((unsigned long long)ts->tv_sec >= KTIME_SEC_MAX) + return false; + return true; +} extern void read_persistent_clock(struct timespec *ts); extern void read_boot_clock(struct timespec *ts); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index e16af197a2bc..898bef066a44 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -427,7 +427,7 @@ int do_settimeofday(const struct timespec *tv) struct timespec ts_delta, xt; unsigned long flags; - if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) + if (!timespec_valid(tv)) return -EINVAL; write_seqlock_irqsave(&tk->lock, flags); @@ -463,6 +463,8 @@ int timekeeping_inject_offset(struct timespec *ts) { struct timekeeper *tk = &timekeeper; unsigned long flags; + struct timespec tmp; + int ret = 0; if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) return -EINVAL; @@ -471,10 +473,17 @@ int timekeeping_inject_offset(struct timespec *ts) timekeeping_forward_now(tk); + /* Make sure the proposed value is valid */ + tmp = timespec_add(tk_xtime(tk), *ts); + if (!timespec_valid(&tmp)) { + ret = -EINVAL; + goto error; + } tk_xtime_add(tk, ts); tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *ts)); +error: /* even if we error out, we forwarded the time, so call update */ timekeeping_update(tk, true); write_sequnlock_irqrestore(&tk->lock, flags); @@ -482,7 +491,7 @@ int timekeeping_inject_offset(struct timespec *ts) /* signal hrtimers about time change */ clock_was_set(); - return 0; + return ret; } EXPORT_SYMBOL(timekeeping_inject_offset); @@ -649,7 +658,20 @@ void __init timekeeping_init(void) struct timespec now, boot, tmp; read_persistent_clock(&now); + if (!timespec_valid(&now)) { + pr_warn("WARNING: Persistent clock returned invalid value!\n" + " Check your CMOS/BIOS settings.\n"); + now.tv_sec = 0; + now.tv_nsec = 0; + } + read_boot_clock(&boot); + if (!timespec_valid(&boot)) { + pr_warn("WARNING: Boot clock returned invalid value!\n" + " Check your CMOS/BIOS settings.\n"); + boot.tv_sec = 0; + boot.tv_nsec = 0; + } seqlock_init(&tk->lock); -- cgit v1.2.3 From 58569aee5a1a5dcc25c34a0a2ed9a377874e6b05 Mon Sep 17 00:00:00 2001 From: "Arnaud Patard (Rtp)" Date: Thu, 26 Jul 2012 12:15:46 +0200 Subject: ARM: Orion: Set eth packet size csum offload limit The mv643xx ethernet controller limits the packet size for the TX checksum offloading. This patch sets this limits for Kirkwood and Dove which have smaller limits that the default. As a side note, this patch is an updated version of a patch sent some years ago: http://lists.infradead.org/pipermail/linux-arm-kernel/2010-June/017320.html which seems to have been lost. Signed-off-by: Arnaud Patard Signed-off-by: Jason Cooper Cc: --- arch/arm/mach-dove/common.c | 3 ++- arch/arm/mach-kirkwood/common.c | 4 ++-- arch/arm/mach-mv78xx0/common.c | 6 ++++-- arch/arm/mach-orion5x/common.c | 3 ++- arch/arm/plat-orion/common.c | 8 ++++++-- arch/arm/plat-orion/include/plat/common.h | 6 ++++-- include/linux/mv643xx_eth.h | 2 ++ 7 files changed, 22 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-dove/common.c b/arch/arm/mach-dove/common.c index 4db5de54b6a7..6321567d8eaa 100644 --- a/arch/arm/mach-dove/common.c +++ b/arch/arm/mach-dove/common.c @@ -102,7 +102,8 @@ void __init dove_ehci1_init(void) void __init dove_ge00_init(struct mv643xx_eth_platform_data *eth_data) { orion_ge00_init(eth_data, DOVE_GE00_PHYS_BASE, - IRQ_DOVE_GE00_SUM, IRQ_DOVE_GE00_ERR); + IRQ_DOVE_GE00_SUM, IRQ_DOVE_GE00_ERR, + 1600); } /***************************************************************************** diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c index c4b64adcbfce..3226077735b1 100644 --- a/arch/arm/mach-kirkwood/common.c +++ b/arch/arm/mach-kirkwood/common.c @@ -301,7 +301,7 @@ void __init kirkwood_ge00_init(struct mv643xx_eth_platform_data *eth_data) { orion_ge00_init(eth_data, GE00_PHYS_BASE, IRQ_KIRKWOOD_GE00_SUM, - IRQ_KIRKWOOD_GE00_ERR); + IRQ_KIRKWOOD_GE00_ERR, 1600); /* The interface forgets the MAC address assigned by u-boot if the clock is turned off, so claim the clk now. */ clk_prepare_enable(ge0); @@ -315,7 +315,7 @@ void __init kirkwood_ge01_init(struct mv643xx_eth_platform_data *eth_data) { orion_ge01_init(eth_data, GE01_PHYS_BASE, IRQ_KIRKWOOD_GE01_SUM, - IRQ_KIRKWOOD_GE01_ERR); + IRQ_KIRKWOOD_GE01_ERR, 1600); clk_prepare_enable(ge1); } diff --git a/arch/arm/mach-mv78xx0/common.c b/arch/arm/mach-mv78xx0/common.c index b4c53b846c9c..3057f7d4329a 100644 --- a/arch/arm/mach-mv78xx0/common.c +++ b/arch/arm/mach-mv78xx0/common.c @@ -213,7 +213,8 @@ void __init mv78xx0_ge00_init(struct mv643xx_eth_platform_data *eth_data) { orion_ge00_init(eth_data, GE00_PHYS_BASE, IRQ_MV78XX0_GE00_SUM, - IRQ_MV78XX0_GE_ERR); + IRQ_MV78XX0_GE_ERR, + MV643XX_TX_CSUM_DEFAULT_LIMIT); } @@ -224,7 +225,8 @@ void __init mv78xx0_ge01_init(struct mv643xx_eth_platform_data *eth_data) { orion_ge01_init(eth_data, GE01_PHYS_BASE, IRQ_MV78XX0_GE01_SUM, - NO_IRQ); + NO_IRQ, + MV643XX_TX_CSUM_DEFAULT_LIMIT); } diff --git a/arch/arm/mach-orion5x/common.c b/arch/arm/mach-orion5x/common.c index 9148b229d0de..410291c67666 100644 --- a/arch/arm/mach-orion5x/common.c +++ b/arch/arm/mach-orion5x/common.c @@ -109,7 +109,8 @@ void __init orion5x_eth_init(struct mv643xx_eth_platform_data *eth_data) { orion_ge00_init(eth_data, ORION5X_ETH_PHYS_BASE, IRQ_ORION5X_ETH_SUM, - IRQ_ORION5X_ETH_ERR); + IRQ_ORION5X_ETH_ERR, + MV643XX_TX_CSUM_DEFAULT_LIMIT); } diff --git a/arch/arm/plat-orion/common.c b/arch/arm/plat-orion/common.c index d245a87dc014..b8b747a9d360 100644 --- a/arch/arm/plat-orion/common.c +++ b/arch/arm/plat-orion/common.c @@ -291,10 +291,12 @@ static struct platform_device orion_ge00 = { void __init orion_ge00_init(struct mv643xx_eth_platform_data *eth_data, unsigned long mapbase, unsigned long irq, - unsigned long irq_err) + unsigned long irq_err, + unsigned int tx_csum_limit) { fill_resources(&orion_ge00_shared, orion_ge00_shared_resources, mapbase + 0x2000, SZ_16K - 1, irq_err); + orion_ge00_shared_data.tx_csum_limit = tx_csum_limit; ge_complete(&orion_ge00_shared_data, orion_ge00_resources, irq, &orion_ge00_shared, eth_data, &orion_ge00); @@ -343,10 +345,12 @@ static struct platform_device orion_ge01 = { void __init orion_ge01_init(struct mv643xx_eth_platform_data *eth_data, unsigned long mapbase, unsigned long irq, - unsigned long irq_err) + unsigned long irq_err, + unsigned int tx_csum_limit) { fill_resources(&orion_ge01_shared, orion_ge01_shared_resources, mapbase + 0x2000, SZ_16K - 1, irq_err); + orion_ge01_shared_data.tx_csum_limit = tx_csum_limit; ge_complete(&orion_ge01_shared_data, orion_ge01_resources, irq, &orion_ge01_shared, eth_data, &orion_ge01); diff --git a/arch/arm/plat-orion/include/plat/common.h b/arch/arm/plat-orion/include/plat/common.h index e00fdb213609..ae2377ef63e5 100644 --- a/arch/arm/plat-orion/include/plat/common.h +++ b/arch/arm/plat-orion/include/plat/common.h @@ -39,12 +39,14 @@ void __init orion_rtc_init(unsigned long mapbase, void __init orion_ge00_init(struct mv643xx_eth_platform_data *eth_data, unsigned long mapbase, unsigned long irq, - unsigned long irq_err); + unsigned long irq_err, + unsigned int tx_csum_limit); void __init orion_ge01_init(struct mv643xx_eth_platform_data *eth_data, unsigned long mapbase, unsigned long irq, - unsigned long irq_err); + unsigned long irq_err, + unsigned int tx_csum_limit); void __init orion_ge10_init(struct mv643xx_eth_platform_data *eth_data, unsigned long mapbase, diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h index 51bf8ada6dc0..49258e0ed1c6 100644 --- a/include/linux/mv643xx_eth.h +++ b/include/linux/mv643xx_eth.h @@ -15,6 +15,8 @@ #define MV643XX_ETH_SIZE_REG_4 0x2224 #define MV643XX_ETH_BASE_ADDR_ENABLE_REG 0x2290 +#define MV643XX_TX_CSUM_DEFAULT_LIMIT 0 + struct mv643xx_eth_shared_platform_data { struct mbus_dram_target_info *dram; struct platform_device *shared_smi; -- cgit v1.2.3 From ca08649eb5dd30f11a5a8fe8659b48899b7ea6a1 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Thu, 16 Aug 2012 11:31:27 -0400 Subject: Revert "xen PVonHVM: move shared_info to MMIO before kexec" This reverts commit 00e37bdb0113a98408de42db85be002f21dbffd3. During shutdown of PVHVM guests with more than 2VCPUs on certain machines we can hit the race where the replaced shared_info is not replaced fast enough and the PV time clock retries reading the same area over and over without any any success and is stuck in an infinite loop. Acked-by: Olaf Hering Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/enlighten.c | 118 +++++---------------------------------------- arch/x86/xen/suspend.c | 2 +- arch/x86/xen/xen-ops.h | 2 +- drivers/xen/platform-pci.c | 15 ------ include/xen/events.h | 2 - 5 files changed, 13 insertions(+), 126 deletions(-) (limited to 'include') diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index a6f8acbdfc9a..f1814fc2cb77 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include @@ -1472,130 +1471,38 @@ asmlinkage void __init xen_start_kernel(void) #endif } -#ifdef CONFIG_XEN_PVHVM -/* - * The pfn containing the shared_info is located somewhere in RAM. This - * will cause trouble if the current kernel is doing a kexec boot into a - * new kernel. The new kernel (and its startup code) can not know where - * the pfn is, so it can not reserve the page. The hypervisor will - * continue to update the pfn, and as a result memory corruption occours - * in the new kernel. - * - * One way to work around this issue is to allocate a page in the - * xen-platform pci device's BAR memory range. But pci init is done very - * late and the shared_info page is already in use very early to read - * the pvclock. So moving the pfn from RAM to MMIO is racy because some - * code paths on other vcpus could access the pfn during the small - * window when the old pfn is moved to the new pfn. There is even a - * small window were the old pfn is not backed by a mfn, and during that - * time all reads return -1. - * - * Because it is not known upfront where the MMIO region is located it - * can not be used right from the start in xen_hvm_init_shared_info. - * - * To minimise trouble the move of the pfn is done shortly before kexec. - * This does not eliminate the race because all vcpus are still online - * when the syscore_ops will be called. But hopefully there is no work - * pending at this point in time. Also the syscore_op is run last which - * reduces the risk further. - */ - -static struct shared_info *xen_hvm_shared_info; - -static void xen_hvm_connect_shared_info(unsigned long pfn) +void __ref xen_hvm_init_shared_info(void) { + int cpu; struct xen_add_to_physmap xatp; + static struct shared_info *shared_info_page = 0; + if (!shared_info_page) + shared_info_page = (struct shared_info *) + extend_brk(PAGE_SIZE, PAGE_SIZE); xatp.domid = DOMID_SELF; xatp.idx = 0; xatp.space = XENMAPSPACE_shared_info; - xatp.gpfn = pfn; + xatp.gpfn = __pa(shared_info_page) >> PAGE_SHIFT; if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) BUG(); -} -static void xen_hvm_set_shared_info(struct shared_info *sip) -{ - int cpu; - - HYPERVISOR_shared_info = sip; + HYPERVISOR_shared_info = (struct shared_info *)shared_info_page; /* xen_vcpu is a pointer to the vcpu_info struct in the shared_info * page, we use it in the event channel upcall and in some pvclock * related functions. We don't need the vcpu_info placement * optimizations because we don't use any pv_mmu or pv_irq op on * HVM. - * When xen_hvm_set_shared_info is run at boot time only vcpu 0 is - * online but xen_hvm_set_shared_info is run at resume time too and + * When xen_hvm_init_shared_info is run at boot time only vcpu 0 is + * online but xen_hvm_init_shared_info is run at resume time too and * in that case multiple vcpus might be online. */ for_each_online_cpu(cpu) { per_cpu(xen_vcpu, cpu) = &HYPERVISOR_shared_info->vcpu_info[cpu]; } } -/* Reconnect the shared_info pfn to a mfn */ -void xen_hvm_resume_shared_info(void) -{ - xen_hvm_connect_shared_info(__pa(xen_hvm_shared_info) >> PAGE_SHIFT); -} - -#ifdef CONFIG_KEXEC -static struct shared_info *xen_hvm_shared_info_kexec; -static unsigned long xen_hvm_shared_info_pfn_kexec; - -/* Remember a pfn in MMIO space for kexec reboot */ -void __devinit xen_hvm_prepare_kexec(struct shared_info *sip, unsigned long pfn) -{ - xen_hvm_shared_info_kexec = sip; - xen_hvm_shared_info_pfn_kexec = pfn; -} - -static void xen_hvm_syscore_shutdown(void) -{ - struct xen_memory_reservation reservation = { - .domid = DOMID_SELF, - .nr_extents = 1, - }; - unsigned long prev_pfn; - int rc; - - if (!xen_hvm_shared_info_kexec) - return; - - prev_pfn = __pa(xen_hvm_shared_info) >> PAGE_SHIFT; - set_xen_guest_handle(reservation.extent_start, &prev_pfn); - - /* Move pfn to MMIO, disconnects previous pfn from mfn */ - xen_hvm_connect_shared_info(xen_hvm_shared_info_pfn_kexec); - - /* Update pointers, following hypercall is also a memory barrier */ - xen_hvm_set_shared_info(xen_hvm_shared_info_kexec); - - /* Allocate new mfn for previous pfn */ - do { - rc = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation); - if (rc == 0) - msleep(123); - } while (rc == 0); - - /* Make sure the previous pfn is really connected to a (new) mfn */ - BUG_ON(rc != 1); -} - -static struct syscore_ops xen_hvm_syscore_ops = { - .shutdown = xen_hvm_syscore_shutdown, -}; -#endif - -/* Use a pfn in RAM, may move to MMIO before kexec. */ -static void __init xen_hvm_init_shared_info(void) -{ - /* Remember pointer for resume */ - xen_hvm_shared_info = extend_brk(PAGE_SIZE, PAGE_SIZE); - xen_hvm_connect_shared_info(__pa(xen_hvm_shared_info) >> PAGE_SHIFT); - xen_hvm_set_shared_info(xen_hvm_shared_info); -} - +#ifdef CONFIG_XEN_PVHVM static void __init init_hvm_pv_info(void) { int major, minor; @@ -1646,9 +1553,6 @@ static void __init xen_hvm_guest_init(void) init_hvm_pv_info(); xen_hvm_init_shared_info(); -#ifdef CONFIG_KEXEC - register_syscore_ops(&xen_hvm_syscore_ops); -#endif if (xen_feature(XENFEAT_hvm_callback_vector)) xen_have_vector_callback = 1; diff --git a/arch/x86/xen/suspend.c b/arch/x86/xen/suspend.c index ae8a00c39de4..45329c8c226e 100644 --- a/arch/x86/xen/suspend.c +++ b/arch/x86/xen/suspend.c @@ -30,7 +30,7 @@ void xen_arch_hvm_post_suspend(int suspend_cancelled) { #ifdef CONFIG_XEN_PVHVM int cpu; - xen_hvm_resume_shared_info(); + xen_hvm_init_shared_info(); xen_callback_vector(); xen_unplug_emulated_devices(); if (xen_feature(XENFEAT_hvm_safe_pvclock)) { diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h index 1e4329e04e0f..202d4c150154 100644 --- a/arch/x86/xen/xen-ops.h +++ b/arch/x86/xen/xen-ops.h @@ -41,7 +41,7 @@ void xen_enable_syscall(void); void xen_vcpu_restore(void); void xen_callback_vector(void); -void xen_hvm_resume_shared_info(void); +void xen_hvm_init_shared_info(void); void xen_unplug_emulated_devices(void); void __init xen_build_dynamic_phys_to_machine(void); diff --git a/drivers/xen/platform-pci.c b/drivers/xen/platform-pci.c index d4c50d63acbc..97ca359ae2bd 100644 --- a/drivers/xen/platform-pci.c +++ b/drivers/xen/platform-pci.c @@ -101,19 +101,6 @@ static int platform_pci_resume(struct pci_dev *pdev) return 0; } -static void __devinit prepare_shared_info(void) -{ -#ifdef CONFIG_KEXEC - unsigned long addr; - struct shared_info *hvm_shared_info; - - addr = alloc_xen_mmio(PAGE_SIZE); - hvm_shared_info = ioremap(addr, PAGE_SIZE); - memset(hvm_shared_info, 0, PAGE_SIZE); - xen_hvm_prepare_kexec(hvm_shared_info, addr >> PAGE_SHIFT); -#endif -} - static int __devinit platform_pci_init(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -151,8 +138,6 @@ static int __devinit platform_pci_init(struct pci_dev *pdev, platform_mmio = mmio_addr; platform_mmiolen = mmio_len; - prepare_shared_info(); - if (!xen_have_vector_callback) { ret = xen_allocate_irq(pdev); if (ret) { diff --git a/include/xen/events.h b/include/xen/events.h index 9c641deb65d2..04399b28e821 100644 --- a/include/xen/events.h +++ b/include/xen/events.h @@ -58,8 +58,6 @@ void notify_remote_via_irq(int irq); void xen_irq_resume(void); -void xen_hvm_prepare_kexec(struct shared_info *sip, unsigned long pfn); - /* Clear an irq's pending state, in preparation for polling on it */ void xen_clear_irq_pending(int irq); void xen_set_irq_pending(int irq); -- cgit v1.2.3 From 8857df3aceb7a8eb7558059b7da109e41dd1fb95 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Fri, 20 Jul 2012 09:31:00 +0100 Subject: iio: frequency: ADF4350: Fix potential reference div factor overflow. With small channel spacing values and high reference frequencies it is possible to exceed the range of the 10-bit counter. Workaround by checking the range and widening some constrains. We don't use the REG1_PHASE value in this case the datasheet recommends to set it to 1 if not used. Signed-off-by: Michael Hennerich Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adf4350.c | 24 +++++++++++++++--------- include/linux/iio/frequency/adf4350.h | 2 ++ 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index 59fbb3ae40e7..e35bb8f6fe75 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -129,7 +129,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq) { struct adf4350_platform_data *pdata = st->pdata; u64 tmp; - u32 div_gcd, prescaler; + u32 div_gcd, prescaler, chspc; u16 mdiv, r_cnt = 0; u8 band_sel_div; @@ -158,14 +158,20 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq) if (pdata->ref_div_factor) r_cnt = pdata->ref_div_factor - 1; - do { - r_cnt = adf4350_tune_r_cnt(st, r_cnt); + chspc = st->chspc; - st->r1_mod = st->fpfd / st->chspc; - while (st->r1_mod > ADF4350_MAX_MODULUS) { - r_cnt = adf4350_tune_r_cnt(st, r_cnt); - st->r1_mod = st->fpfd / st->chspc; - } + do { + do { + do { + r_cnt = adf4350_tune_r_cnt(st, r_cnt); + st->r1_mod = st->fpfd / chspc; + if (r_cnt > ADF4350_MAX_R_CNT) { + /* try higher spacing values */ + chspc++; + r_cnt = 0; + } + } while ((st->r1_mod > ADF4350_MAX_MODULUS) && r_cnt); + } while (r_cnt == 0); tmp = freq * (u64)st->r1_mod + (st->fpfd > 1); do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */ @@ -194,7 +200,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq) st->regs[ADF4350_REG0] = ADF4350_REG0_INT(st->r0_int) | ADF4350_REG0_FRACT(st->r0_fract); - st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(0) | + st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(1) | ADF4350_REG1_MOD(st->r1_mod) | prescaler; diff --git a/include/linux/iio/frequency/adf4350.h b/include/linux/iio/frequency/adf4350.h index b76b4a87065e..be91f344d5fc 100644 --- a/include/linux/iio/frequency/adf4350.h +++ b/include/linux/iio/frequency/adf4350.h @@ -87,6 +87,8 @@ #define ADF4350_MAX_BANDSEL_CLK 125000 /* Hz */ #define ADF4350_MAX_FREQ_REFIN 250000000 /* Hz */ #define ADF4350_MAX_MODULUS 4095 +#define ADF4350_MAX_R_CNT 1023 + /** * struct adf4350_platform_data - platform specific information -- cgit v1.2.3 From ac5aa7f9e0891a115ab307b4bdde9c55b9232970 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 10 Aug 2012 16:52:58 +0200 Subject: pinctrl: header: trivial: declare struct device As struct device is used as a function argument, it should at least be declared (device.h is not included). Signed-off-by: Richard Genoud Signed-off-by: Linus Walleij --- include/linux/pinctrl/consumer.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h index 6dd96fb45482..e9b7f4350844 100644 --- a/include/linux/pinctrl/consumer.h +++ b/include/linux/pinctrl/consumer.h @@ -20,6 +20,7 @@ /* This struct is private to the core and should be regarded as a cookie */ struct pinctrl; struct pinctrl_state; +struct device; #ifdef CONFIG_PINCTRL -- cgit v1.2.3 From 8513915accc611e576dbebb93422c257e7e68be8 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 18 Aug 2012 17:43:05 -0700 Subject: ALSA: fix pcm.h kernel-doc warning and notation Fix kernel-doc warning in and add function name to make the kernel-doc notation complete. Warning(include/sound/pcm.h:1081): No description found for parameter 'substream' Signed-off-by: Randy Dunlap Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index c75c0d1a85e2..cdca2ab1e711 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1075,7 +1075,8 @@ static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) const char *snd_pcm_format_name(snd_pcm_format_t format); /** - * Get a string naming the direction of a stream + * snd_pcm_stream_str - Get a string naming the direction of a stream + * @substream: the pcm substream instance */ static inline const char *snd_pcm_stream_str(struct snd_pcm_substream *substream) { -- cgit v1.2.3 From 3296193d1421c2d6f9e49e181cecfd917f0f5764 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 14 Aug 2012 13:20:23 +0000 Subject: dt: introduce for_each_available_child_of_node, of_get_next_available_child Macro for_each_child_of_node() makes it easy to iterate over all of the children for a given device tree node, including those nodes that are marked as unavailable (i.e. status = "disabled"). Introduce for_each_available_child_of_node(), which is like for_each_child_of_node(), but it automatically skips unavailable nodes. This also requires the introduction of helper function of_get_next_available_child(), which returns the next available child node. Signed-off-by: Timur Tabi Signed-off-by: David S. Miller --- drivers/of/base.c | 27 +++++++++++++++++++++++++++ include/linux/of.h | 7 +++++++ 2 files changed, 34 insertions(+) (limited to 'include') diff --git a/drivers/of/base.c b/drivers/of/base.c index c181b94abc36..d4a1c9a043e1 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -363,6 +363,33 @@ struct device_node *of_get_next_child(const struct device_node *node, } EXPORT_SYMBOL(of_get_next_child); +/** + * of_get_next_available_child - Find the next available child node + * @node: parent node + * @prev: previous child of the parent node, or NULL to get first + * + * This function is like of_get_next_child(), except that it + * automatically skips any disabled nodes (i.e. status = "disabled"). + */ +struct device_node *of_get_next_available_child(const struct device_node *node, + struct device_node *prev) +{ + struct device_node *next; + + read_lock(&devtree_lock); + next = prev ? prev->sibling : node->child; + for (; next; next = next->sibling) { + if (!of_device_is_available(next)) + continue; + if (of_node_get(next)) + break; + } + of_node_put(prev); + read_unlock(&devtree_lock); + return next; +} +EXPORT_SYMBOL(of_get_next_available_child); + /** * of_find_node_by_path - Find a node matching a full OF path * @path: The full path to match diff --git a/include/linux/of.h b/include/linux/of.h index 5919ee33f2b7..1b1163225f3b 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -190,10 +190,17 @@ extern struct device_node *of_get_parent(const struct device_node *node); extern struct device_node *of_get_next_parent(struct device_node *node); extern struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev); +extern struct device_node *of_get_next_available_child( + const struct device_node *node, struct device_node *prev); + #define for_each_child_of_node(parent, child) \ for (child = of_get_next_child(parent, NULL); child != NULL; \ child = of_get_next_child(parent, child)) +#define for_each_available_child_of_node(parent, child) \ + for (child = of_get_next_available_child(parent, NULL); child != NULL; \ + child = of_get_next_available_child(parent, child)) + static inline int of_get_child_count(const struct device_node *np) { struct device_node *child; -- cgit v1.2.3 From c0de08d04215031d68fa13af36f347a6cfa252ca Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Thu, 16 Aug 2012 22:02:58 +0000 Subject: af_packet: don't emit packet on orig fanout group If a packet is emitted on one socket in one group of fanout sockets, it is transmitted again. It is thus read again on one of the sockets of the fanout group. This result in a loop for software which generate packets when receiving one. This retransmission is not the intended behavior: a fanout group must behave like a single socket. The packet should not be transmitted on a socket if it originates from a socket belonging to the same fanout group. This patch fixes the issue by changing the transmission check to take fanout group info account. Reported-by: Aleksandr Kotov Signed-off-by: Eric Leblond Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 16 ++++++++++++++-- net/packet/af_packet.c | 9 +++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 3560d688161e..59dc05f38247 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1522,6 +1522,8 @@ struct packet_type { struct sk_buff **(*gro_receive)(struct sk_buff **head, struct sk_buff *skb); int (*gro_complete)(struct sk_buff *skb); + bool (*id_match)(struct packet_type *ptype, + struct sock *sk); void *af_packet_priv; struct list_head list; }; diff --git a/net/core/dev.c b/net/core/dev.c index a39354ee1432..debd9372472f 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1642,6 +1642,19 @@ static inline int deliver_skb(struct sk_buff *skb, return pt_prev->func(skb, skb->dev, pt_prev, orig_dev); } +static inline bool skb_loop_sk(struct packet_type *ptype, struct sk_buff *skb) +{ + if (ptype->af_packet_priv == NULL) + return false; + + if (ptype->id_match) + return ptype->id_match(ptype, skb->sk); + else if ((struct sock *)ptype->af_packet_priv == skb->sk) + return true; + + return false; +} + /* * Support routine. Sends outgoing frames to any network * taps currently in use. @@ -1659,8 +1672,7 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) * they originated from - MvS (miquels@drinkel.ow.org) */ if ((ptype->dev == dev || !ptype->dev) && - (ptype->af_packet_priv == NULL || - (struct sock *)ptype->af_packet_priv != skb->sk)) { + (!skb_loop_sk(ptype, skb))) { if (pt_prev) { deliver_skb(skb2, pt_prev, skb->dev); pt_prev = ptype; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 8ac890a1a4c0..aee7196aac36 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1273,6 +1273,14 @@ static void __fanout_unlink(struct sock *sk, struct packet_sock *po) spin_unlock(&f->lock); } +bool match_fanout_group(struct packet_type *ptype, struct sock * sk) +{ + if (ptype->af_packet_priv == (void*)((struct packet_sock *)sk)->fanout) + return true; + + return false; +} + static int fanout_add(struct sock *sk, u16 id, u16 type_flags) { struct packet_sock *po = pkt_sk(sk); @@ -1325,6 +1333,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) match->prot_hook.dev = po->prot_hook.dev; match->prot_hook.func = packet_rcv_fanout; match->prot_hook.af_packet_priv = match; + match->prot_hook.id_match = match_fanout_group; dev_add_pack(&match->prot_hook); list_add(&match->list, &fanout_list); } -- cgit v1.2.3 From 9d7b0fc1ef1f17aff57c0dc9a59453d8fca255c3 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 20 Aug 2012 02:56:56 -0700 Subject: net: ipv6: fix oops in inet_putpeer() Commit 97bab73f (inet: Hide route peer accesses behind helpers.) introduced a bug in xfrm6_policy_destroy(). The xfrm_dst's _rt6i_peer member is not initialized, causing a false positive result from inetpeer_ptr_is_peer(), which in turn causes a NULL pointer dereference in inet_putpeer(). Pid: 314, comm: kworker/0:1 Not tainted 3.6.0-rc1+ #17 To Be Filled By O.E.M. To Be Filled By O.E.M./P4S800D-X EIP: 0060:[] EFLAGS: 00010246 CPU: 0 EIP is at inet_putpeer+0xe/0x16 EAX: 00000000 EBX: f3481700 ECX: 00000000 EDX: 000dd641 ESI: f3481700 EDI: c05e949c EBP: f551def4 ESP: f551def4 DS: 007b ES: 007b FS: 0000 GS: 00e0 SS: 0068 CR0: 8005003b CR2: 00000070 CR3: 3243d000 CR4: 00000750 DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000 DR6: ffff0ff0 DR7: 00000400 f551df04 c0423de1 00000000 f3481700 f551df18 c038d5f7 f254b9f8 f551df28 f34f85d8 f551df20 c03ef48d f551df3c c0396870 f30697e8 f24e1738 c05e98f4 f5509540 c05cd2b4 f551df7c c0142d2b c043feb5 f5509540 00000000 c05cd2e8 [] xfrm6_dst_destroy+0x42/0xdb [] dst_destroy+0x1d/0xa4 [] xfrm_bundle_flo_delete+0x2b/0x36 [] flow_cache_gc_task+0x85/0x9f [] process_one_work+0x122/0x441 [] ? apic_timer_interrupt+0x31/0x38 [] ? flow_cache_new_hashrnd+0x2b/0x2b [] worker_thread+0x113/0x3cc Fix by adding a init_dst() callback to struct xfrm_policy_afinfo to properly initialize the dst's peer pointer. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/net/xfrm.h | 2 ++ net/ipv6/xfrm6_policy.c | 8 ++++++++ net/xfrm/xfrm_policy.c | 2 ++ 3 files changed, 12 insertions(+) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 62b619e82a90..976a81abe1a2 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -292,6 +292,8 @@ struct xfrm_policy_afinfo { struct flowi *fl, int reverse); int (*get_tos)(const struct flowi *fl); + void (*init_dst)(struct net *net, + struct xfrm_dst *dst); int (*init_path)(struct xfrm_dst *path, struct dst_entry *dst, int nfheader_len); diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index ef39812107b1..f8c4c08ffb60 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -73,6 +73,13 @@ static int xfrm6_get_tos(const struct flowi *fl) return 0; } +static void xfrm6_init_dst(struct net *net, struct xfrm_dst *xdst) +{ + struct rt6_info *rt = (struct rt6_info *)xdst; + + rt6_init_peer(rt, net->ipv6.peers); +} + static int xfrm6_init_path(struct xfrm_dst *path, struct dst_entry *dst, int nfheader_len) { @@ -286,6 +293,7 @@ static struct xfrm_policy_afinfo xfrm6_policy_afinfo = { .get_saddr = xfrm6_get_saddr, .decode_session = _decode_session6, .get_tos = xfrm6_get_tos, + .init_dst = xfrm6_init_dst, .init_path = xfrm6_init_path, .fill_dst = xfrm6_fill_dst, .blackhole_route = ip6_blackhole_route, diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index c5a5165a5927..5a2aa17e4d3c 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1357,6 +1357,8 @@ static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family) memset(dst + 1, 0, sizeof(*xdst) - sizeof(*dst)); xdst->flo.ops = &xfrm_bundle_fc_ops; + if (afinfo->init_dst) + afinfo->init_dst(net, xdst); } else xdst = ERR_PTR(-ENOBUFS); -- cgit v1.2.3 From af74115eed22698f771fec1287a864975c9a6671 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Wed, 15 Aug 2012 21:24:52 -0700 Subject: target: Remove unused se_cmd.cmd_spdtl This was originally for helping fabrics to determine overflow/underflow status, and has been superceeded by SCF_OVERFLOW_BIT + SCF_UNDERFLOW_BIT. Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 2 -- include/target/target_core_base.h | 2 -- 2 files changed, 4 deletions(-) (limited to 'include') diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index ea9a3d2e4f55..4de3186dc44e 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1165,8 +1165,6 @@ int target_cmd_size_check(struct se_cmd *cmd, unsigned int size) " 0x%02x\n", cmd->se_tfo->get_fabric_name(), cmd->data_length, size, cmd->t_task_cdb[0]); - cmd->cmd_spdtl = size; - if (cmd->data_direction == DMA_TO_DEVICE) { pr_err("Rejecting underflow/overflow" " WRITE data\n"); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 128ce46fa48a..015cea01ae39 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -503,8 +503,6 @@ struct se_cmd { u32 se_ordered_id; /* Total size in bytes associated with command */ u32 data_length; - /* SCSI Presented Data Transfer Length */ - u32 cmd_spdtl; u32 residual_count; u32 orig_fe_lun; /* Persistent Reservation key */ -- cgit v1.2.3 From e0e3cea46d31d23dc40df0a49a7a2c04fe8edfea Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 21 Aug 2012 06:21:17 +0000 Subject: af_netlink: force credentials passing [CVE-2012-3520] Pablo Neira Ayuso discovered that avahi and potentially NetworkManager accept spoofed Netlink messages because of a kernel bug. The kernel passes all-zero SCM_CREDENTIALS ancillary data to the receiver if the sender did not provide such data, instead of not including any such data at all or including the correct data from the peer (as it is the case with AF_UNIX). This bug was introduced in commit 16e572626961 (af_unix: dont send SCM_CREDENTIALS by default) This patch forces passing credentials for netlink, as before the regression. Another fix would be to not add SCM_CREDENTIALS in netlink messages if not provided by the sender, but it might break some programs. With help from Florian Weimer & Petr Matousek This issue is designated as CVE-2012-3520 Signed-off-by: Eric Dumazet Cc: Petr Matousek Cc: Florian Weimer Cc: Pablo Neira Ayuso Signed-off-by: David S. Miller --- include/net/scm.h | 4 +++- net/netlink/af_netlink.c | 2 +- net/unix/af_unix.c | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/scm.h b/include/net/scm.h index 079d7887dac1..7dc0854f0b38 100644 --- a/include/net/scm.h +++ b/include/net/scm.h @@ -70,9 +70,11 @@ static __inline__ void scm_destroy(struct scm_cookie *scm) } static __inline__ int scm_send(struct socket *sock, struct msghdr *msg, - struct scm_cookie *scm) + struct scm_cookie *scm, bool forcecreds) { memset(scm, 0, sizeof(*scm)); + if (forcecreds) + scm_set_cred(scm, task_tgid(current), current_cred()); unix_get_peersec_dgram(sock, scm); if (msg->msg_controllen <= 0) return 0; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 5463969da45b..1445d73533ed 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1362,7 +1362,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, if (NULL == siocb->scm) siocb->scm = &scm; - err = scm_send(sock, msg, siocb->scm); + err = scm_send(sock, msg, siocb->scm, true); if (err < 0) return err; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index e4768c180da2..c5ee4ff61364 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1450,7 +1450,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, if (NULL == siocb->scm) siocb->scm = &tmp_scm; wait_for_unix_gc(); - err = scm_send(sock, msg, siocb->scm); + err = scm_send(sock, msg, siocb->scm, false); if (err < 0) return err; @@ -1619,7 +1619,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, if (NULL == siocb->scm) siocb->scm = &tmp_scm; wait_for_unix_gc(); - err = scm_send(sock, msg, siocb->scm); + err = scm_send(sock, msg, siocb->scm, false); if (err < 0) return err; -- cgit v1.2.3 From 04ccfe77f132b973659f11954443214659014072 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Fri, 17 Aug 2012 14:20:02 +0000 Subject: drm: Remove two unused fields from struct drm_display_mode Signed-off-by: Damien Lespiau Reviewed-by: Jani Nikula Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_modes.c | 3 --- include/drm/drm_crtc.h | 2 -- 2 files changed, 5 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index b7adb4a967fd..28637c181b15 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -706,9 +706,6 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal); p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay); p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal); - - p->crtc_hadjusted = false; - p->crtc_vadjusted = false; } EXPORT_SYMBOL(drm_mode_set_crtcinfo); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index a1a0386e0160..ced362533e3c 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -166,8 +166,6 @@ struct drm_display_mode { int crtc_vsync_start; int crtc_vsync_end; int crtc_vtotal; - int crtc_hadjusted; - int crtc_vadjusted; /* Driver private mode info */ int private_size; -- cgit v1.2.3 From c3a5ce0416b6c172a23bc8a3760d8704d3d1535b Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Tue, 21 Aug 2012 16:16:00 -0700 Subject: string: do not export memweight() to userspace Fix the following warning: usr/include/linux/string.h:8: userspace cannot reference function or variable defined in the kernel Signed-off-by: WANG Cong Acked-by: Akinobu Mita Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/string.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/string.h b/include/linux/string.h index ffe0442e18d2..b9178812d9df 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -144,8 +144,8 @@ static inline bool strstarts(const char *str, const char *prefix) { return strncmp(str, prefix, strlen(prefix)) == 0; } -#endif extern size_t memweight(const void *ptr, size_t bytes); +#endif /* __KERNEL__ */ #endif /* _LINUX_STRING_H_ */ -- cgit v1.2.3 From c67fe3752abe6ab47639e2f9b836900c3dc3da84 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 21 Aug 2012 16:16:17 -0700 Subject: mm: compaction: Abort async compaction if locks are contended or taking too long Jim Schutt reported a problem that pointed at compaction contending heavily on locks. The workload is straight-forward and in his own words; The systems in question have 24 SAS drives spread across 3 HBAs, running 24 Ceph OSD instances, one per drive. FWIW these servers are dual-socket Intel 5675 Xeons w/48 GB memory. I've got ~160 Ceph Linux clients doing dd simultaneously to a Ceph file system backed by 12 of these servers. Early in the test everything looks fine procs -------------------memory------------------ ---swap-- -----io---- --system-- -----cpu------- r b swpd free buff cache si so bi bo in cs us sy id wa st 31 15 0 287216 576 38606628 0 0 2 1158 2 14 1 3 95 0 0 27 15 0 225288 576 38583384 0 0 18 2222016 203357 134876 11 56 17 15 0 28 17 0 219256 576 38544736 0 0 11 2305932 203141 146296 11 49 23 17 0 6 18 0 215596 576 38552872 0 0 7 2363207 215264 166502 12 45 22 20 0 22 18 0 226984 576 38596404 0 0 3 2445741 223114 179527 12 43 23 22 0 and then it goes to pot procs -------------------memory------------------ ---swap-- -----io---- --system-- -----cpu------- r b swpd free buff cache si so bi bo in cs us sy id wa st 163 8 0 464308 576 36791368 0 0 11 22210 866 536 3 13 79 4 0 207 14 0 917752 576 36181928 0 0 712 1345376 134598 47367 7 90 1 2 0 123 12 0 685516 576 36296148 0 0 429 1386615 158494 60077 8 84 5 3 0 123 12 0 598572 576 36333728 0 0 1107 1233281 147542 62351 7 84 5 4 0 622 7 0 660768 576 36118264 0 0 557 1345548 151394 59353 7 85 4 3 0 223 11 0 283960 576 36463868 0 0 46 1107160 121846 33006 6 93 1 1 0 Note that system CPU usage is very high blocks being written out has dropped by 42%. He analysed this with perf and found perf record -g -a sleep 10 perf report --sort symbol --call-graph fractal,5 34.63% [k] _raw_spin_lock_irqsave | |--97.30%-- isolate_freepages | compaction_alloc | unmap_and_move | migrate_pages | compact_zone | compact_zone_order | try_to_compact_pages | __alloc_pages_direct_compact | __alloc_pages_slowpath | __alloc_pages_nodemask | alloc_pages_vma | do_huge_pmd_anonymous_page | handle_mm_fault | do_page_fault | page_fault | | | |--87.39%-- skb_copy_datagram_iovec | | tcp_recvmsg | | inet_recvmsg | | sock_recvmsg | | sys_recvfrom | | system_call | | __recv | | | | | --100.00%-- (nil) | | | --12.61%-- memcpy --2.70%-- [...] There was other data but primarily it is all showing that compaction is contended heavily on the zone->lock and zone->lru_lock. commit [b2eef8c0: mm: compaction: minimise the time IRQs are disabled while isolating pages for migration] noted that it was possible for migration to hold the lru_lock for an excessive amount of time. Very broadly speaking this patch expands the concept. This patch introduces compact_checklock_irqsave() to check if a lock is contended or the process needs to be scheduled. If either condition is true then async compaction is aborted and the caller is informed. The page allocator will fail a THP allocation if compaction failed due to contention. This patch also introduces compact_trylock_irqsave() which will acquire the lock only if it is not contended and the process does not need to schedule. Reported-by: Jim Schutt Tested-by: Jim Schutt Signed-off-by: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/compaction.h | 4 +- mm/compaction.c | 100 +++++++++++++++++++++++++++++++++++---------- mm/internal.h | 1 + mm/page_alloc.c | 17 +++++--- 4 files changed, 93 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/linux/compaction.h b/include/linux/compaction.h index 133ddcf83397..ef658147e4e8 100644 --- a/include/linux/compaction.h +++ b/include/linux/compaction.h @@ -22,7 +22,7 @@ extern int sysctl_extfrag_handler(struct ctl_table *table, int write, extern int fragmentation_index(struct zone *zone, unsigned int order); extern unsigned long try_to_compact_pages(struct zonelist *zonelist, int order, gfp_t gfp_mask, nodemask_t *mask, - bool sync); + bool sync, bool *contended); extern int compact_pgdat(pg_data_t *pgdat, int order); extern unsigned long compaction_suitable(struct zone *zone, int order); @@ -64,7 +64,7 @@ static inline bool compaction_deferred(struct zone *zone, int order) #else static inline unsigned long try_to_compact_pages(struct zonelist *zonelist, int order, gfp_t gfp_mask, nodemask_t *nodemask, - bool sync) + bool sync, bool *contended) { return COMPACT_CONTINUE; } diff --git a/mm/compaction.c b/mm/compaction.c index bcce7897e17a..7fcd3a52e68d 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -50,6 +50,47 @@ static inline bool migrate_async_suitable(int migratetype) return is_migrate_cma(migratetype) || migratetype == MIGRATE_MOVABLE; } +/* + * Compaction requires the taking of some coarse locks that are potentially + * very heavily contended. Check if the process needs to be scheduled or + * if the lock is contended. For async compaction, back out in the event + * if contention is severe. For sync compaction, schedule. + * + * Returns true if the lock is held. + * Returns false if the lock is released and compaction should abort + */ +static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags, + bool locked, struct compact_control *cc) +{ + if (need_resched() || spin_is_contended(lock)) { + if (locked) { + spin_unlock_irqrestore(lock, *flags); + locked = false; + } + + /* async aborts if taking too long or contended */ + if (!cc->sync) { + if (cc->contended) + *cc->contended = true; + return false; + } + + cond_resched(); + if (fatal_signal_pending(current)) + return false; + } + + if (!locked) + spin_lock_irqsave(lock, *flags); + return true; +} + +static inline bool compact_trylock_irqsave(spinlock_t *lock, + unsigned long *flags, struct compact_control *cc) +{ + return compact_checklock_irqsave(lock, flags, false, cc); +} + /* * Isolate free pages onto a private freelist. Caller must hold zone->lock. * If @strict is true, will abort returning 0 on any invalid PFNs or non-free @@ -173,7 +214,7 @@ isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn) } /* Update the number of anon and file isolated pages in the zone */ -static void acct_isolated(struct zone *zone, struct compact_control *cc) +static void acct_isolated(struct zone *zone, bool locked, struct compact_control *cc) { struct page *page; unsigned int count[2] = { 0, }; @@ -181,8 +222,14 @@ static void acct_isolated(struct zone *zone, struct compact_control *cc) list_for_each_entry(page, &cc->migratepages, lru) count[!!page_is_file_cache(page)]++; - __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]); - __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]); + /* If locked we can use the interrupt unsafe versions */ + if (locked) { + __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]); + __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]); + } else { + mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]); + mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]); + } } /* Similar to reclaim, but different enough that they don't share logic */ @@ -228,6 +275,8 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc, struct list_head *migratelist = &cc->migratepages; isolate_mode_t mode = 0; struct lruvec *lruvec; + unsigned long flags; + bool locked; /* * Ensure that there are not too many pages isolated from the LRU @@ -247,25 +296,22 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc, /* Time to isolate some pages for migration */ cond_resched(); - spin_lock_irq(&zone->lru_lock); + spin_lock_irqsave(&zone->lru_lock, flags); + locked = true; for (; low_pfn < end_pfn; low_pfn++) { struct page *page; - bool locked = true; /* give a chance to irqs before checking need_resched() */ if (!((low_pfn+1) % SWAP_CLUSTER_MAX)) { - spin_unlock_irq(&zone->lru_lock); + spin_unlock_irqrestore(&zone->lru_lock, flags); locked = false; } - if (need_resched() || spin_is_contended(&zone->lru_lock)) { - if (locked) - spin_unlock_irq(&zone->lru_lock); - cond_resched(); - spin_lock_irq(&zone->lru_lock); - if (fatal_signal_pending(current)) - break; - } else if (!locked) - spin_lock_irq(&zone->lru_lock); + + /* Check if it is ok to still hold the lock */ + locked = compact_checklock_irqsave(&zone->lru_lock, &flags, + locked, cc); + if (!locked) + break; /* * migrate_pfn does not necessarily start aligned to a @@ -349,9 +395,10 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc, } } - acct_isolated(zone, cc); + acct_isolated(zone, locked, cc); - spin_unlock_irq(&zone->lru_lock); + if (locked) + spin_unlock_irqrestore(&zone->lru_lock, flags); trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated); @@ -461,7 +508,16 @@ static void isolate_freepages(struct zone *zone, * are disabled */ isolated = 0; - spin_lock_irqsave(&zone->lock, flags); + + /* + * The zone lock must be held to isolate freepages. This + * unfortunately this is a very coarse lock and can be + * heavily contended if there are parallel allocations + * or parallel compactions. For async compaction do not + * spin on the lock + */ + if (!compact_trylock_irqsave(&zone->lock, &flags, cc)) + break; if (suitable_migration_target(page)) { end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn); isolated = isolate_freepages_block(pfn, end_pfn, @@ -773,7 +829,7 @@ out: static unsigned long compact_zone_order(struct zone *zone, int order, gfp_t gfp_mask, - bool sync) + bool sync, bool *contended) { struct compact_control cc = { .nr_freepages = 0, @@ -782,6 +838,7 @@ static unsigned long compact_zone_order(struct zone *zone, .migratetype = allocflags_to_migratetype(gfp_mask), .zone = zone, .sync = sync, + .contended = contended, }; INIT_LIST_HEAD(&cc.freepages); INIT_LIST_HEAD(&cc.migratepages); @@ -803,7 +860,7 @@ int sysctl_extfrag_threshold = 500; */ unsigned long try_to_compact_pages(struct zonelist *zonelist, int order, gfp_t gfp_mask, nodemask_t *nodemask, - bool sync) + bool sync, bool *contended) { enum zone_type high_zoneidx = gfp_zone(gfp_mask); int may_enter_fs = gfp_mask & __GFP_FS; @@ -827,7 +884,8 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist, nodemask) { int status; - status = compact_zone_order(zone, order, gfp_mask, sync); + status = compact_zone_order(zone, order, gfp_mask, sync, + contended); rc = max(status, rc); /* If a normal allocation would succeed, stop compacting */ diff --git a/mm/internal.h b/mm/internal.h index 3314f79d775a..b8c91b342e24 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -130,6 +130,7 @@ struct compact_control { int order; /* order a direct compactor needs */ int migratetype; /* MOVABLE, RECLAIMABLE etc */ struct zone *zone; + bool *contended; /* True if a lock was contended */ }; unsigned long diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 07f19248acb5..c66fb875104a 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2102,7 +2102,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, struct zonelist *zonelist, enum zone_type high_zoneidx, nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone, int migratetype, bool sync_migration, - bool *deferred_compaction, + bool *contended_compaction, bool *deferred_compaction, unsigned long *did_some_progress) { struct page *page; @@ -2117,7 +2117,8 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, current->flags |= PF_MEMALLOC; *did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask, - nodemask, sync_migration); + nodemask, sync_migration, + contended_compaction); current->flags &= ~PF_MEMALLOC; if (*did_some_progress != COMPACT_SKIPPED) { @@ -2163,7 +2164,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order, struct zonelist *zonelist, enum zone_type high_zoneidx, nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone, int migratetype, bool sync_migration, - bool *deferred_compaction, + bool *contended_compaction, bool *deferred_compaction, unsigned long *did_some_progress) { return NULL; @@ -2336,6 +2337,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, unsigned long did_some_progress; bool sync_migration = false; bool deferred_compaction = false; + bool contended_compaction = false; /* * In the slowpath, we sanity check order to avoid ever trying to @@ -2425,6 +2427,7 @@ rebalance: nodemask, alloc_flags, preferred_zone, migratetype, sync_migration, + &contended_compaction, &deferred_compaction, &did_some_progress); if (page) @@ -2434,10 +2437,11 @@ rebalance: /* * If compaction is deferred for high-order allocations, it is because * sync compaction recently failed. In this is the case and the caller - * has requested the system not be heavily disrupted, fail the - * allocation now instead of entering direct reclaim + * requested a movable allocation that does not heavily disrupt the + * system then fail the allocation instead of entering direct reclaim. */ - if (deferred_compaction && (gfp_mask & __GFP_NO_KSWAPD)) + if ((deferred_compaction || contended_compaction) && + (gfp_mask & __GFP_NO_KSWAPD)) goto nopage; /* Try direct reclaim and then allocating */ @@ -2508,6 +2512,7 @@ rebalance: nodemask, alloc_flags, preferred_zone, migratetype, sync_migration, + &contended_compaction, &deferred_compaction, &did_some_progress); if (page) -- cgit v1.2.3 From 8ad5db8a8ddbe3bd33078863a027011e28f1f4ee Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 17 Aug 2012 20:10:46 -0400 Subject: introduce kref_put_mutex() equivalent of mutex_lock(mutex); if (!kref_put(kref, release)) mutex_unlock(mutex); Signed-off-by: Al Viro --- include/linux/kref.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'include') diff --git a/include/linux/kref.h b/include/linux/kref.h index 9c07dcebded7..65af6887872f 100644 --- a/include/linux/kref.h +++ b/include/linux/kref.h @@ -18,6 +18,7 @@ #include #include #include +#include struct kref { atomic_t refcount; @@ -93,4 +94,21 @@ static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref) { return kref_sub(kref, 1, release); } + +static inline int kref_put_mutex(struct kref *kref, + void (*release)(struct kref *kref), + struct mutex *lock) +{ + WARN_ON(release == NULL); + if (unlikely(!atomic_add_unless(&kref->refcount, -1, 1))) { + mutex_lock(lock); + if (unlikely(!atomic_dec_and_test(&kref->refcount))) { + mutex_unlock(lock); + return 0; + } + release(kref); + return 1; + } + return 0; +} #endif /* _KREF_H_ */ -- cgit v1.2.3 From c7a9b09b1a4a1fbccb2ec409daec95f9068d77c0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 15 Aug 2012 20:51:54 +0000 Subject: ARM: omap: allow building omap44xx without SMP The new omap4 cpuidle implementation currently requires ARCH_NEEDS_CPU_IDLE_COUPLED, which only works on SMP. This patch makes it possible to build a non-SMP kernel for that platform. This is not normally desired for end-users but can be useful for testing. Without this patch, building rand-0y2jSKT results in: drivers/cpuidle/coupled.c: In function 'cpuidle_coupled_poke': drivers/cpuidle/coupled.c:317:3: error: implicit declaration of function '__smp_call_function_single' [-Werror=implicit-function-declaration] It's not clear if this patch is the best solution for the problem at hand. I have made sure that we can now build the kernel in all configurations, but that does not mean it will actually work on an OMAP44xx. Signed-off-by: Arnd Bergmann Acked-by: Santosh Shilimkar Tested-by: Santosh Shilimkar Cc: Kevin Hilman Cc: Tony Lindgren --- arch/arm/mach-omap2/Kconfig | 2 +- arch/arm/mach-omap2/cpuidle44xx.c | 3 ++- include/linux/cpuidle.h | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index dd2db025f778..66a8be331caf 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -62,7 +62,7 @@ config ARCH_OMAP4 select PM_OPP if PM select USB_ARCH_HAS_EHCI if USB_SUPPORT select ARM_CPU_SUSPEND if PM - select ARCH_NEEDS_CPU_IDLE_COUPLED + select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP config SOC_OMAP5 bool "TI OMAP5" diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle44xx.c index ee05e193fc61..288bee6cbb76 100644 --- a/arch/arm/mach-omap2/cpuidle44xx.c +++ b/arch/arm/mach-omap2/cpuidle44xx.c @@ -238,8 +238,9 @@ int __init omap4_idle_init(void) for_each_cpu(cpu_id, cpu_online_mask) { dev = &per_cpu(omap4_idle_dev, cpu_id); dev->cpu = cpu_id; +#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED dev->coupled_cpus = *cpu_online_mask; - +#endif cpuidle_register_driver(&omap4_idle_driver); if (cpuidle_register_device(dev)) { diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 040b13b5c14a..279b1eaa8b73 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -194,6 +194,10 @@ static inline int cpuidle_play_dead(void) {return -ENODEV; } #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a); +#else +static inline void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a) +{ +} #endif /****************************** -- cgit v1.2.3 From 22f5d115a2b087c977128f84ee557ad71530330e Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 14 Aug 2012 10:53:38 +0000 Subject: drm: Initialize object type when using DRM_MODE() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DRM_MODE() macro doesn't initialize the type of the base drm object. When a copy is made of the mode, the object type is overwritten with zero, and the the object can no longer be found by drm_mode_object_find() due to the type check failing. Signed-off-by: Ville Syrjälä Signed-off-by: Dave Airlie --- include/drm/drm_crtc.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index ced362533e3c..bfacf0d5a225 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -118,7 +118,8 @@ enum drm_mode_status { .hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), \ .htotal = (ht), .hskew = (hsk), .vdisplay = (vd), \ .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \ - .vscan = (vs), .flags = (f), .vrefresh = 0 + .vscan = (vs), .flags = (f), .vrefresh = 0, \ + .base.type = DRM_MODE_OBJECT_MODE #define CRTC_INTERLACE_HALVE_V 0x1 /* halve V values for interlacing */ -- cgit v1.2.3 From 7c4eaca4162d0b5ad4fb39f974d7ffd71b9daa09 Mon Sep 17 00:00:00 2001 From: Jakob Bornecrantz Date: Thu, 16 Aug 2012 08:29:03 +0000 Subject: drm: Check for invalid cursor flags Signed-off-by: Jakob Bornecrantz Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 2 +- include/drm/drm_mode.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 08a7aa722d6b..6fbfc244748f 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1981,7 +1981,7 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - if (!req->flags) + if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) return -EINVAL; mutex_lock(&dev->mode_config.mutex); diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h index 5581980b14f6..3d6301b6ec16 100644 --- a/include/drm/drm_mode.h +++ b/include/drm/drm_mode.h @@ -359,8 +359,9 @@ struct drm_mode_mode_cmd { struct drm_mode_modeinfo mode; }; -#define DRM_MODE_CURSOR_BO (1<<0) -#define DRM_MODE_CURSOR_MOVE (1<<1) +#define DRM_MODE_CURSOR_BO 0x01 +#define DRM_MODE_CURSOR_MOVE 0x02 +#define DRM_MODE_CURSOR_FLAGS 0x03 /* * depending on the value in flags different members are used. -- cgit v1.2.3 From 5c879d2094946081af934739850c7260e8b25d3c Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Sun, 26 Aug 2012 00:35:45 +0000 Subject: bnx2x: fix 57840_MF pci id Commit c3def943c7117d42caaed3478731ea7c3c87190e have added support for new pci ids of the 57840 board, while failing to change the obsolete value in 'pci_ids.h'. This patch does so, allowing the probe of such devices. Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- include/linux/pci_ids.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index fc3526077348..6b4565c440c8 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2149,7 +2149,7 @@ #define PCI_DEVICE_ID_TIGON3_5704S 0x16a8 #define PCI_DEVICE_ID_NX2_57800_VF 0x16a9 #define PCI_DEVICE_ID_NX2_5706S 0x16aa -#define PCI_DEVICE_ID_NX2_57840_MF 0x16ab +#define PCI_DEVICE_ID_NX2_57840_MF 0x16a4 #define PCI_DEVICE_ID_NX2_5708S 0x16ac #define PCI_DEVICE_ID_NX2_57840_VF 0x16ad #define PCI_DEVICE_ID_NX2_57810_MF 0x16ae -- cgit v1.2.3 From 5b423f6a40a0327f9d40bc8b97ce9be266f74368 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 29 Aug 2012 16:25:49 +0000 Subject: netfilter: nf_conntrack: fix racy timer handling with reliable events Existing code assumes that del_timer returns true for alive conntrack entries. However, this is not true if reliable events are enabled. In that case, del_timer may return true for entries that were just inserted in the dying list. Note that packets / ctnetlink may hold references to conntrack entries that were just inserted to such list. This patch fixes the issue by adding an independent timer for event delivery. This increases the size of the ecache extension. Still we can revisit this later and use variable size extensions to allocate this area on demand. Tested-by: Oliver Smith Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_ecache.h | 1 + net/netfilter/nf_conntrack_core.c | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h index e1ce1048fe5f..4a045cda9c60 100644 --- a/include/net/netfilter/nf_conntrack_ecache.h +++ b/include/net/netfilter/nf_conntrack_ecache.h @@ -18,6 +18,7 @@ struct nf_conntrack_ecache { u16 ctmask; /* bitmask of ct events to be delivered */ u16 expmask; /* bitmask of expect events to be delivered */ u32 pid; /* netlink pid of destroyer */ + struct timer_list timeout; }; static inline struct nf_conntrack_ecache * diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index cf4875565d67..2ceec64b19f9 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -249,12 +249,15 @@ static void death_by_event(unsigned long ul_conntrack) { struct nf_conn *ct = (void *)ul_conntrack; struct net *net = nf_ct_net(ct); + struct nf_conntrack_ecache *ecache = nf_ct_ecache_find(ct); + + BUG_ON(ecache == NULL); if (nf_conntrack_event(IPCT_DESTROY, ct) < 0) { /* bad luck, let's retry again */ - ct->timeout.expires = jiffies + + ecache->timeout.expires = jiffies + (random32() % net->ct.sysctl_events_retry_timeout); - add_timer(&ct->timeout); + add_timer(&ecache->timeout); return; } /* we've got the event delivered, now it's dying */ @@ -268,6 +271,9 @@ static void death_by_event(unsigned long ul_conntrack) void nf_ct_insert_dying_list(struct nf_conn *ct) { struct net *net = nf_ct_net(ct); + struct nf_conntrack_ecache *ecache = nf_ct_ecache_find(ct); + + BUG_ON(ecache == NULL); /* add this conntrack to the dying list */ spin_lock_bh(&nf_conntrack_lock); @@ -275,10 +281,10 @@ void nf_ct_insert_dying_list(struct nf_conn *ct) &net->ct.dying); spin_unlock_bh(&nf_conntrack_lock); /* set a new timer to retry event delivery */ - setup_timer(&ct->timeout, death_by_event, (unsigned long)ct); - ct->timeout.expires = jiffies + + setup_timer(&ecache->timeout, death_by_event, (unsigned long)ct); + ecache->timeout.expires = jiffies + (random32() % net->ct.sysctl_events_retry_timeout); - add_timer(&ct->timeout); + add_timer(&ecache->timeout); } EXPORT_SYMBOL_GPL(nf_ct_insert_dying_list); -- cgit v1.2.3 From cee58483cf56e0ba355fdd97ff5e8925329aa936 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 31 Aug 2012 13:30:06 -0400 Subject: time: Move ktime_t overflow checking into timespec_valid_strict Andreas Bombe reported that the added ktime_t overflow checking added to timespec_valid in commit 4e8b14526ca7 ("time: Improve sanity checking of timekeeping inputs") was causing problems with X.org because it caused timeouts larger then KTIME_T to be invalid. Previously, these large timeouts would be clamped to KTIME_MAX and would never expire, which is valid. This patch splits the ktime_t overflow checking into a new timespec_valid_strict function, and converts the timekeeping codes internal checking to use this more strict function. Reported-and-tested-by: Andreas Bombe Cc: Zhouping Liu Cc: Ingo Molnar Cc: Prarit Bhargava Cc: Thomas Gleixner Cc: stable@vger.kernel.org Signed-off-by: John Stultz Signed-off-by: Linus Torvalds --- include/linux/time.h | 7 +++++++ kernel/time/timekeeping.c | 10 +++++----- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/time.h b/include/linux/time.h index b0bbd8f0130d..b51e664c83e7 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -125,6 +125,13 @@ static inline bool timespec_valid(const struct timespec *ts) /* Can't have more nanoseconds then a second */ if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) return false; + return true; +} + +static inline bool timespec_valid_strict(const struct timespec *ts) +{ + if (!timespec_valid(ts)) + return false; /* Disallow values that could overflow ktime_t */ if ((unsigned long long)ts->tv_sec >= KTIME_SEC_MAX) return false; diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 0c1485e42be6..34e5eac81424 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -428,7 +428,7 @@ int do_settimeofday(const struct timespec *tv) struct timespec ts_delta, xt; unsigned long flags; - if (!timespec_valid(tv)) + if (!timespec_valid_strict(tv)) return -EINVAL; write_seqlock_irqsave(&tk->lock, flags); @@ -476,7 +476,7 @@ int timekeeping_inject_offset(struct timespec *ts) /* Make sure the proposed value is valid */ tmp = timespec_add(tk_xtime(tk), *ts); - if (!timespec_valid(&tmp)) { + if (!timespec_valid_strict(&tmp)) { ret = -EINVAL; goto error; } @@ -659,7 +659,7 @@ void __init timekeeping_init(void) struct timespec now, boot, tmp; read_persistent_clock(&now); - if (!timespec_valid(&now)) { + if (!timespec_valid_strict(&now)) { pr_warn("WARNING: Persistent clock returned invalid value!\n" " Check your CMOS/BIOS settings.\n"); now.tv_sec = 0; @@ -667,7 +667,7 @@ void __init timekeeping_init(void) } read_boot_clock(&boot); - if (!timespec_valid(&boot)) { + if (!timespec_valid_strict(&boot)) { pr_warn("WARNING: Boot clock returned invalid value!\n" " Check your CMOS/BIOS settings.\n"); boot.tv_sec = 0; @@ -713,7 +713,7 @@ static struct timespec timekeeping_suspend_time; static void __timekeeping_inject_sleeptime(struct timekeeper *tk, struct timespec *delta) { - if (!timespec_valid(delta)) { + if (!timespec_valid_strict(delta)) { printk(KERN_WARNING "__timekeeping_inject_sleeptime: Invalid " "sleep delta value!\n"); return; -- cgit v1.2.3 From b6d86d3d6d6e4c9b588d81615c81b5a8292b62ed Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 24 Aug 2012 17:25:01 -0700 Subject: linux/kernel.h: Fix DIV_ROUND_CLOSEST to support negative dividends DIV_ROUND_CLOSEST returns a bad result for negative dividends: DIV_ROUND_CLOSEST(-2, 2) = 0 Most of the time this does not matter. However, in the hardware monitoring subsystem, DIV_ROUND_CLOSEST is sometimes used on integers which can be negative (such as temperatures). Signed-off-by: Guenter Roeck Acked-by: Jean Delvare --- include/linux/kernel.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 604382143bcf..594b419b7d20 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -82,10 +82,18 @@ __x - (__x % (y)); \ } \ ) + +/* + * Divide positive or negative dividend by positive divisor and round + * to closest integer. Result is undefined for negative divisors. + */ #define DIV_ROUND_CLOSEST(x, divisor)( \ { \ - typeof(divisor) __divisor = divisor; \ - (((x) + ((__divisor) / 2)) / (__divisor)); \ + typeof(x) __x = x; \ + typeof(divisor) __d = divisor; \ + (((typeof(x))-1) >= 0 || (__x) >= 0) ? \ + (((__x) + ((__d) / 2)) / (__d)) : \ + (((__x) - ((__d) / 2)) / (__d)); \ } \ ) -- cgit v1.2.3 From 3550ccdb9d8d350e526b809bf3dd92b550a74fe1 Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Wed, 29 Aug 2012 15:05:36 +0900 Subject: mmc: card: Skip secure erase on MoviNAND; causes unrecoverable corruption. For several MoviNAND eMMC parts, there are known issues with secure erase and secure trim. For these specific MoviNAND devices, we skip these operations. Specifically, there is a bug in the eMMC firmware that causes unrecoverable corruption when the MMC is erased with MMC_CAP_ERASE enabled. References: http://forum.xda-developers.com/showthread.php?t=1644364 https://plus.google.com/111398485184813224730/posts/21pTYfTsCkB#111398485184813224730/posts/21pTYfTsCkB Signed-off-by: Ian Chen Reviewed-by: Namjae Jeon Acked-by: Jaehoon Chung Reviewed-by: Linus Walleij Cc: stable [3.0+] Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 26 +++++++++++++++++++++++++- include/linux/mmc/card.h | 1 + 2 files changed, 26 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index f1c84decb192..172a768036d8 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1411,7 +1411,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) /* complete ongoing async transfer before issuing discard */ if (card->host->areq) mmc_blk_issue_rw_rq(mq, NULL); - if (req->cmd_flags & REQ_SECURE) + if (req->cmd_flags & REQ_SECURE && + !(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN)) ret = mmc_blk_issue_secdiscard_rq(mq, req); else ret = mmc_blk_issue_discard_rq(mq, req); @@ -1716,6 +1717,7 @@ force_ro_fail: #define CID_MANFID_SANDISK 0x2 #define CID_MANFID_TOSHIBA 0x11 #define CID_MANFID_MICRON 0x13 +#define CID_MANFID_SAMSUNG 0x15 static const struct mmc_fixup blk_fixups[] = { @@ -1752,6 +1754,28 @@ static const struct mmc_fixup blk_fixups[] = MMC_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc, MMC_QUIRK_LONG_READ_TIME), + /* + * On these Samsung MoviNAND parts, performing secure erase or + * secure trim can result in unrecoverable corruption due to a + * firmware bug. + */ + MMC_FIXUP("M8G2FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("MAG4FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("MBG8FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("MCGAFA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("VAL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("VYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("KYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + END_FIXUP }; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 111aca5e97f3..4b27f9f503e4 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -239,6 +239,7 @@ struct mmc_card { #define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */ #define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8) /* Avoid sending 512 bytes in */ #define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */ +#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */ /* byte mode */ unsigned int poweroff_notify_state; /* eMMC4.5 notify feature */ #define MMC_NO_POWER_NOTIFICATION 0 -- cgit v1.2.3 From dae373be9fec6f850159a05af3a1c36236a70d43 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 13 Sep 2012 19:12:16 -0400 Subject: alarmtimer: Use hrtimer per-alarm instead of per-base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Arve Hjønnevåg reported numerous crashes from the "BUG_ON(timer->state != HRTIMER_STATE_CALLBACK)" check in __run_hrtimer after it called alarmtimer_fired. It ends up the alarmtimer code was not properly handling possible failures of hrtimer_try_to_cancel, and because these faulres occur when the underlying base hrtimer is being run, this limits the ability to properly handle modifications to any alarmtimers on that base. Because much of the logic duplicates the hrtimer logic, it seems that we might as well have a per-alarmtimer hrtimer, and avoid the extra complextity of trying to multiplex many alarmtimers off of one hrtimer. Thus this patch moves the hrtimer to the alarm structure and simplifies the management logic. Changelog: v2: * Includes a fix for double alarm_start calls found by Arve Cc: Arve Hjønnevåg Cc: Colin Cross Cc: Thomas Gleixner Reported-by: Arve Hjønnevåg Tested-by: Arve Hjønnevåg Signed-off-by: John Stultz --- include/linux/alarmtimer.h | 3 +- kernel/time/alarmtimer.c | 94 +++++++++++++--------------------------------- 2 files changed, 29 insertions(+), 68 deletions(-) (limited to 'include') diff --git a/include/linux/alarmtimer.h b/include/linux/alarmtimer.h index 96c5c249b086..f122c9fbf8c7 100644 --- a/include/linux/alarmtimer.h +++ b/include/linux/alarmtimer.h @@ -35,6 +35,7 @@ enum alarmtimer_restart { */ struct alarm { struct timerqueue_node node; + struct hrtimer timer; enum alarmtimer_restart (*function)(struct alarm *, ktime_t now); enum alarmtimer_type type; int state; @@ -43,7 +44,7 @@ struct alarm { void alarm_init(struct alarm *alarm, enum alarmtimer_type type, enum alarmtimer_restart (*function)(struct alarm *, ktime_t)); -void alarm_start(struct alarm *alarm, ktime_t start); +int alarm_start(struct alarm *alarm, ktime_t start); int alarm_try_to_cancel(struct alarm *alarm); int alarm_cancel(struct alarm *alarm); diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index 54e7145c5414..b1560ebe759f 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -37,7 +37,6 @@ static struct alarm_base { spinlock_t lock; struct timerqueue_head timerqueue; - struct hrtimer timer; ktime_t (*gettime)(void); clockid_t base_clockid; } alarm_bases[ALARM_NUMTYPE]; @@ -132,21 +131,17 @@ static inline void alarmtimer_rtc_timer_init(void) { } * @base: pointer to the base where the timer is being run * @alarm: pointer to alarm being enqueued. * - * Adds alarm to a alarm_base timerqueue and if necessary sets - * an hrtimer to run. + * Adds alarm to a alarm_base timerqueue * * Must hold base->lock when calling. */ static void alarmtimer_enqueue(struct alarm_base *base, struct alarm *alarm) { + if (alarm->state & ALARMTIMER_STATE_ENQUEUED) + timerqueue_del(&base->timerqueue, &alarm->node); + timerqueue_add(&base->timerqueue, &alarm->node); alarm->state |= ALARMTIMER_STATE_ENQUEUED; - - if (&alarm->node == timerqueue_getnext(&base->timerqueue)) { - hrtimer_try_to_cancel(&base->timer); - hrtimer_start(&base->timer, alarm->node.expires, - HRTIMER_MODE_ABS); - } } /** @@ -154,28 +149,17 @@ static void alarmtimer_enqueue(struct alarm_base *base, struct alarm *alarm) * @base: pointer to the base where the timer is running * @alarm: pointer to alarm being removed * - * Removes alarm to a alarm_base timerqueue and if necessary sets - * a new timer to run. + * Removes alarm to a alarm_base timerqueue * * Must hold base->lock when calling. */ static void alarmtimer_remove(struct alarm_base *base, struct alarm *alarm) { - struct timerqueue_node *next = timerqueue_getnext(&base->timerqueue); - if (!(alarm->state & ALARMTIMER_STATE_ENQUEUED)) return; timerqueue_del(&base->timerqueue, &alarm->node); alarm->state &= ~ALARMTIMER_STATE_ENQUEUED; - - if (next == &alarm->node) { - hrtimer_try_to_cancel(&base->timer); - next = timerqueue_getnext(&base->timerqueue); - if (!next) - return; - hrtimer_start(&base->timer, next->expires, HRTIMER_MODE_ABS); - } } @@ -190,42 +174,23 @@ static void alarmtimer_remove(struct alarm_base *base, struct alarm *alarm) */ static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer) { - struct alarm_base *base = container_of(timer, struct alarm_base, timer); - struct timerqueue_node *next; + struct alarm *alarm = container_of(timer, struct alarm, timer); + struct alarm_base *base = &alarm_bases[alarm->type]; unsigned long flags; - ktime_t now; int ret = HRTIMER_NORESTART; int restart = ALARMTIMER_NORESTART; spin_lock_irqsave(&base->lock, flags); - now = base->gettime(); - while ((next = timerqueue_getnext(&base->timerqueue))) { - struct alarm *alarm; - ktime_t expired = next->expires; - - if (expired.tv64 > now.tv64) - break; - - alarm = container_of(next, struct alarm, node); - - timerqueue_del(&base->timerqueue, &alarm->node); - alarm->state &= ~ALARMTIMER_STATE_ENQUEUED; - - alarm->state |= ALARMTIMER_STATE_CALLBACK; - spin_unlock_irqrestore(&base->lock, flags); - if (alarm->function) - restart = alarm->function(alarm, now); - spin_lock_irqsave(&base->lock, flags); - alarm->state &= ~ALARMTIMER_STATE_CALLBACK; + alarmtimer_remove(base, alarm); + spin_unlock_irqrestore(&base->lock, flags); - if (restart != ALARMTIMER_NORESTART) { - timerqueue_add(&base->timerqueue, &alarm->node); - alarm->state |= ALARMTIMER_STATE_ENQUEUED; - } - } + if (alarm->function) + restart = alarm->function(alarm, base->gettime()); - if (next) { - hrtimer_set_expires(&base->timer, next->expires); + spin_lock_irqsave(&base->lock, flags); + if (restart != ALARMTIMER_NORESTART) { + hrtimer_set_expires(&alarm->timer, alarm->node.expires); + alarmtimer_enqueue(base, alarm); ret = HRTIMER_RESTART; } spin_unlock_irqrestore(&base->lock, flags); @@ -331,6 +296,9 @@ void alarm_init(struct alarm *alarm, enum alarmtimer_type type, enum alarmtimer_restart (*function)(struct alarm *, ktime_t)) { timerqueue_init(&alarm->node); + hrtimer_init(&alarm->timer, alarm_bases[type].base_clockid, + HRTIMER_MODE_ABS); + alarm->timer.function = alarmtimer_fired; alarm->function = function; alarm->type = type; alarm->state = ALARMTIMER_STATE_INACTIVE; @@ -341,17 +309,19 @@ void alarm_init(struct alarm *alarm, enum alarmtimer_type type, * @alarm: ptr to alarm to set * @start: time to run the alarm */ -void alarm_start(struct alarm *alarm, ktime_t start) +int alarm_start(struct alarm *alarm, ktime_t start) { struct alarm_base *base = &alarm_bases[alarm->type]; unsigned long flags; + int ret; spin_lock_irqsave(&base->lock, flags); - if (alarmtimer_active(alarm)) - alarmtimer_remove(base, alarm); alarm->node.expires = start; alarmtimer_enqueue(base, alarm); + ret = hrtimer_start(&alarm->timer, alarm->node.expires, + HRTIMER_MODE_ABS); spin_unlock_irqrestore(&base->lock, flags); + return ret; } /** @@ -365,18 +335,12 @@ int alarm_try_to_cancel(struct alarm *alarm) { struct alarm_base *base = &alarm_bases[alarm->type]; unsigned long flags; - int ret = -1; - spin_lock_irqsave(&base->lock, flags); - - if (alarmtimer_callback_running(alarm)) - goto out; + int ret; - if (alarmtimer_is_queued(alarm)) { + spin_lock_irqsave(&base->lock, flags); + ret = hrtimer_try_to_cancel(&alarm->timer); + if (ret >= 0) alarmtimer_remove(base, alarm); - ret = 1; - } else - ret = 0; -out: spin_unlock_irqrestore(&base->lock, flags); return ret; } @@ -809,10 +773,6 @@ static int __init alarmtimer_init(void) for (i = 0; i < ALARM_NUMTYPE; i++) { timerqueue_init_head(&alarm_bases[i].timerqueue); spin_lock_init(&alarm_bases[i].lock); - hrtimer_init(&alarm_bases[i].timer, - alarm_bases[i].base_clockid, - HRTIMER_MODE_ABS); - alarm_bases[i].timer.function = alarmtimer_fired; } error = alarmtimer_rtc_interface_setup(); -- cgit v1.2.3 From a269eb50bd5793e43589270c2f326f4f7519ddda Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 13 Sep 2012 19:23:22 -0400 Subject: alarmtimer: Remove unused helpers & defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No one is using these alarmtimer state helpers, so yank them. Cc: Arve Hjønnevåg Cc: Colin Cross Cc: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/alarmtimer.h | 28 ---------------------------- 1 file changed, 28 deletions(-) (limited to 'include') diff --git a/include/linux/alarmtimer.h b/include/linux/alarmtimer.h index f122c9fbf8c7..9069694e70eb 100644 --- a/include/linux/alarmtimer.h +++ b/include/linux/alarmtimer.h @@ -21,7 +21,6 @@ enum alarmtimer_restart { #define ALARMTIMER_STATE_INACTIVE 0x00 #define ALARMTIMER_STATE_ENQUEUED 0x01 -#define ALARMTIMER_STATE_CALLBACK 0x02 /** * struct alarm - Alarm timer structure @@ -50,33 +49,6 @@ int alarm_cancel(struct alarm *alarm); u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval); -/* - * A alarmtimer is active, when it is enqueued into timerqueue or the - * callback function is running. - */ -static inline int alarmtimer_active(const struct alarm *timer) -{ - return timer->state != ALARMTIMER_STATE_INACTIVE; -} - -/* - * Helper function to check, whether the timer is on one of the queues - */ -static inline int alarmtimer_is_queued(struct alarm *timer) -{ - return timer->state & ALARMTIMER_STATE_ENQUEUED; -} - -/* - * Helper function to check, whether the timer is running the callback - * function - */ -static inline int alarmtimer_callback_running(struct alarm *timer) -{ - return timer->state & ALARMTIMER_STATE_CALLBACK; -} - - /* Provide way to access the rtc device being used by alarmtimers */ struct rtc_device *alarmtimer_get_rtcdev(void); -- cgit v1.2.3 From 7916a1f14f06ac93e4cec81139fe4f7ec13b572c Mon Sep 17 00:00:00 2001 From: John Stultz Date: Wed, 5 Sep 2012 00:44:53 -0400 Subject: jiffies: Kill unused TICK_USEC_TO_NSEC No one is using TICK_USEC_TO_NSEC, so kill it. Cc: Catalin Marinas Cc: Arnd Bergmann Cc: Richard Cochran Cc: Prarit Bhargava Cc: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/jiffies.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'include') diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h index 82680541576d..4a7e3864e80d 100644 --- a/include/linux/jiffies.h +++ b/include/linux/jiffies.h @@ -70,12 +70,6 @@ /* TICK_USEC is the time between ticks in usec assuming fake USER_HZ */ #define TICK_USEC ((1000000UL + USER_HZ/2) / USER_HZ) -/* - * TICK_USEC_TO_NSEC is the time between ticks in nsec assuming SHIFTED_HZ and - * a value TUSEC for TICK_USEC (can be set bij adjtimex) - */ -#define TICK_USEC_TO_NSEC(TUSEC) (SH_DIV(TUSEC * USER_HZ * 1000, SHIFTED_HZ, 8)) - /* some arch's have a small-data section that can be accessed register-relative * but that can only take up to, say, 4-byte variables. jiffies being part of * an 8-byte variable may not be correctly accessed unless we force the issue -- cgit v1.2.3 From b3c869d35b9b014f63ac0beacd31c57372084d01 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 4 Sep 2012 12:42:27 -0400 Subject: jiffies: Remove compile time assumptions about CLOCK_TICK_RATE CLOCK_TICK_RATE is used to accurately caclulate exactly how a tick will be at a given HZ. This is useful, because while we'd expect NSEC_PER_SEC/HZ, the underlying hardware will have some granularity limit, so we won't be able to have exactly HZ ticks per second. This slight error can cause timekeeping quality problems when using the jiffies or other jiffies driven clocksources. Thus we currently use compile time CLOCK_TICK_RATE value to generate SHIFTED_HZ and NSEC_PER_JIFFIES, which we then use to adjust the jiffies clocksource to correct this error. Unfortunately though, since CLOCK_TICK_RATE is a compile time value, and the jiffies clocksource is registered very early during boot, there are a number of cases where there are different possible hardware timers that have different tick rates. This causes problems in cases like ARM where there are numerous different types of hardware, each having their own compile-time CLOCK_TICK_RATE, making it hard to accurately support different hardware with a single kernel. For the most part, this doesn't matter all that much, as not too many systems actually utilize the jiffies or jiffies driven clocksource. Usually there are other highres clocksources who's granularity error is negligable. Even so, we have some complicated calcualtions that we do everywhere to handle these edge cases. This patch removes the compile time SHIFTED_HZ value, and introduces a register_refined_jiffies() function. This results in the default jiffies clock as being assumed a perfect HZ freq, and allows archtectures that care about jiffies accuracy to call register_refined_jiffies() with the tick rate, specified dynamically at boot. This allows us, where necessary, to not have a compile time CLOCK_TICK_RATE constant, simplifies the jiffies code, and still provides a way to have an accurate jiffies clock. NOTE: Since this patch does not add register_refinied_jiffies() calls for every arch, it may cause time quality regressions in some cases. Its likely these will not be noticable, but if they are an issue, adding the following to the end of setup_arch() should resolve the regression: register_refinied_jiffies(CLOCK_TICK_RATE) Cc: Catalin Marinas Cc: Arnd Bergmann Cc: Richard Cochran Cc: Prarit Bhargava Cc: Thomas Gleixner Signed-off-by: John Stultz --- arch/x86/kernel/setup.c | 3 +++ include/linux/jiffies.h | 15 ++------------- kernel/time/jiffies.c | 32 +++++++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index f4b9b80e1b95..4062f15bfbdd 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -68,6 +68,7 @@ #include #include #include +#include #include